- ToolboxVignette: prop bullets[] remplace description, touch targets agrandis - Design arrondi: border-radius 16px cards, 20px pills, 12px inputs, no borders - Hover animations: translateY(-3px) + shadow, active states pour touch - SectionLayout: toolbox accordion mobile, pills scroll horizontal, responsive title/subtitle - app.vue: MoodSwitcher dans drawer mobile, header responsive, nav touch-friendly - Dashboard: grille 2-colonnes mobile, connect banner column layout, formula code scroll - Documents/decisions/mandates/protocols: cards responsive (padding, font-size, gap) - Login: touch targets 3rem min, iOS zoom prevention, responsive sizing - Modals: padding responsive sm:p-6 - Protocols: table compact mobile, proto items responsive - nuxt.config: host 0.0.0.0 pour fix IPv4/IPv6 binding Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
907 lines
21 KiB
Vue
907 lines
21 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* Protocoles & Fonctionnement — Boite a outils de vote.
|
|
*
|
|
* Liste les protocoles de vote avec SectionLayout,
|
|
* sidebar n8n workflow + simulateur de formules.
|
|
*/
|
|
const protocols = useProtocolsStore()
|
|
const auth = useAuthStore()
|
|
|
|
const activeStatus = ref<string | null>(null)
|
|
const searchQuery = ref('')
|
|
const showCreateModal = ref(false)
|
|
const creating = ref(false)
|
|
|
|
const newProtocol = reactive({
|
|
name: '',
|
|
description: '',
|
|
vote_type: 'binary',
|
|
formula_config_id: '',
|
|
})
|
|
|
|
onMounted(async () => {
|
|
await Promise.all([
|
|
protocols.fetchProtocols(),
|
|
protocols.fetchFormulas(),
|
|
])
|
|
})
|
|
|
|
const voteTypeLabel = (voteType: string) => {
|
|
switch (voteType) {
|
|
case 'binary': return 'Binaire'
|
|
case 'nuanced': return 'Nuance'
|
|
default: return voteType
|
|
}
|
|
}
|
|
|
|
const voteTypeOptions = [
|
|
{ label: 'Binaire (Pour/Contre)', value: 'binary' },
|
|
{ label: 'Nuance (6 niveaux)', value: 'nuanced' },
|
|
]
|
|
|
|
const formulaOptions = computed(() => {
|
|
return protocols.formulas.map(f => ({
|
|
label: f.name,
|
|
value: f.id,
|
|
}))
|
|
})
|
|
|
|
/** Status pills. */
|
|
const statuses = computed(() => [
|
|
{
|
|
id: 'binary',
|
|
label: 'Binaire',
|
|
count: protocols.protocols.filter(p => p.vote_type === 'binary').length,
|
|
cssClass: 'status-vote',
|
|
},
|
|
{
|
|
id: 'nuanced',
|
|
label: 'Nuance',
|
|
count: protocols.protocols.filter(p => p.vote_type === 'nuanced').length,
|
|
cssClass: 'status-prepa',
|
|
},
|
|
])
|
|
|
|
/** Filtered protocols. */
|
|
const filteredProtocols = computed(() => {
|
|
let list = [...protocols.protocols]
|
|
|
|
if (activeStatus.value) {
|
|
list = list.filter(p => p.vote_type === activeStatus.value)
|
|
}
|
|
|
|
if (searchQuery.value.trim()) {
|
|
const q = searchQuery.value.toLowerCase()
|
|
list = list.filter(p =>
|
|
p.name.toLowerCase().includes(q)
|
|
|| (p.description && p.description.toLowerCase().includes(q)),
|
|
)
|
|
}
|
|
|
|
return list
|
|
})
|
|
|
|
function openCreateModal() {
|
|
newProtocol.name = ''
|
|
newProtocol.description = ''
|
|
newProtocol.vote_type = 'binary'
|
|
newProtocol.formula_config_id = ''
|
|
showCreateModal.value = true
|
|
}
|
|
|
|
async function createProtocol() {
|
|
if (!newProtocol.name.trim() || !newProtocol.formula_config_id) return
|
|
|
|
creating.value = true
|
|
try {
|
|
await protocols.createProtocol({
|
|
name: newProtocol.name.trim(),
|
|
description: newProtocol.description.trim() || null,
|
|
vote_type: newProtocol.vote_type,
|
|
formula_config_id: newProtocol.formula_config_id,
|
|
})
|
|
showCreateModal.value = false
|
|
await protocols.fetchProtocols()
|
|
}
|
|
finally {
|
|
creating.value = false
|
|
}
|
|
}
|
|
|
|
/** n8n workflow demo items. */
|
|
const n8nWorkflows = [
|
|
{
|
|
name: 'Vote -> Notification',
|
|
description: 'Notifie les membres lorsqu\'un nouveau vote demarre ou se termine.',
|
|
icon: 'i-lucide-bell',
|
|
status: 'actif',
|
|
},
|
|
{
|
|
name: 'Seuil atteint -> Sanctuaire',
|
|
description: 'Archive automatiquement le document valide sur IPFS quand le seuil est atteint.',
|
|
icon: 'i-lucide-archive',
|
|
status: 'actif',
|
|
},
|
|
{
|
|
name: 'Decision -> Etape suivante',
|
|
description: 'Avance automatiquement une decision a l\'etape suivante apres validation.',
|
|
icon: 'i-lucide-git-branch',
|
|
status: 'demo',
|
|
},
|
|
{
|
|
name: 'Mandat expire -> Alerte',
|
|
description: 'Envoie une alerte 7 jours avant l\'expiration d\'un mandat.',
|
|
icon: 'i-lucide-alarm-clock',
|
|
status: 'demo',
|
|
},
|
|
]
|
|
</script>
|
|
|
|
<template>
|
|
<SectionLayout
|
|
title="Protocoles & Fonctionnement"
|
|
subtitle="Boite a outils de vote, formules de seuil, workflows automatises"
|
|
:statuses="statuses"
|
|
:active-status="activeStatus"
|
|
@update:active-status="activeStatus = $event"
|
|
>
|
|
<!-- Search slot -->
|
|
<template #search>
|
|
<input
|
|
v-model="searchQuery"
|
|
type="text"
|
|
class="proto-search"
|
|
placeholder="Rechercher un protocole..."
|
|
/>
|
|
<button
|
|
v-if="auth.isAuthenticated"
|
|
class="proto-add-btn"
|
|
@click="openCreateModal"
|
|
>
|
|
<UIcon name="i-lucide-plus" class="text-xs" />
|
|
<span>Nouveau</span>
|
|
</button>
|
|
</template>
|
|
|
|
<!-- Loading -->
|
|
<template v-if="protocols.loading">
|
|
<div class="proto-loading">
|
|
<LoadingSkeleton v-for="i in 3" :key="i" :lines="4" card />
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Error -->
|
|
<template v-else-if="protocols.error">
|
|
<div class="proto-error">
|
|
<UIcon name="i-lucide-alert-circle" class="text-lg" />
|
|
<span>{{ protocols.error }}</span>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Protocol list -->
|
|
<template v-else>
|
|
<div v-if="filteredProtocols.length === 0" class="proto-empty">
|
|
<UIcon name="i-lucide-settings" class="text-2xl" />
|
|
<p>Aucun protocole trouve</p>
|
|
</div>
|
|
|
|
<div v-else class="proto-list">
|
|
<NuxtLink
|
|
v-for="protocol in filteredProtocols"
|
|
:key="protocol.id"
|
|
:to="`/protocols/${protocol.id}`"
|
|
class="proto-item"
|
|
>
|
|
<div class="proto-item__left">
|
|
<div class="proto-item__icon">
|
|
<UIcon
|
|
:name="protocol.vote_type === 'nuanced' ? 'i-lucide-bar-chart-3' : 'i-lucide-check-circle'"
|
|
class="text-lg"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div class="proto-item__body">
|
|
<div class="proto-item__head">
|
|
<h3 class="proto-item__name">{{ protocol.name }}</h3>
|
|
<span class="proto-item__type" :class="protocol.vote_type === 'nuanced' ? 'proto-item__type--nuanced' : ''">
|
|
{{ voteTypeLabel(protocol.vote_type) }}
|
|
</span>
|
|
</div>
|
|
<p v-if="protocol.description" class="proto-item__desc">
|
|
{{ protocol.description }}
|
|
</p>
|
|
<div class="proto-item__meta">
|
|
<span v-if="protocol.mode_params" class="proto-item__params">
|
|
{{ protocol.mode_params }}
|
|
</span>
|
|
<span class="proto-item__formula">
|
|
{{ protocol.formula_config.name }}
|
|
</span>
|
|
<span class="proto-item__detail">
|
|
{{ protocol.formula_config.duration_days }}j · {{ protocol.formula_config.majority_pct }}%
|
|
</span>
|
|
</div>
|
|
</div>
|
|
<div class="proto-item__arrow">
|
|
<UIcon name="i-lucide-chevron-right" class="text-sm" />
|
|
</div>
|
|
</NuxtLink>
|
|
</div>
|
|
|
|
<!-- Formulas table -->
|
|
<div class="proto-formulas">
|
|
<h3 class="proto-formulas__title">
|
|
<UIcon name="i-lucide-calculator" class="text-sm" />
|
|
Configurations de formule
|
|
<span class="proto-formulas__count">{{ protocols.formulas.length }}</span>
|
|
</h3>
|
|
<div class="proto-formulas__table-wrap">
|
|
<table class="proto-formulas__table">
|
|
<thead>
|
|
<tr>
|
|
<th>Nom</th>
|
|
<th>Duree</th>
|
|
<th>Majorite</th>
|
|
<th>B</th>
|
|
<th>G</th>
|
|
<th>Smith</th>
|
|
<th>TechComm</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="formula in protocols.formulas" :key="formula.id">
|
|
<td class="proto-formulas__name">{{ formula.name }}</td>
|
|
<td>{{ formula.duration_days }}j</td>
|
|
<td>{{ formula.majority_pct }}%</td>
|
|
<td>{{ formula.base_exponent }}</td>
|
|
<td>{{ formula.gradient_exponent }}</td>
|
|
<td>{{ formula.smith_exponent ?? '-' }}</td>
|
|
<td>{{ formula.techcomm_exponent ?? '-' }}</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- Toolbox sidebar -->
|
|
<template #toolbox>
|
|
<!-- Simulateur -->
|
|
<ToolboxVignette
|
|
title="Simulateur de formules"
|
|
:bullets="['Testez WoT, Smith, TechComm', 'Ajustez les parametres en temps reel', 'Visualisez les seuils']"
|
|
:actions="[
|
|
{ label: 'Tutos', icon: 'i-lucide-graduation-cap', emit: 'tutos' },
|
|
{ label: 'Ouvrir', icon: 'i-lucide-calculator', to: '/protocols/formulas', primary: true },
|
|
]"
|
|
/>
|
|
|
|
<!-- n8n Workflows -->
|
|
<div class="n8n-section">
|
|
<div class="n8n-section__head">
|
|
<UIcon name="i-lucide-workflow" class="text-xs" />
|
|
<span>Workflows n8n</span>
|
|
</div>
|
|
<p class="n8n-section__desc">
|
|
Automatisations reliees via MCP
|
|
</p>
|
|
|
|
<div class="n8n-workflows">
|
|
<div
|
|
v-for="wf in n8nWorkflows"
|
|
:key="wf.name"
|
|
class="n8n-wf"
|
|
>
|
|
<div class="n8n-wf__icon">
|
|
<UIcon :name="wf.icon" class="text-xs" />
|
|
</div>
|
|
<div class="n8n-wf__body">
|
|
<div class="n8n-wf__head">
|
|
<span class="n8n-wf__name">{{ wf.name }}</span>
|
|
<span
|
|
class="n8n-wf__status"
|
|
:class="wf.status === 'actif' ? 'n8n-wf__status--active' : 'n8n-wf__status--demo'"
|
|
>
|
|
{{ wf.status }}
|
|
</span>
|
|
</div>
|
|
<p class="n8n-wf__desc">{{ wf.description }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Meta-gouvernance -->
|
|
<ToolboxVignette
|
|
title="Meta-gouvernance"
|
|
:bullets="['Les formules sont soumises au vote', 'Modifier les seuils collectivement', 'Transparence totale']"
|
|
:actions="[
|
|
{ label: 'Tutos', icon: 'i-lucide-graduation-cap', emit: 'tutos' },
|
|
{ label: 'Formules', icon: 'i-lucide-calculator', emit: 'formules' },
|
|
{ label: 'Demarrer', icon: 'i-lucide-play', emit: 'meta', primary: true },
|
|
]"
|
|
/>
|
|
</template>
|
|
</SectionLayout>
|
|
|
|
<!-- Create protocol modal -->
|
|
<UModal v-model:open="showCreateModal">
|
|
<template #content>
|
|
<div class="proto-modal">
|
|
<h3 class="proto-modal__title">Nouveau protocole de vote</h3>
|
|
|
|
<div class="proto-modal__fields">
|
|
<div class="proto-modal__field">
|
|
<label class="proto-modal__label">Nom du protocole</label>
|
|
<input
|
|
v-model="newProtocol.name"
|
|
type="text"
|
|
class="proto-modal__input"
|
|
placeholder="Ex: Vote standard G1"
|
|
/>
|
|
</div>
|
|
|
|
<div class="proto-modal__field">
|
|
<label class="proto-modal__label">Description</label>
|
|
<textarea
|
|
v-model="newProtocol.description"
|
|
class="proto-modal__textarea"
|
|
placeholder="Description du protocole..."
|
|
rows="2"
|
|
/>
|
|
</div>
|
|
|
|
<div class="proto-modal__field">
|
|
<label class="proto-modal__label">Type de vote</label>
|
|
<USelect
|
|
v-model="newProtocol.vote_type"
|
|
:items="voteTypeOptions"
|
|
value-key="value"
|
|
/>
|
|
</div>
|
|
|
|
<div class="proto-modal__field">
|
|
<label class="proto-modal__label">Configuration de formule</label>
|
|
<USelect
|
|
v-model="newProtocol.formula_config_id"
|
|
:items="formulaOptions"
|
|
placeholder="Selectionnez une formule..."
|
|
value-key="value"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="proto-modal__actions">
|
|
<button class="proto-modal__cancel" @click="showCreateModal = false">
|
|
Annuler
|
|
</button>
|
|
<button
|
|
class="proto-modal__submit"
|
|
:disabled="!newProtocol.name.trim() || !newProtocol.formula_config_id || creating"
|
|
@click="createProtocol"
|
|
>
|
|
<UIcon v-if="creating" name="i-lucide-loader-2" class="animate-spin text-xs" />
|
|
<span>Creer</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</UModal>
|
|
</template>
|
|
|
|
<style scoped>
|
|
/* --- Search --- */
|
|
.proto-search {
|
|
flex: 1;
|
|
min-width: 10rem;
|
|
padding: 0.625rem 1rem;
|
|
font-size: 0.9375rem;
|
|
color: var(--mood-text);
|
|
background: var(--mood-accent-soft);
|
|
border-radius: 12px;
|
|
outline: none;
|
|
transition: box-shadow 0.15s ease;
|
|
}
|
|
.proto-search:focus {
|
|
box-shadow: 0 0 0 3px var(--mood-accent-soft);
|
|
}
|
|
.proto-search::placeholder {
|
|
color: var(--mood-text-muted);
|
|
opacity: 0.4;
|
|
}
|
|
|
|
.proto-add-btn {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.375rem;
|
|
padding: 0.625rem 1.25rem;
|
|
font-size: 0.875rem;
|
|
font-weight: 700;
|
|
color: var(--mood-accent-text);
|
|
background: var(--mood-accent);
|
|
border-radius: 20px;
|
|
cursor: pointer;
|
|
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
|
}
|
|
.proto-add-btn:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px var(--mood-shadow);
|
|
}
|
|
|
|
/* --- States --- */
|
|
.proto-loading { display: flex; flex-direction: column; gap: 0.75rem; }
|
|
.proto-error {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
padding: 1rem 1.25rem;
|
|
font-size: 0.9375rem;
|
|
font-weight: 600;
|
|
color: var(--mood-error);
|
|
background: rgba(196, 43, 43, 0.08);
|
|
border-radius: 16px;
|
|
}
|
|
.proto-empty {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 0.75rem;
|
|
padding: 3rem;
|
|
text-align: center;
|
|
color: var(--mood-text-muted);
|
|
font-size: 1rem;
|
|
}
|
|
|
|
/* --- Protocol list --- */
|
|
.proto-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.proto-item {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 0.75rem;
|
|
padding: 1rem;
|
|
background: var(--mood-surface);
|
|
border-radius: 16px;
|
|
text-decoration: none;
|
|
cursor: pointer;
|
|
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-item {
|
|
gap: 1rem;
|
|
padding: 1.25rem;
|
|
}
|
|
}
|
|
|
|
.proto-item:hover {
|
|
transform: translateY(-3px);
|
|
box-shadow: 0 8px 24px var(--mood-shadow);
|
|
}
|
|
|
|
.proto-item:active {
|
|
transform: translateY(0);
|
|
}
|
|
|
|
.proto-item__left { flex-shrink: 0; }
|
|
|
|
.proto-item__icon {
|
|
width: 2.25rem;
|
|
height: 2.25rem;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 12px;
|
|
background: var(--mood-accent-soft);
|
|
color: var(--mood-accent);
|
|
font-size: 1rem;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-item__icon {
|
|
width: 2.75rem;
|
|
height: 2.75rem;
|
|
font-size: 1.25rem;
|
|
}
|
|
}
|
|
|
|
.proto-item__body {
|
|
flex: 1;
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-item__body {
|
|
gap: 0.375rem;
|
|
}
|
|
}
|
|
|
|
.proto-item__head {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 0.375rem;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-item__head {
|
|
gap: 0.625rem;
|
|
}
|
|
}
|
|
|
|
.proto-item__name {
|
|
font-size: 0.9375rem;
|
|
font-weight: 700;
|
|
color: var(--mood-text);
|
|
margin: 0;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-item__name {
|
|
font-size: 1.0625rem;
|
|
}
|
|
}
|
|
|
|
.proto-item__type {
|
|
font-size: 0.6875rem;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
padding: 3px 10px;
|
|
border-radius: 20px;
|
|
background: var(--mood-status-vote-bg);
|
|
color: var(--mood-status-vote);
|
|
}
|
|
.proto-item__type--nuanced {
|
|
background: var(--mood-status-prepa-bg);
|
|
color: var(--mood-status-prepa);
|
|
}
|
|
|
|
.proto-item__desc {
|
|
font-size: 0.875rem;
|
|
color: var(--mood-text-muted);
|
|
line-height: 1.5;
|
|
margin: 0;
|
|
}
|
|
|
|
.proto-item__meta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.375rem;
|
|
margin-top: 0.125rem;
|
|
font-size: 0.75rem;
|
|
color: var(--mood-text-muted);
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-item__meta {
|
|
gap: 0.5rem;
|
|
margin-top: 0.25rem;
|
|
font-size: 0.8125rem;
|
|
}
|
|
}
|
|
|
|
.proto-item__params {
|
|
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
font-weight: 700;
|
|
color: var(--mood-accent);
|
|
background: var(--mood-accent-soft);
|
|
padding: 2px 8px;
|
|
border-radius: 20px;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
.proto-item__formula {
|
|
font-weight: 600;
|
|
}
|
|
|
|
.proto-item__detail {
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.proto-item__arrow {
|
|
flex-shrink: 0;
|
|
color: var(--mood-text-muted);
|
|
opacity: 0.3;
|
|
margin-top: 0.375rem;
|
|
transition: opacity 0.12s;
|
|
}
|
|
.proto-item:hover .proto-item__arrow {
|
|
opacity: 1;
|
|
color: var(--mood-accent);
|
|
}
|
|
|
|
/* --- Formulas --- */
|
|
.proto-formulas {
|
|
margin-top: 0.75rem;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-formulas {
|
|
margin-top: 1rem;
|
|
}
|
|
}
|
|
|
|
.proto-formulas__title {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
font-size: 0.9375rem;
|
|
font-weight: 700;
|
|
color: var(--mood-text);
|
|
margin: 0 0 0.5rem;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-formulas__title {
|
|
font-size: 1rem;
|
|
margin: 0 0 0.75rem;
|
|
}
|
|
}
|
|
|
|
.proto-formulas__count {
|
|
font-size: 0.6875rem;
|
|
font-weight: 700;
|
|
background: var(--mood-accent-soft);
|
|
color: var(--mood-accent);
|
|
padding: 2px 8px;
|
|
border-radius: 20px;
|
|
}
|
|
|
|
.proto-formulas__table-wrap {
|
|
background: var(--mood-surface);
|
|
border-radius: 16px;
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.proto-formulas__table {
|
|
width: 100%;
|
|
font-size: 0.875rem;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
.proto-formulas__table th {
|
|
text-align: left;
|
|
padding: 0.5rem 0.625rem;
|
|
font-weight: 700;
|
|
font-size: 0.6875rem;
|
|
color: var(--mood-text-muted);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-formulas__table th {
|
|
padding: 0.75rem 1rem;
|
|
font-size: 0.75rem;
|
|
}
|
|
}
|
|
|
|
.proto-formulas__table td {
|
|
padding: 0.5rem 0.625rem;
|
|
color: var(--mood-text-muted);
|
|
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
font-size: 0.75rem;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-formulas__table td {
|
|
padding: 0.75rem 1rem;
|
|
font-size: 0.8125rem;
|
|
}
|
|
}
|
|
|
|
.proto-formulas__name {
|
|
font-weight: 700;
|
|
color: var(--mood-text) !important;
|
|
font-family: inherit !important;
|
|
}
|
|
|
|
/* --- n8n Section --- */
|
|
.n8n-section {
|
|
background: var(--mood-accent-soft);
|
|
border-radius: 12px;
|
|
padding: 0.75rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.n8n-section__head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.375rem;
|
|
font-size: 0.8125rem;
|
|
font-weight: 700;
|
|
color: var(--mood-tertiary, var(--mood-accent));
|
|
}
|
|
|
|
.n8n-section__desc {
|
|
font-size: 0.75rem;
|
|
color: var(--mood-text-muted);
|
|
margin: 0;
|
|
}
|
|
|
|
.n8n-workflows {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.375rem;
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
.n8n-wf {
|
|
display: flex;
|
|
gap: 0.5rem;
|
|
padding: 0.5rem;
|
|
border-radius: 10px;
|
|
transition: background 0.1s;
|
|
}
|
|
.n8n-wf:hover {
|
|
background: var(--mood-surface);
|
|
}
|
|
|
|
.n8n-wf__icon {
|
|
width: 1.75rem;
|
|
height: 1.75rem;
|
|
flex-shrink: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 8px;
|
|
background: var(--mood-surface);
|
|
color: var(--mood-tertiary, var(--mood-accent));
|
|
}
|
|
|
|
.n8n-wf__body {
|
|
min-width: 0;
|
|
flex: 1;
|
|
}
|
|
|
|
.n8n-wf__head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 0.375rem;
|
|
}
|
|
|
|
.n8n-wf__name {
|
|
font-size: 0.75rem;
|
|
font-weight: 700;
|
|
color: var(--mood-text);
|
|
}
|
|
|
|
.n8n-wf__status {
|
|
font-size: 0.5625rem;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
padding: 2px 6px;
|
|
border-radius: 20px;
|
|
}
|
|
.n8n-wf__status--active {
|
|
background: var(--mood-status-vigueur-bg);
|
|
color: var(--mood-success);
|
|
}
|
|
.n8n-wf__status--demo {
|
|
background: var(--mood-surface);
|
|
color: var(--mood-text-muted);
|
|
}
|
|
|
|
.n8n-wf__desc {
|
|
font-size: 0.6875rem;
|
|
color: var(--mood-text-muted);
|
|
line-height: 1.4;
|
|
margin: 0;
|
|
}
|
|
|
|
/* --- Modal --- */
|
|
.proto-modal {
|
|
padding: 1.25rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1.25rem;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-modal {
|
|
padding: 2rem;
|
|
gap: 1.5rem;
|
|
}
|
|
}
|
|
|
|
.proto-modal__title {
|
|
font-size: 1.125rem;
|
|
font-weight: 800;
|
|
color: var(--mood-text);
|
|
margin: 0;
|
|
}
|
|
|
|
@media (min-width: 640px) {
|
|
.proto-modal__title {
|
|
font-size: 1.375rem;
|
|
}
|
|
}
|
|
|
|
.proto-modal__fields {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.proto-modal__field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.375rem;
|
|
}
|
|
|
|
.proto-modal__label {
|
|
font-size: 0.8125rem;
|
|
font-weight: 700;
|
|
color: var(--mood-text-muted);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
}
|
|
|
|
.proto-modal__input,
|
|
.proto-modal__textarea {
|
|
width: 100%;
|
|
padding: 0.75rem 1rem;
|
|
font-size: 0.9375rem;
|
|
color: var(--mood-text);
|
|
background: var(--mood-accent-soft);
|
|
border-radius: 12px;
|
|
outline: none;
|
|
transition: box-shadow 0.15s ease;
|
|
font-family: inherit;
|
|
resize: vertical;
|
|
}
|
|
.proto-modal__input:focus,
|
|
.proto-modal__textarea:focus {
|
|
box-shadow: 0 0 0 3px var(--mood-accent-soft);
|
|
}
|
|
|
|
.proto-modal__actions {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.proto-modal__cancel {
|
|
padding: 0.625rem 1.25rem;
|
|
font-size: 0.875rem;
|
|
font-weight: 600;
|
|
color: var(--mood-text-muted);
|
|
background: none;
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
}
|
|
.proto-modal__cancel:hover { color: var(--mood-text); background: var(--mood-accent-soft); }
|
|
|
|
.proto-modal__submit {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.375rem;
|
|
padding: 0.625rem 1.5rem;
|
|
font-size: 0.9375rem;
|
|
font-weight: 700;
|
|
color: var(--mood-accent-text);
|
|
background: var(--mood-accent);
|
|
border-radius: 16px;
|
|
cursor: pointer;
|
|
transition: transform 0.12s ease, box-shadow 0.12s ease;
|
|
}
|
|
.proto-modal__submit:hover:not(:disabled) {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 12px var(--mood-shadow);
|
|
}
|
|
.proto-modal__submit:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
</style>
|