Restructure Engagement Forgeron + fix GenesisBlock + InertiaSlider

- Seed: restructure Engagement Forgeron (51→59 items) avec 3 nouvelles
  sections: Engagements fondamentaux (EF1-EF3), Engagements techniques
  (ET1-ET3), Qualification (Q0-Q1) liée au protocole Embarquement
- Seed: ajout protocole Embarquement Forgeron (5 jalons: candidature,
  miroir, évaluation, certification Smith, mise en ligne)
- GenesisBlock: fix lisibilité — fond mood-surface teinté accent au lieu
  de mood-text inversé, texte mood-aware au lieu de rgba blanc hardcodé
- InertiaSlider: mini affiche "Inertie" sous le curseur, compact en
  width:fit-content pour s'adapter au label
- Frontend: ajout section qualification dans SECTION_META/SECTION_ORDER
- Pages, composants et tests des sprints précédents

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-03-03 03:44:33 +01:00
parent 4212e847d4
commit c19c1aa55e
16 changed files with 3002 additions and 361 deletions

View File

@@ -109,6 +109,35 @@ async function createProtocol() {
}
}
/** Operational protocols (workflow templates). */
interface WorkflowStep {
label: string
actor: string
icon: string
type: string
}
const operationalProtocols = [
{
slug: 'embarquement-forgeron',
name: 'Embarquement Forgeron',
description: 'Processus complet d\'intégration d\'un nouveau forgeron dans le réseau Duniter.',
category: 'onboarding',
icon: 'i-lucide-hammer',
instancesLabel: '~10-50 / an',
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: 'Go online', actor: 'Candidat', icon: 'i-lucide-wifi', type: 'on_chain' },
] as WorkflowStep[],
},
]
/** n8n workflow demo items. */
const n8nWorkflows = [
{
@@ -229,6 +258,50 @@ const n8nWorkflows = [
</NuxtLink>
</div>
<!-- Operational protocols (workflow templates) -->
<div class="proto-ops">
<h3 class="proto-ops__title">
<UIcon name="i-lucide-git-branch" class="text-sm" />
Protocoles operationnels
<span class="proto-ops__count">{{ operationalProtocols.length }}</span>
</h3>
<div
v-for="op in operationalProtocols"
:key="op.slug"
class="proto-ops__card"
>
<div class="proto-ops__card-head">
<div class="proto-ops__card-icon">
<UIcon :name="op.icon" class="text-lg" />
</div>
<div class="proto-ops__card-info">
<h4 class="proto-ops__card-name">{{ op.name }}</h4>
<p class="proto-ops__card-desc">{{ op.description }}</p>
<span class="proto-ops__card-meta">{{ op.instancesLabel }}</span>
</div>
</div>
<!-- Step timeline -->
<div class="proto-ops__timeline">
<div
v-for="(step, idx) in op.steps"
:key="idx"
class="proto-ops__step"
>
<div class="proto-ops__step-dot" :class="`proto-ops__step-dot--${step.type}`">
<UIcon :name="step.icon" class="text-xs" />
</div>
<div class="proto-ops__step-body">
<span class="proto-ops__step-label">{{ step.label }}</span>
<span class="proto-ops__step-actor">{{ step.actor }}</span>
</div>
<div v-if="idx < op.steps.length - 1" class="proto-ops__step-line" />
</div>
</div>
</div>
</div>
<!-- Formulas table -->
<div class="proto-formulas">
<h3 class="proto-formulas__title">
@@ -802,6 +875,152 @@ const n8nWorkflows = [
margin: 0;
}
/* --- Operational protocols --- */
.proto-ops {
margin-top: 1.5rem;
}
.proto-ops__title {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9375rem;
font-weight: 700;
color: var(--mood-text);
margin: 0 0 0.75rem;
}
.proto-ops__count {
font-size: 0.6875rem;
font-weight: 700;
background: var(--mood-accent-soft);
color: var(--mood-accent);
padding: 2px 8px;
border-radius: 20px;
}
.proto-ops__card {
background: var(--mood-surface);
border-radius: 16px;
padding: 1.25rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
.proto-ops__card-head {
display: flex;
gap: 0.75rem;
align-items: flex-start;
}
.proto-ops__card-icon {
width: 2.75rem;
height: 2.75rem;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background: var(--mood-accent-soft);
color: var(--mood-accent);
}
.proto-ops__card-info {
flex: 1;
min-width: 0;
}
.proto-ops__card-name {
font-size: 1.0625rem;
font-weight: 800;
color: var(--mood-text);
margin: 0;
}
.proto-ops__card-desc {
font-size: 0.8125rem;
color: var(--mood-text-muted);
line-height: 1.4;
margin: 0.125rem 0 0;
}
.proto-ops__card-meta {
font-size: 0.6875rem;
font-weight: 600;
color: var(--mood-accent);
opacity: 0.7;
}
/* Timeline */
.proto-ops__timeline {
display: flex;
flex-direction: column;
gap: 0;
padding-left: 0.25rem;
}
.proto-ops__step {
display: flex;
align-items: center;
gap: 0.625rem;
position: relative;
padding: 0.375rem 0;
}
.proto-ops__step-dot {
width: 1.75rem;
height: 1.75rem;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--mood-accent-soft);
color: var(--mood-accent);
z-index: 1;
}
.proto-ops__step-dot--on_chain {
background: color-mix(in srgb, var(--mood-success) 15%, transparent);
color: var(--mood-success);
}
.proto-ops__step-dot--checklist {
background: color-mix(in srgb, var(--mood-warning) 15%, transparent);
color: var(--mood-warning);
}
.proto-ops__step-dot--certification {
background: color-mix(in srgb, var(--mood-secondary) 15%, transparent);
color: var(--mood-secondary, var(--mood-accent));
}
.proto-ops__step-body {
display: flex;
flex-direction: column;
}
.proto-ops__step-label {
font-size: 0.8125rem;
font-weight: 700;
color: var(--mood-text);
}
.proto-ops__step-actor {
font-size: 0.6875rem;
color: var(--mood-text-muted);
}
.proto-ops__step-line {
position: absolute;
left: calc(0.875rem - 1px);
top: calc(0.375rem + 1.75rem);
width: 2px;
height: calc(100% - 1.75rem + 0.375rem);
background: color-mix(in srgb, var(--mood-accent) 15%, transparent);
}
/* --- Modal --- */
.proto-modal {
padding: 1.25rem;