@@ -531,6 +545,51 @@ function toggleSection(tag: string) {
gap: 0.75rem;
}
+/* Protocol link */
+.doc-page__protocol-link {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.75rem 1rem;
+ background: color-mix(in srgb, var(--mood-tertiary, var(--mood-accent)) 8%, var(--mood-surface));
+ border: 1px solid color-mix(in srgb, var(--mood-tertiary, var(--mood-accent)) 15%, transparent);
+ border-radius: 14px;
+ text-decoration: none;
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
+ color: var(--mood-tertiary, var(--mood-accent));
+}
+
+.doc-page__protocol-link:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px var(--mood-shadow);
+}
+
+.doc-page__protocol-link-label {
+ display: block;
+ font-size: 0.625rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--mood-text-muted);
+}
+
+.doc-page__protocol-link-name {
+ display: block;
+ font-size: 0.875rem;
+ font-weight: 700;
+ color: var(--mood-text);
+}
+
+.doc-page__protocol-link-arrow {
+ margin-left: auto;
+ opacity: 0.3;
+ transition: opacity 0.12s;
+}
+
+.doc-page__protocol-link:hover .doc-page__protocol-link-arrow {
+ opacity: 1;
+}
+
/* Section collapse transition */
.section-collapse-enter-active,
.section-collapse-leave-active {
diff --git a/frontend/app/pages/documents/index.vue b/frontend/app/pages/documents/index.vue
index 428f5bb..21381af 100644
--- a/frontend/app/pages/documents/index.vue
+++ b/frontend/app/pages/documents/index.vue
@@ -3,7 +3,7 @@
* Documents de reference — page index.
*
* Utilise SectionLayout avec status filters, recherche, tri,
- * et sidebar "Boite a outils" affichant les protocoles de vote.
+ * et sidebar "Boîte à outils" affichant les protocoles de vote.
*/
import type { DocumentCreate } from '~/stores/documents'
@@ -29,7 +29,7 @@ const creating = ref(false)
const newDocTypeOptions = [
{ label: 'Licence', value: 'licence' },
{ label: 'Engagement', value: 'engagement' },
- { label: 'Reglement', value: 'reglement' },
+ { label: 'Règlement', value: 'reglement' },
{ label: 'Constitution', value: 'constitution' },
]
@@ -48,7 +48,7 @@ onMounted(async () => {
/** Status filter pills with counts. */
const statuses = computed(() => [
- { id: 'draft', label: 'En prepa', count: documents.list.filter(d => d.status === 'draft').length },
+ { id: 'draft', label: 'En prépa', count: documents.list.filter(d => d.status === 'draft').length },
{ id: 'voting', label: 'En vote', count: documents.list.filter(d => d.status === 'voting').length },
{ id: 'active', label: 'En vigueur', count: documents.list.filter(d => d.status === 'active').length },
{ id: 'archived', label: 'Clos', count: documents.list.filter(d => d.status === 'archived').length },
@@ -92,7 +92,7 @@ const typeLabel = (docType: string): string => {
switch (docType) {
case 'licence': return 'Licence'
case 'engagement': return 'Engagement'
- case 'reglement': return 'Reglement'
+ case 'reglement': return 'Règlement'
case 'constitution': return 'Constitution'
default: return docType
}
@@ -154,8 +154,8 @@ async function createDocument() {
- Aucun document trouve
+ Aucun document trouvé
Essayez de modifier vos filtres
@@ -253,7 +253,7 @@ async function createDocument() {
- Nouveau document de reference
+ Nouveau document de référence
@@ -335,7 +335,7 @@ async function createDocument() {
@@ -349,7 +349,7 @@ async function createDocument() {
@click="showNewDocModal = false"
/>
{
const entryCards = computed(() => [
{
key: 'decisions',
- title: 'Decisions',
+ title: 'Décisions structurantes',
icon: 'i-lucide-scale',
to: '/decisions',
count: decisions.activeDecisions.length,
countLabel: `${decisions.activeDecisions.length} en cours`,
totalLabel: `${decisions.list.length} au total`,
- description: 'Processus de decision collectifs',
+ description: 'Processus de décision collectifs',
color: 'var(--mood-secondary, var(--mood-accent))',
},
{
key: 'documents',
- title: 'Documents',
+ title: 'Documents de référence',
icon: 'i-lucide-book-open',
to: '/documents',
count: documents.activeDocuments.length,
@@ -45,24 +45,24 @@ const entryCards = computed(() => [
},
{
key: 'mandats',
- title: 'Mandats',
+ title: 'Mandats et nominations',
icon: 'i-lucide-user-check',
to: '/mandates',
count: null,
countLabel: null,
totalLabel: null,
- description: 'Missions deleguees avec nomination en binome',
+ description: 'Missions déléguées avec nomination en binôme',
color: 'var(--mood-success)',
},
{
key: 'protocoles',
- title: 'Protocoles',
+ title: 'Protocoles et fonctionnement',
icon: 'i-lucide-settings',
to: '/protocols',
count: protocols.protocols.length,
- countLabel: `${protocols.protocols.length} modalite${protocols.protocols.length > 1 ? 's' : ''}`,
- totalLabel: 'Boite a outils de vote + workflows',
- description: 'Modalites de vote, formules, workflows',
+ countLabel: `${protocols.protocols.length} modalité${protocols.protocols.length > 1 ? 's' : ''}`,
+ totalLabel: 'Boîte à outils de vote + workflows',
+ description: 'Modalités de vote, formules, workflows',
color: 'var(--mood-tertiary, var(--mood-accent))',
},
])
@@ -81,7 +81,7 @@ function formatDate(dateStr: string): string {
if (diffHours < 1) {
const diffMinutes = Math.floor(diffMs / (1000 * 60))
- return diffMinutes <= 1 ? 'A l\'instant' : `Il y a ${diffMinutes} min`
+ return diffMinutes <= 1 ? 'À l\'instant' : `Il y a ${diffMinutes} min`
}
if (diffHours < 24) {
return `Il y a ${Math.floor(diffHours)}h`
@@ -101,7 +101,7 @@ function formatDate(dateStr: string): string {
ğ(Decision)
- Decisions collectives pour la communaute Duniter / G1
+ Décisions collectives pour la communauté Duniter / G1
@@ -141,7 +141,7 @@ function formatDate(dateStr: string): string {
-
Connectez-vous avec votre identite Duniter pour participer.
+
Connectez-vous avec votre identité Duniter pour participer.
Signature Ed25519 · aucun mot de passe
@@ -158,7 +158,7 @@ function formatDate(dateStr: string): string {
-
+
Simulateur de formules, modules de vote, workflows
@@ -177,7 +177,7 @@ function formatDate(dateStr: string): string {
-
Activite recente
+ Activité récente
- Le seuil s'adapte a la participation : faible = quasi-unanimite ; forte = majorite simple.
+ Le seuil s'adapte à la participation : faible = quasi-unanimité ; forte = majorité simple.
Seuil = C + B^W + (M + (1-M) * (1 - (T/W)^G)) * max(0, T-C)
@@ -217,7 +217,7 @@ function formatDate(dateStr: string): string {
B = base
W = taille WoT
T = votes
- M = majorite
+ M = majorité
G = gradient
diff --git a/frontend/app/pages/mandates/index.vue b/frontend/app/pages/mandates/index.vue
index 2d6e7f0..75bdd5f 100644
--- a/frontend/app/pages/mandates/index.vue
+++ b/frontend/app/pages/mandates/index.vue
@@ -3,8 +3,8 @@
* Mandats — page index.
*
* Utilise SectionLayout avec status filters, recherche,
- * et sidebar "Boite a outils" affichant les protocoles de vote.
- * Etat vide enrichi avec onboarding expliquant le concept de mandat.
+ * et sidebar "Boîte à outils" affichant les protocoles de vote.
+ * État vide enrichi avec onboarding expliquant le concept de mandat.
*/
import type { MandateCreate } from '~/stores/mandates'
@@ -25,9 +25,9 @@ const sortOptions = [
// Create mandate modal state
const showCreateModal = ref(false)
const mandateTypeOptions = [
- { label: 'Comite technique', value: 'techcomm' },
+ { label: 'Comité technique', value: 'techcomm' },
{ label: 'Forgeron', value: 'smith' },
- { label: 'Personnalise', value: 'custom' },
+ { label: 'Personnalisé', value: 'custom' },
]
const newMandate = ref({
@@ -46,7 +46,7 @@ onMounted(async () => {
/** Status filter pills with counts. */
const statuses = computed(() => [
- { id: 'draft', label: 'En prepa', count: mandates.list.filter(m => m.status === 'draft' || m.status === 'candidacy').length },
+ { id: 'draft', label: 'En prépa', count: mandates.list.filter(m => m.status === 'draft' || m.status === 'candidacy').length },
{ id: 'voting', label: 'En vote', count: mandates.list.filter(m => m.status === 'voting').length },
{ id: 'active', label: 'En vigueur', count: mandates.list.filter(m => m.status === 'active' || m.status === 'reporting').length },
{ id: 'closed', label: 'Clos', count: mandates.list.filter(m => m.status === 'completed' || m.status === 'revoked').length },
@@ -95,9 +95,9 @@ const filteredMandates = computed(() => {
const typeLabel = (mandateType: string) => {
switch (mandateType) {
- case 'techcomm': return 'Comite technique'
+ case 'techcomm': return 'Comité technique'
case 'smith': return 'Forgeron'
- case 'custom': return 'Personnalise'
+ case 'custom': return 'Personnalisé'
default: return mandateType
}
}
@@ -133,7 +133,7 @@ async function handleCreate() {
- Un mandat definit un contexte, un objectif et une duree pour une mission de gouvernance.
- Il peut porter sur le comite technique, les forgerons, ou tout role specifique de la communaute.
+ Un mandat définit un contexte, un objectif et une durée pour une mission de gouvernance.
+ Il peut porter sur le comité technique, les forgerons, ou tout rôle spécifique de la communauté.
- Par defaut, un mandat nomme un binome pour assurer la continuite.
+ Par défaut, un mandat nomme un binôme pour assurer la continuité.
Le processus comprend : candidature, vote communautaire, periode active et rapport final.
- Aucun mandat trouve
+ Aucun mandat trouvé
Essayez de modifier vos filtres
@@ -254,7 +254,7 @@ async function handleCreate() {
- {{ mandate.steps.length }} etape{{ mandate.steps.length !== 1 ? 's' : '' }}
+ {{ mandate.steps.length }} étape{{ mandate.steps.length !== 1 ? 's' : '' }}
@@ -263,7 +263,7 @@ async function handleCreate() {
- Debut : {{ formatDate(mandate.starts_at) }}
+ Début : {{ formatDate(mandate.starts_at) }}
Fin : {{ formatDate(mandate.ends_at) }}
@@ -274,28 +274,28 @@ async function handleCreate() {
/**
- * Protocoles & Fonctionnement — Boite a outils de vote.
+ * Protocoles & Fonctionnement — Boîte à outils de vote.
*
* Liste les protocoles de vote avec SectionLayout,
* sidebar n8n workflow + simulateur de formules.
@@ -30,14 +30,14 @@ onMounted(async () => {
const voteTypeLabel = (voteType: string) => {
switch (voteType) {
case 'binary': return 'Binaire'
- case 'nuanced': return 'Nuance'
+ case 'nuanced': return 'Nuancé'
default: return voteType
}
}
const voteTypeOptions = [
{ label: 'Binaire (Pour/Contre)', value: 'binary' },
- { label: 'Nuance (6 niveaux)', value: 'nuanced' },
+ { label: 'Nuancé (6 niveaux)', value: 'nuanced' },
]
const formulaOptions = computed(() => {
@@ -57,7 +57,7 @@ const statuses = computed(() => [
},
{
id: 'nuanced',
- label: 'Nuance',
+ label: 'Nuancé',
count: protocols.protocols.filter(p => p.vote_type === 'nuanced').length,
cssClass: 'status-prepa',
},
@@ -117,24 +117,60 @@ interface WorkflowStep {
type: string
}
-const operationalProtocols = [
+interface LinkedRef {
+ label: string
+ icon: string
+ to: string
+ kind: 'document' | 'decision'
+}
+
+interface OperationalProtocol {
+ slug: string
+ name: string
+ description: string
+ category: string
+ icon: string
+ instancesLabel: string
+ linkedRefs: LinkedRef[]
+ steps: WorkflowStep[]
+}
+
+const operationalProtocols: OperationalProtocol[] = [
{
slug: 'embarquement-forgeron',
name: 'Embarquement Forgeron',
- description: 'Processus complet d\'intégration d\'un nouveau forgeron dans le réseau Duniter.',
+ description: 'Processus complet d\'intégration d\'un nouveau forgeron dans le réseau Duniter. Parcours en 5 jalons de la candidature à la mise en ligne du nœud validateur.',
category: 'onboarding',
icon: 'i-lucide-hammer',
instancesLabel: '~10-50 / an',
+ linkedRefs: [
+ { label: 'Acte d\'engagement forgeron', icon: 'i-lucide-book-open', to: '/documents/engagement-forgeron', kind: 'document' },
+ ],
steps: [
- { label: 'Invitation on-chain', actor: 'Smith existant', icon: 'i-lucide-send', type: 'on_chain' },
- { label: 'Acceptation', actor: 'Candidat', icon: 'i-lucide-check', type: 'on_chain' },
- { label: 'Session keys', actor: 'Candidat', icon: 'i-lucide-key', type: 'on_chain' },
- { label: 'Checklist aspirant', actor: 'Candidat', icon: 'i-lucide-clipboard-check', type: 'checklist' },
- { label: 'Certification 1', actor: 'Certificateur', icon: 'i-lucide-stamp', type: 'certification' },
- { label: 'Certification 2', actor: 'Certificateur', icon: 'i-lucide-stamp', type: 'certification' },
- { label: 'Certification 3', actor: 'Certificateur', icon: 'i-lucide-stamp', type: 'certification' },
+ { label: 'Candidature', actor: 'Aspirant forgeron', icon: 'i-lucide-user-plus', type: 'checklist' },
+ { label: 'Nœud miroir', actor: 'Candidat', icon: 'i-lucide-server', type: 'on_chain' },
+ { label: 'Évaluation technique', actor: 'Certificateur', icon: 'i-lucide-clipboard-check', type: 'checklist' },
+ { label: 'Certification Smith (×3)', actor: 'Certificateurs', icon: 'i-lucide-stamp', type: 'certification' },
{ label: 'Go online', actor: 'Candidat', icon: 'i-lucide-wifi', type: 'on_chain' },
- ] as WorkflowStep[],
+ ],
+ },
+ {
+ slug: 'soumission-runtime-upgrade',
+ name: 'Soumission Runtime Upgrade',
+ description: 'Protocole de soumission d\'une mise à jour du runtime Duniter V2 on-chain. Chaque upgrade suit un parcours strict en 5 étapes, de la qualification technique au suivi post-déploiement.',
+ category: 'on-chain',
+ icon: 'i-lucide-cpu',
+ instancesLabel: '~2-6 / an',
+ linkedRefs: [
+ { label: 'Décision Runtime Upgrade', icon: 'i-lucide-scale', to: '/decisions', kind: 'decision' },
+ ],
+ steps: [
+ { label: 'Qualification', actor: 'Proposant', icon: 'i-lucide-file-check', type: 'checklist' },
+ { label: 'Revue technique', actor: 'Comité technique', icon: 'i-lucide-search', type: 'checklist' },
+ { label: 'Vote communautaire', actor: 'Communauté WoT', icon: 'i-lucide-vote', type: 'on_chain' },
+ { label: 'Exécution on-chain', actor: 'Proposant', icon: 'i-lucide-zap', type: 'on_chain' },
+ { label: 'Suivi post-upgrade', actor: 'Forgerons', icon: 'i-lucide-activity', type: 'checklist' },
+ ],
},
]
@@ -142,7 +178,7 @@ const operationalProtocols = [
const n8nWorkflows = [
{
name: 'Vote -> Notification',
- description: 'Notifie les membres lorsqu\'un nouveau vote demarre ou se termine.',
+ description: 'Notifie les membres lorsqu\'un nouveau vote démarre ou se termine.',
icon: 'i-lucide-bell',
status: 'actif',
},
@@ -153,13 +189,13 @@ const n8nWorkflows = [
status: 'actif',
},
{
- name: 'Decision -> Etape suivante',
- description: 'Avance automatiquement une decision a l\'etape suivante apres validation.',
+ name: 'Décision → Étape suivante',
+ description: 'Avance automatiquement une décision à l\'étape suivante après validation.',
icon: 'i-lucide-git-branch',
status: 'demo',
},
{
- name: 'Mandat expire -> Alerte',
+ name: 'Mandat expiré → Alerte',
description: 'Envoie une alerte 7 jours avant l\'expiration d\'un mandat.',
icon: 'i-lucide-alarm-clock',
status: 'demo',
@@ -170,7 +206,7 @@ const n8nWorkflows = [
-
Aucun protocole trouve
+
Aucun protocole trouvé
@@ -262,7 +298,7 @@ const n8nWorkflows = [
- Protocoles operationnels
+ Protocoles opérationnels
{{ operationalProtocols.length }}
@@ -282,6 +318,21 @@ const n8nWorkflows = [
+
+
+
+
+ {{ ref.label }}
+
+
+
+
| Nom |
- Duree |
- Majorite |
+ Durée |
+ Majorité |
B |
G |
Smith |
@@ -343,7 +394,7 @@ const n8nWorkflows = [
- Automatisations reliees via MCP
+ Automatisations reliées via MCP
@@ -387,12 +438,12 @@ const n8nWorkflows = [
@@ -439,7 +490,7 @@ const n8nWorkflows = [
@@ -455,7 +506,7 @@ const n8nWorkflows = [
@click="createProtocol"
>
- Creer
+ Créer
@@ -952,6 +1003,49 @@ const n8nWorkflows = [
opacity: 0.7;
}
+/* Linked references */
+.proto-ops__refs {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+}
+
+.proto-ops__ref {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.375rem;
+ padding: 0.375rem 0.75rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ border-radius: 20px;
+ text-decoration: none;
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
+}
+
+.proto-ops__ref:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 2px 8px var(--mood-shadow);
+}
+
+.proto-ops__ref--document {
+ background: color-mix(in srgb, var(--mood-accent) 12%, transparent);
+ color: var(--mood-accent);
+}
+
+.proto-ops__ref--decision {
+ background: color-mix(in srgb, var(--mood-secondary, var(--mood-accent)) 12%, transparent);
+ color: var(--mood-secondary, var(--mood-accent));
+}
+
+.proto-ops__ref-arrow {
+ opacity: 0.4;
+ transition: opacity 0.12s;
+}
+
+.proto-ops__ref:hover .proto-ops__ref-arrow {
+ opacity: 1;
+}
+
/* Timeline */
.proto-ops__timeline {
display: flex;
diff --git a/frontend/app/pages/tools.vue b/frontend/app/pages/tools.vue
index f788a18..e1a7ce5 100644
--- a/frontend/app/pages/tools.vue
+++ b/frontend/app/pages/tools.vue
@@ -28,23 +28,23 @@ const sections: ToolSection[] = [
color: 'var(--mood-accent)',
tools: [
{ label: 'Modules', icon: 'i-lucide-puzzle', description: 'Structurer un document en sections et clauses modulaires', to: '/documents', status: 'ready' },
- { label: 'Votes permanents', icon: 'i-lucide-infinity', description: 'Chaque clause est sous vote permanent, modifiable a tout moment', status: 'ready' },
- { label: 'Inertie de remplacement', icon: 'i-lucide-sliders-horizontal', description: 'Regler la difficulte de modification par section (standard, haute, tres haute)', to: '/protocols/formulas', status: 'ready' },
- { label: 'Contre-propositions', icon: 'i-lucide-pen-line', description: 'Soumettre un texte alternatif soumis au vote de la communaute', status: 'ready' },
- { label: 'Ancrage IPFS', icon: 'i-lucide-hard-drive', description: 'Archiver les documents valides sur IPFS avec preuve on-chain', status: 'soon' },
+ { label: 'Votes permanents', icon: 'i-lucide-infinity', description: 'Chaque clause est sous vote permanent, modifiable à tout moment', status: 'ready' },
+ { label: 'Inertie de remplacement', icon: 'i-lucide-sliders-horizontal', description: 'Régler la difficulté de modification par section (standard, haute, très haute)', to: '/protocols/formulas', status: 'ready' },
+ { label: 'Contre-propositions', icon: 'i-lucide-pen-line', description: 'Soumettre un texte alternatif soumis au vote de la communauté', status: 'ready' },
+ { label: 'Ancrage IPFS', icon: 'i-lucide-hard-drive', description: 'Archiver les documents validés sur IPFS avec preuve on-chain', status: 'soon' },
],
},
{
key: 'decisions',
- title: 'Decisions',
+ title: 'Décisions',
icon: 'i-lucide-scale',
color: 'var(--mood-secondary, var(--mood-accent))',
tools: [
{ label: 'Vote majoritaire WoT', icon: 'i-lucide-check-circle', description: 'Seuil adaptatif par la toile de confiance, formule g1vote', to: '/protocols/formulas', status: 'ready' },
- { label: 'Vote quadratique', icon: 'i-lucide-square-stack', description: 'Ponderation degresssive pour eviter la concentration de pouvoir', status: 'soon' },
- { label: 'Vote nuance 6 niveaux', icon: 'i-lucide-bar-chart-3', description: 'De Tout a fait contre a Tout a fait pour, avec seuil de satisfaction', status: 'ready' },
- { label: 'Mandature', icon: 'i-lucide-user-check', description: 'Election et nomination en binome avec transparence', status: 'ready' },
- { label: 'Multi-criteres', icon: 'i-lucide-layers', description: 'Combinaison WoT + Smith + TechComm, tous doivent passer', to: '/protocols/formulas', status: 'ready' },
+ { label: 'Vote quadratique', icon: 'i-lucide-square-stack', description: 'Pondération dégressive pour éviter la concentration de pouvoir', status: 'soon' },
+ { label: 'Vote nuancé 6 niveaux', icon: 'i-lucide-bar-chart-3', description: 'De Tout à fait contre à Tout à fait pour, avec seuil de satisfaction', status: 'ready' },
+ { label: 'Mandature', icon: 'i-lucide-user-check', description: 'Élection et nomination en binôme avec transparence', status: 'ready' },
+ { label: 'Multi-critères', icon: 'i-lucide-layers', description: 'Combinaison WoT + Smith + TechComm, tous doivent passer', to: '/protocols/formulas', status: 'ready' },
],
},
{
@@ -53,10 +53,10 @@ const sections: ToolSection[] = [
icon: 'i-lucide-user-check',
color: 'var(--mood-success)',
tools: [
- { label: 'Ouverture', icon: 'i-lucide-door-open', description: 'Definir une mission, son perimetre, sa duree et ses objectifs', status: 'ready' },
- { label: 'Nomination', icon: 'i-lucide-users', description: 'Election en binome : un titulaire + un suppleant', status: 'ready' },
- { label: 'Transparence', icon: 'i-lucide-eye', description: 'Rapports d\'activite periodiques soumis au vote', status: 'ready' },
- { label: 'Cloture', icon: 'i-lucide-lock', description: 'Fin de mandat avec bilan ou revocation anticipee par vote', status: 'ready' },
+ { label: 'Ouverture', icon: 'i-lucide-door-open', description: 'Définir une mission, son périmètre, sa durée et ses objectifs', status: 'ready' },
+ { label: 'Nomination', icon: 'i-lucide-users', description: 'Élection en binôme : un titulaire + un suppléant', status: 'ready' },
+ { label: 'Transparence', icon: 'i-lucide-eye', description: 'Rapports d\'activité périodiques soumis au vote', status: 'ready' },
+ { label: 'Clôture', icon: 'i-lucide-lock', description: 'Fin de mandat avec bilan ou révocation anticipée par vote', status: 'ready' },
],
},
{
@@ -65,10 +65,10 @@ const sections: ToolSection[] = [
icon: 'i-lucide-settings',
color: 'var(--mood-tertiary, var(--mood-accent))',
tools: [
- { label: 'Simulateur de formules', icon: 'i-lucide-calculator', description: 'Tester les parametres de seuil WoT en temps reel', to: '/protocols/formulas', status: 'ready' },
- { label: 'Meta-gouvernance', icon: 'i-lucide-shield', description: 'Les formules elles-memes sont soumises au vote', status: 'ready' },
+ { label: 'Simulateur de formules', icon: 'i-lucide-calculator', description: 'Tester les paramètres de seuil WoT en temps réel', to: '/protocols/formulas', status: 'ready' },
+ { label: 'Méta-gouvernance', icon: 'i-lucide-shield', description: 'Les formules elles-mêmes sont soumises au vote', status: 'ready' },
{ label: 'Workflows n8n', icon: 'i-lucide-workflow', description: 'Automatisations optionnelles (notifications, alertes, relances)', status: 'soon' },
- { label: 'Protocoles operationnels', icon: 'i-lucide-git-branch', description: 'Processus multi-etapes reutilisables (embarquement, upgrade)', to: '/protocols', status: 'ready' },
+ { label: 'Protocoles opérationnels', icon: 'i-lucide-git-branch', description: 'Processus multi-étapes réutilisables (embarquement, upgrade)', to: '/protocols', status: 'ready' },
],
},
]
@@ -83,7 +83,7 @@ const sections: ToolSection[] = [
variant="ghost"
color="neutral"
icon="i-lucide-arrow-left"
- label="Retour a l'accueil"
+ label="Retour à l'accueil"
size="sm"
/>
@@ -92,10 +92,10 @@ const sections: ToolSection[] = [
@@ -143,7 +143,7 @@ const sections: ToolSection[] = [
{{ tool.label }}
- bientot
+ bientôt
{{ tool.description }}