feat(marketing): refonte narrative 'Comment ça marche' — processus 4 étapes step-by-step (capture → IA → distribution → gouvernance)
Au lieu d'un cycle générique 1→12, l'animation suit maintenant un FLOW PROCESSUS canonique qui raconte l'histoire produit du début à la fin : ÉTAPE 1 · CAPTURE → Recording (7) + Transcription (1) ÉTAPE 2 · TRANSFORMATION IA → Diarisation (2) + Langues (3) + Résumé (9) + Chat IA (0) ÉTAPE 3 · DISTRIBUTION → Exports (4) + Partage (6) + Users (5) + Intégrations (10) ÉTAPE 4 · GOUVERNANCE → Recherche IA (8) + Audit (11) + Conformité (12) PROCESS_ORDER = [7, 1, 2, 3, 9, 0, 4, 6, 5, 10, 8, 11, 12] — 13 sub-modes, mode 0 IA inclus dans le cycle (au lieu d'être skippé), 1100ms × 13 ≈ 14.3s tour complet. Composants ajoutés / refondus : - PROCESS BREADCRUMB visible au-dessus du phone (4 pilules · flèches · past/active/future) - PROGRESS BAR sous breadcrumb (gradient brand-b1 → b2 → b3, role=progressbar) - BOTTOM TAB BAR : 4 boutons étapes (au lieu de 6 features individuelles) - RIGHT PANEL : 4 mini-sections par étape (12 boutons regroupés selon flow) - FEATURE INFO CARD : préfixe 'Étape X/4 · TITRE' pour contexte processus - CONNECTING LINE : flèche directionnelle + dot animé qui se déplace selon processIdx - Mobile pills : suivent PROCESS_ORDER (13 sub-modes au lieu de 1-12) Alpine refactor : - selectedFeature devient un getter computed sur PROCESS_ORDER[processIdx] - nouveau STEPS array avec subModes mapping + activeStep / progressPercent getters - goToStep(id) helper jump au 1er sub-mode d'une étape - handleManualSelect(i) résout l'index dans PROCESS_ORDER Préservé : palette brand stricte (b1/b2/b3/navy), V3 radii, Inter+JetBrains Mono, phone shell statique, 13 templates de modes, IA Mistral premium card, eyebrow 'COMMENT ÇA MARCHE' brand-navy, WCAG (aria-current, aria-label, role=navigation, role=progressbar, role=tablist), prefers-reduced-motion guards. Tests : 9/9 fonctionnalites passent (test how_it_works_reactor étendu de +37 assertions narratives : PROCESS_ORDER, STEPS, processIdx, activeStep, goToStep, breadcrumb, progressbar, step-bottom, step-section, contexte processus dans card). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -408,6 +408,20 @@
|
|||||||
.dictia-feat-btn { transition: transform 0.18s ease, background-color 0.2s ease, opacity 0.2s ease; will-change: transform; }
|
.dictia-feat-btn { transition: transform 0.18s ease, background-color 0.2s ease, opacity 0.2s ease; will-change: transform; }
|
||||||
.dictia-feat-btn:hover { transform: scale(1.08); opacity: 1 !important; }
|
.dictia-feat-btn:hover { transform: scale(1.08); opacity: 1 !important; }
|
||||||
|
|
||||||
|
/* Process breadcrumb step buttons (top of section, light bg) */
|
||||||
|
.dictia-step-btn { transition: transform 0.2s ease, background-color 0.25s ease, border-color 0.25s ease; will-change: transform; }
|
||||||
|
.dictia-step-btn:hover { transform: translateY(-1px); }
|
||||||
|
.dictia-step-btn.is-past { opacity: 0.55; }
|
||||||
|
.dictia-step-btn.is-future { opacity: 0.40; }
|
||||||
|
.dictia-step-btn.is-active { transform: scale(1.03); }
|
||||||
|
|
||||||
|
/* Step navigator bottom (dark bg, inside phone) */
|
||||||
|
.dictia-step-bottom:hover { transform: translateY(-1px); }
|
||||||
|
|
||||||
|
/* Step section right panel (mini groups by stage) */
|
||||||
|
.dictia-step-section { transition: border-color 0.3s ease, background-color 0.3s ease, box-shadow 0.3s ease; }
|
||||||
|
.dictia-step-section.is-active { box-shadow: 0 0 24px rgba(0,0,0,0.25), inset 0 0 16px rgba(255,255,255,0.02); }
|
||||||
|
|
||||||
/* Hide scrollbar mobile pills */
|
/* Hide scrollbar mobile pills */
|
||||||
.dictia-hide-scrollbar::-webkit-scrollbar { display: none; }
|
.dictia-hide-scrollbar::-webkit-scrollbar { display: none; }
|
||||||
|
|
||||||
@@ -452,6 +466,7 @@
|
|||||||
.dictia-phone-glow-ring, .dictia-rec-dot, .dictia-wave-bar,
|
.dictia-phone-glow-ring, .dictia-rec-dot, .dictia-wave-bar,
|
||||||
.dictia-typing-dot, .dictia-lang-highlight, .dictia-connecting-line,
|
.dictia-typing-dot, .dictia-lang-highlight, .dictia-connecting-line,
|
||||||
.dictia-auto-pulse, .dictia-countdown-fill { animation: none !important; }
|
.dictia-auto-pulse, .dictia-countdown-fill { animation: none !important; }
|
||||||
|
.dictia-step-btn, .dictia-step-bottom, .dictia-step-section { transition: none !important; }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
@@ -496,23 +511,93 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# ── DASHBOARD HOLOGRAPHIQUE — 2 colonnes (phone center + IA right) ── #}
|
{# ── DASHBOARD HOLOGRAPHIQUE — wrapper outer column (process breadcrumb + progress + 2-col layout) ── #}
|
||||||
<div x-data="dictiaDashboard()" x-init="init()"
|
<div x-data="dictiaDashboard()" x-init="init()" class="w-full relative">
|
||||||
class="w-full flex flex-col lg:flex-row lg:justify-center lg:items-start gap-6 lg:gap-10 relative">
|
|
||||||
|
|
||||||
{# Connecting line SVG (phone -> IA panel, desktop only) #}
|
{# ─────────────── PROCESS BREADCRUMB — 4 étapes du flow utilisateur (refonte narrative 2026-04-29 v3) ─────────────── #}
|
||||||
|
<nav class="dictia-process-breadcrumb mb-3 mx-auto max-w-3xl"
|
||||||
|
role="navigation" aria-label="Étapes du processus DictIA">
|
||||||
|
<ol class="flex items-stretch justify-center gap-1.5 sm:gap-2 px-2">
|
||||||
|
<template x-for="(step, sIdx) in STEPS" :key="step.id">
|
||||||
|
<li class="flex items-center flex-1 min-w-0">
|
||||||
|
<button type="button" @click="goToStep(step.id)"
|
||||||
|
class="dictia-step-btn group relative flex flex-col items-start justify-center w-full px-2 sm:px-3 py-2 rounded transition-all outline-none focus-visible:ring-2 focus-visible:ring-brand-b1 text-left overflow-hidden"
|
||||||
|
:class="{
|
||||||
|
'is-active': activeStep === step.id,
|
||||||
|
'is-past': activeStep > step.id,
|
||||||
|
'is-future': activeStep < step.id
|
||||||
|
}"
|
||||||
|
:style="`border:1px solid ${activeStep === step.id ? step.color + '70' : 'rgba(0,0,0,0.10)'}; background-color:${activeStep === step.id ? step.color + '14' : 'rgba(255,255,255,0.70)'};`"
|
||||||
|
:aria-current="activeStep === step.id ? 'step' : false"
|
||||||
|
:aria-label="`Étape ${step.id} : ${step.title}`">
|
||||||
|
<div class="flex items-center gap-1.5 w-full min-w-0">
|
||||||
|
<span class="font-mono font-black text-[10px] tracking-widest flex-shrink-0"
|
||||||
|
:style="`color:${activeStep === step.id ? step.color : 'rgba(0,0,0,0.35)'};`"
|
||||||
|
x-text="`0${step.id}`"></span>
|
||||||
|
<span class="font-bold text-[11px] sm:text-xs leading-tight truncate"
|
||||||
|
:style="`color:${activeStep === step.id ? 'rgb(2,6,23)' : 'rgba(0,0,0,0.55)'};`"
|
||||||
|
x-text="step.title"></span>
|
||||||
|
</div>
|
||||||
|
<span class="font-mono text-[9px] uppercase tracking-wider mt-0.5 hidden sm:block"
|
||||||
|
:style="`color:${activeStep === step.id ? step.color : 'rgba(0,0,0,0.40)'};`"
|
||||||
|
x-text="step.subtitle"></span>
|
||||||
|
{# Active indicator bottom border 2px #}
|
||||||
|
<span x-show="activeStep === step.id" class="absolute left-0 right-0 bottom-0" aria-hidden="true"
|
||||||
|
:style="`height:2px;background:${step.color};box-shadow:0 0 8px ${step.color}80;`"></span>
|
||||||
|
</button>
|
||||||
|
{# Arrow between steps (skip last) #}
|
||||||
|
<template x-if="sIdx < STEPS.length - 1">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"
|
||||||
|
class="hidden sm:block flex-shrink-0 mx-0.5 text-brand-navy/30" style="width:14px;height:14px;" aria-hidden="true">
|
||||||
|
<polyline points="9 18 15 12 9 6"/>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</ol>
|
||||||
|
|
||||||
|
{# PROGRESS BAR — gradient brand-b1 → b2 → b3, width selon processIdx (13 sub-modes) #}
|
||||||
|
<div class="mx-auto mt-2 max-w-3xl px-2">
|
||||||
|
<div class="flex items-center justify-between mb-1">
|
||||||
|
<span class="font-mono text-[9px] uppercase tracking-widest text-brand-navy/50">
|
||||||
|
Progression du flow
|
||||||
|
</span>
|
||||||
|
<span class="font-mono text-[9px] font-bold text-brand-navy/65">
|
||||||
|
<span x-text="processIdx + 1"></span> / <span x-text="PROCESS_ORDER.length"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="w-full rounded-full overflow-hidden" style="height:2px;background:rgba(2,6,23,0.08);"
|
||||||
|
role="progressbar" :aria-valuenow="processIdx + 1" aria-valuemin="1" :aria-valuemax="PROCESS_ORDER.length">
|
||||||
|
<div class="h-full rounded-full transition-all duration-700 ease-out"
|
||||||
|
style="background:linear-gradient(90deg,#2563eb 0%,#06b6d4 50%,#c026d3 100%);box-shadow:0 0 8px rgba(192,38,211,0.35);"
|
||||||
|
:style="`width:${progressPercent}%;`"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{# ── 2 colonnes (phone center + IA right) ── #}
|
||||||
|
<div class="w-full flex flex-col lg:flex-row lg:justify-center lg:items-start gap-6 lg:gap-10 relative">
|
||||||
|
|
||||||
|
{# Connecting line SVG (phone -> IA panel, desktop only) — directional arrow with travelling dot #}
|
||||||
<div class="dictia-connecting-line-wrap absolute pointer-events-none hidden lg:block"
|
<div class="dictia-connecting-line-wrap absolute pointer-events-none hidden lg:block"
|
||||||
style="top:200px;left:50%;transform:translateX(-50%);width:160px;height:2px;z-index:1;"
|
style="top:200px;left:50%;transform:translateX(-50%);width:160px;height:12px;z-index:1;"
|
||||||
aria-hidden="true">
|
aria-hidden="true">
|
||||||
<svg width="160" height="2" viewBox="0 0 160 2" preserveAspectRatio="none">
|
<svg width="160" height="12" viewBox="0 0 160 12" preserveAspectRatio="none" overflow="visible">
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient id="dictia-connecting-grad" x1="0%" y1="0%" x2="100%" y2="0%">
|
<linearGradient id="dictia-connecting-grad" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||||
<stop offset="0%" stop-color="#06b6d4" stop-opacity="0.6"/>
|
<stop offset="0%" stop-color="#06b6d4" stop-opacity="0.6"/>
|
||||||
<stop offset="100%" stop-color="#c026d3" stop-opacity="0.6"/>
|
<stop offset="100%" stop-color="#c026d3" stop-opacity="0.6"/>
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
<line x1="0" y1="1" x2="160" y2="1" stroke="url(#dictia-connecting-grad)"
|
<line x1="0" y1="6" x2="148" y2="6" stroke="url(#dictia-connecting-grad)"
|
||||||
stroke-width="1.5" stroke-dasharray="4 4" class="dictia-connecting-line"/>
|
stroke-width="1.5" stroke-dasharray="4 4" class="dictia-connecting-line"/>
|
||||||
|
{# Arrow head pointing right (phone → right panel direction) #}
|
||||||
|
<polyline points="148,2 156,6 148,10" fill="none" stroke="#c026d3" stroke-opacity="0.7"
|
||||||
|
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
{# Travelling dot — animated position based on processIdx #}
|
||||||
|
<circle r="2.5" cy="6" fill="#c026d3"
|
||||||
|
:cx="6 + (140 * processIdx / (PROCESS_ORDER.length - 1))"
|
||||||
|
style="filter:drop-shadow(0 0 4px #c026d3); transition:cx 0.7s ease-out;"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1200,27 +1285,28 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# BOTTOM : 6 boutons feature originaux (Trans / Diari / Lang / Exp / Users / Part) + Auto pill — hauteur fixe 90px #}
|
{# BOTTOM : Step navigator — 4 boutons étapes (au lieu de 6 features individuelles) + Auto pill — refonte narrative 2026-04-29 v3 #}
|
||||||
<div class="relative flex flex-col items-center justify-center gap-1.5 z-10 flex-shrink-0"
|
<div class="relative flex flex-col items-center justify-center gap-1.5 z-10 flex-shrink-0"
|
||||||
style="height: 90px; border-top: 1px solid rgba(255,255,255,0.06); background: linear-gradient(180deg, transparent, rgba(0,0,0,0.32));">
|
style="height: 90px; border-top: 1px solid rgba(255,255,255,0.06); background: linear-gradient(180deg, transparent, rgba(0,0,0,0.32));">
|
||||||
|
|
||||||
{# 6 boutons features originaux (FEATURES indexes 1-6) — visible bottom tab bar #}
|
{# 4 step buttons (mapped to STEPS) — bottom step navigator #}
|
||||||
<div class="flex items-end gap-1.5 justify-center">
|
<div class="flex items-end gap-1 justify-center" role="tablist" aria-label="Étapes du processus">
|
||||||
<template x-for="i in [1,2,3,4,5,6]" :key="i">
|
<template x-for="step in STEPS" :key="step.id">
|
||||||
<button type="button" @click="handleManualSelect(i)"
|
<button type="button" @click="goToStep(step.id)"
|
||||||
class="dictia-feat-btn outline-none cursor-pointer focus-visible:ring-2 focus-visible:ring-white/40 flex flex-col items-center justify-end relative"
|
class="dictia-feat-btn dictia-step-bottom outline-none cursor-pointer focus-visible:ring-2 focus-visible:ring-white/40 flex flex-col items-center justify-end relative"
|
||||||
style="width:34px;height:42px;border:none;padding:4px 0 6px;background:transparent;"
|
style="min-width:48px;height:42px;border:none;padding:4px 6px 6px;background:transparent;"
|
||||||
:aria-pressed="selectedFeature === i ? 'true' : 'false'"
|
:aria-pressed="activeStep === step.id ? 'true' : 'false'"
|
||||||
:aria-label="`Fonction : ${FEATURES[i].title}`">
|
:aria-current="activeStep === step.id ? 'step' : false"
|
||||||
<span :style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.40)'}; transition: color 0.2s, transform 0.15s, filter 0.2s; filter: ${selectedFeature === i ? 'drop-shadow(0 0 6px ' + FEATURES[i].color + 'CC)' : 'none'}; transform: scale(${selectedFeature === i ? 1.15 : 1}); display: inline-block;`">
|
:aria-label="`Étape ${step.id} : ${step.title}`">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;" aria-hidden="true" x-html="featureIconPath(i)"></svg>
|
<span class="font-mono font-black text-[10px] tracking-widest leading-none"
|
||||||
</span>
|
:style="`color: ${activeStep === step.id ? step.color : 'rgba(255,255,255,0.40)'}; transition: color 0.2s; filter: ${activeStep === step.id ? 'drop-shadow(0 0 6px ' + step.color + 'CC)' : 'none'};`"
|
||||||
<span class="text-[9px] font-mono font-bold mt-0.5 leading-none uppercase tracking-wider"
|
x-text="`0${step.id}`"></span>
|
||||||
:style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.55)'};`"
|
<span class="text-[9px] font-mono font-bold mt-1 leading-none uppercase tracking-wider"
|
||||||
x-text="featureShortLabel(i)"></span>
|
:style="`color: ${activeStep === step.id ? step.color : 'rgba(255,255,255,0.55)'};`"
|
||||||
|
x-text="step.short"></span>
|
||||||
{# Tab indicator bottom border 2px #}
|
{# Tab indicator bottom border 2px #}
|
||||||
<span x-show="selectedFeature === i" class="absolute" aria-hidden="true"
|
<span x-show="activeStep === step.id" class="absolute" aria-hidden="true"
|
||||||
:style="`bottom: 0; left: 4px; right: 4px; height: 2px; background: ${FEATURES[i].color}; border-radius: 1px; box-shadow: 0 0 8px ${FEATURES[i].color};`"></span>
|
:style="`bottom: 0; left: 4px; right: 4px; height: 2px; background: ${step.color}; border-radius: 1px; box-shadow: 0 0 8px ${step.color};`"></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -1231,12 +1317,12 @@
|
|||||||
<span class="inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full"
|
<span class="inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full"
|
||||||
style="background:rgba(16,185,129,0.12);border:1px solid rgba(16,185,129,0.30);">
|
style="background:rgba(16,185,129,0.12);border:1px solid rgba(16,185,129,0.30);">
|
||||||
<span class="dictia-auto-pulse block rounded-full" style="width:5px;height:5px;background:#10b981;"></span>
|
<span class="dictia-auto-pulse block rounded-full" style="width:5px;height:5px;background:#10b981;"></span>
|
||||||
<span class="font-mono text-[9px] font-bold tracking-widest uppercase" style="color:#10b981;">Auto</span>
|
<span class="font-mono text-[9px] font-bold tracking-widest uppercase" style="color:#10b981;">Auto · 14 s</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template x-if="isManual">
|
<template x-if="isManual">
|
||||||
<span class="inline-flex flex-col items-center gap-0.5">
|
<span class="inline-flex flex-col items-center gap-0.5">
|
||||||
<span class="font-mono text-[9px] text-white/60">Auto reprend bientôt</span>
|
<span class="font-mono text-[9px] text-white/60">Manuel · Auto reprend bientôt</span>
|
||||||
<span class="rounded-full overflow-hidden" style="width:56px;height:2px;background:rgba(255,255,255,0.12);">
|
<span class="rounded-full overflow-hidden" style="width:56px;height:2px;background:rgba(255,255,255,0.12);">
|
||||||
<span class="block dictia-countdown-fill h-full" :key="selectedFeature" style="background:linear-gradient(90deg,#06b6d4,#c026d3);"></span>
|
<span class="block dictia-countdown-fill h-full" :key="selectedFeature" style="background:linear-gradient(90deg,#06b6d4,#c026d3);"></span>
|
||||||
</span>
|
</span>
|
||||||
@@ -1247,20 +1333,26 @@
|
|||||||
</div>{# /dictia-phone-shell #}
|
</div>{# /dictia-phone-shell #}
|
||||||
</div>{# /phone wrapper (glow ring) #}
|
</div>{# /phone wrapper (glow ring) #}
|
||||||
|
|
||||||
{# Feature info card sous le phone — fond bg-brand-navy SOLIDE (extension visuelle du phone, contraste WCAG AA garanti sur section claire) + accent border-left tab indicator #}
|
{# Feature info card sous le phone — affiche contexte processus (étape + sub-mode) — bg-brand-navy SOLIDE + accent border-left #}
|
||||||
<div style="min-height:64px;width:280px;max-width:100%;" class="mt-3 mx-auto">
|
<div style="min-height:80px;width:280px;max-width:100%;" class="mt-3 mx-auto">
|
||||||
<div class="dictia-feature-card rounded-xl px-4 py-3 relative bg-brand-navy border border-white/10 overflow-hidden"
|
<div class="dictia-feature-card rounded-xl px-4 py-3 relative bg-brand-navy border border-white/10 overflow-hidden"
|
||||||
:style="`box-shadow: 0 6px 24px rgba(0,0,0,0.20);`">
|
:style="`box-shadow: 0 6px 24px rgba(0,0,0,0.20);`">
|
||||||
{# Border-left 3px accent — style tab indicator #}
|
{# Border-left 3px accent — style tab indicator #}
|
||||||
<span class="absolute left-0 top-0 bottom-0" aria-hidden="true"
|
<span class="absolute left-0 top-0 bottom-0" aria-hidden="true"
|
||||||
:style="`width:3px;background:${activeColor};box-shadow:0 0 8px ${activeColor}80;`"></span>
|
:style="`width:3px;background:${activeColor};box-shadow:0 0 8px ${activeColor}80;`"></span>
|
||||||
{# Badge top-right #}
|
{# Process context header : ÉTAPE X/4 · TITRE — small breadcrumb pill #}
|
||||||
|
<div class="flex items-center justify-between gap-2 mb-2">
|
||||||
|
<span class="font-mono text-[9px] font-bold uppercase tracking-widest"
|
||||||
|
:style="`color:${STEPS[activeStep - 1].color};`">
|
||||||
|
Étape <span x-text="activeStep"></span>/4 · <span x-text="STEPS[activeStep - 1].title"></span>
|
||||||
|
</span>
|
||||||
|
{# Badge top-right (sub-mode) #}
|
||||||
<template x-if="FEATURES[displayMode].badge">
|
<template x-if="FEATURES[displayMode].badge">
|
||||||
<span class="absolute text-[10px] px-1.5 py-0.5 rounded font-mono font-bold tracking-wider"
|
<span class="text-[9px] px-1.5 py-0.5 rounded font-mono font-bold tracking-wider"
|
||||||
style="top:8px;right:8px;"
|
|
||||||
:style="`background-color: ${activeColor}22; color: ${activeColor}; border: 1px solid ${activeColor}55;`"
|
:style="`background-color: ${activeColor}22; color: ${activeColor}; border: 1px solid ${activeColor}55;`"
|
||||||
x-text="FEATURES[displayMode].badge"></span>
|
x-text="FEATURES[displayMode].badge"></span>
|
||||||
</template>
|
</template>
|
||||||
|
</div>
|
||||||
<div class="flex items-start gap-2.5">
|
<div class="flex items-start gap-2.5">
|
||||||
{# Icon container 32×32 #}
|
{# Icon container 32×32 #}
|
||||||
<span class="rounded flex items-center justify-center flex-shrink-0"
|
<span class="rounded flex items-center justify-center flex-shrink-0"
|
||||||
@@ -1271,7 +1363,7 @@
|
|||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;" x-html="featureIconPath(displayMode)"></svg>
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:16px;height:16px;" x-html="featureIconPath(displayMode)"></svg>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<div class="flex-1 min-w-0 pr-12">
|
<div class="flex-1 min-w-0">
|
||||||
<p class="text-sm font-bold text-white leading-tight mb-0.5" x-text="FEATURES[displayMode].title"></p>
|
<p class="text-sm font-bold text-white leading-tight mb-0.5" x-text="FEATURES[displayMode].title"></p>
|
||||||
<p class="text-[11px] leading-snug text-white/65" x-text="FEATURES[displayMode].subtitle"></p>
|
<p class="text-[11px] leading-snug text-white/65" x-text="FEATURES[displayMode].subtitle"></p>
|
||||||
</div>
|
</div>
|
||||||
@@ -1279,10 +1371,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Mobile : pills features horizontales scrollables (12 fonctions, indexes 1-12) #}
|
{# Mobile : pills features horizontales scrollables — suivent PROCESS_ORDER (13 sub-modes) #}
|
||||||
<div class="lg:hidden w-full overflow-x-auto dictia-hide-scrollbar" style="scrollbar-width:none;">
|
<div class="lg:hidden w-full overflow-x-auto dictia-hide-scrollbar" style="scrollbar-width:none;">
|
||||||
<div class="flex gap-2 px-1 pb-1" style="width:max-content;">
|
<div class="flex gap-2 px-1 pb-1" style="width:max-content;">
|
||||||
<template x-for="i in [1,2,3,4,5,6,7,8,9,10,11,12]" :key="i">
|
<template x-for="i in PROCESS_ORDER" :key="i">
|
||||||
<button type="button" @click="handleManualSelect(i)"
|
<button type="button" @click="handleManualSelect(i)"
|
||||||
class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-full shrink-0 transition-all focus-visible:ring-2 focus-visible:ring-brand-b1"
|
class="flex items-center gap-1.5 px-2.5 py-1.5 rounded-full shrink-0 transition-all focus-visible:ring-2 focus-visible:ring-brand-b1"
|
||||||
:style="`border: 1px solid ${selectedFeature === i ? FEATURES[i].color + '70' : 'rgba(0,0,0,0.10)'}; background-color: ${selectedFeature === i ? FEATURES[i].color + '18' : 'rgba(0,0,0,0.04)'}; outline: none;`"
|
:style="`border: 1px solid ${selectedFeature === i ? FEATURES[i].color + '70' : 'rgba(0,0,0,0.10)'}; background-color: ${selectedFeature === i ? FEATURES[i].color + '18' : 'rgba(0,0,0,0.04)'}; outline: none;`"
|
||||||
@@ -1313,7 +1405,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-[10px] font-mono font-bold px-2 py-0.5 rounded-full text-brand-b3 whitespace-nowrap"
|
<span class="text-[10px] font-mono font-bold px-2 py-0.5 rounded-full text-brand-b3 whitespace-nowrap"
|
||||||
style="background-color:rgba(192,38,211,0.10);border:1px solid rgba(192,38,211,0.25);">12 fonctions</span>
|
style="background-color:rgba(192,38,211,0.10);border:1px solid rgba(192,38,211,0.25);">12 fonctions · 4 étapes</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# IA Mistral 7B premium card — couleurs brand-b3 (fuchsia officiel) #}
|
{# IA Mistral 7B premium card — couleurs brand-b3 (fuchsia officiel) #}
|
||||||
@@ -1409,26 +1501,49 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Feature grid 3×4 — 12 fonctions (FEATURES indexes 1-12, IA mode 0 accessible via Brain card ci-dessus) #}
|
{# Feature grid REFONTE NARRATIVE — 4 mini-sections par étape (12 boutons regroupés selon flow) #}
|
||||||
<div class="grid grid-cols-3 gap-2">
|
<div class="flex flex-col gap-3">
|
||||||
<template x-for="i in [1,2,3,4,5,6,7,8,9,10,11,12]" :key="i">
|
<template x-for="step in STEPS" :key="step.id">
|
||||||
|
<div class="dictia-step-section rounded-xl p-2.5"
|
||||||
|
:class="activeStep === step.id ? 'is-active' : ''"
|
||||||
|
:style="`border:1px solid ${activeStep === step.id ? step.color + '60' : 'rgba(255,255,255,0.08)'}; background-color:${activeStep === step.id ? 'rgba(8,12,24,0.92)' : 'rgba(8,12,24,0.65)'}; transition:all 0.3s ease;`"
|
||||||
|
:aria-current="activeStep === step.id ? 'step' : false">
|
||||||
|
{# Mini header : badge numéro + titre #}
|
||||||
|
<div class="flex items-center gap-2 mb-2 px-1">
|
||||||
|
<span class="rounded-full flex items-center justify-center flex-shrink-0 font-mono font-black text-[10px]"
|
||||||
|
style="width:18px;height:18px;"
|
||||||
|
:style="`background:linear-gradient(135deg,${step.color}30,${step.color}14);border:1px solid ${step.color}60;color:${step.color};`"
|
||||||
|
aria-hidden="true"
|
||||||
|
x-text="step.id"></span>
|
||||||
|
<span class="font-mono text-[10px] font-bold uppercase tracking-widest"
|
||||||
|
:style="`color:${activeStep === step.id ? step.color : 'rgba(255,255,255,0.55)'};`">
|
||||||
|
Étape <span x-text="step.id"></span> · <span x-text="step.title"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{# Mini buttons — flex wrap compact #}
|
||||||
|
<div class="flex flex-wrap gap-1.5">
|
||||||
|
<template x-for="i in step.subModes" :key="i">
|
||||||
<button type="button" @click="handleManualSelect(i)"
|
<button type="button" @click="handleManualSelect(i)"
|
||||||
class="flex flex-col items-center gap-1.5 rounded-xl py-2.5 px-2 outline-none transition-all focus-visible:ring-2 focus-visible:ring-white/40 text-center"
|
class="flex items-center gap-1.5 rounded py-1.5 px-2 outline-none transition-all focus-visible:ring-2 focus-visible:ring-white/40 cursor-pointer"
|
||||||
:style="`border: 1px solid ${selectedFeature === i ? FEATURES[i].color + '70' : 'rgba(255,255,255,0.10)'}; background-color: ${selectedFeature === i ? 'rgba(8,12,24,0.95)' : 'rgba(8,12,24,0.85)'}; box-shadow: ${selectedFeature === i ? '0 0 16px ' + FEATURES[i].color + '40, inset 0 0 16px ' + FEATURES[i].color + '20' : 'none'}; cursor: pointer; min-height:64px;`"
|
:style="`border:1px solid ${selectedFeature === i ? FEATURES[i].color + '70' : 'rgba(255,255,255,0.10)'}; background-color:${selectedFeature === i ? FEATURES[i].color + '20' : 'rgba(8,12,24,0.55)'}; box-shadow:${selectedFeature === i ? '0 0 12px ' + FEATURES[i].color + '40, inset 0 0 8px ' + FEATURES[i].color + '15' : 'none'}; min-height:30px;`"
|
||||||
:aria-pressed="selectedFeature === i ? 'true' : 'false'"
|
:aria-pressed="selectedFeature === i ? 'true' : 'false'"
|
||||||
:aria-label="`Fonction : ${FEATURES[i].title}`">
|
:aria-label="`Fonction : ${FEATURES[i].title}`">
|
||||||
<span :style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.55)'}; filter: ${selectedFeature === i ? 'drop-shadow(0 0 6px ' + FEATURES[i].color + 'BB)' : 'none'}; transition: all 0.2s; transform: scale(${selectedFeature === i ? 1.15 : 1}); display:inline-block;`">
|
<span :style="`color:${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.55)'}; filter:${selectedFeature === i ? 'drop-shadow(0 0 4px ' + FEATURES[i].color + 'BB)' : 'none'}; transition:all 0.2s; transform:scale(${selectedFeature === i ? 1.1 : 1}); display:inline-block;`">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:18px;height:18px;" aria-hidden="true" x-html="featureIconPath(i)"></svg>
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:13px;height:13px;" aria-hidden="true" x-html="featureIconPath(i)"></svg>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-[10px] font-bold leading-tight w-full truncate"
|
<span class="text-[10px] font-semibold leading-none whitespace-nowrap"
|
||||||
:style="`color: ${selectedFeature === i ? 'rgba(255,255,255,0.98)' : 'rgba(255,255,255,0.85)'};`"
|
:style="`color:${selectedFeature === i ? 'rgba(255,255,255,0.98)' : 'rgba(255,255,255,0.80)'};`"
|
||||||
x-text="featureGridLabel(i)"></span>
|
x-text="featureGridLabel(i)"></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>{# /2-col flex layout #}
|
||||||
|
</div>{# /dictiaDashboard wrapper outer column #}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1453,29 +1568,47 @@
|
|||||||
{ idx: 11, title: 'Audit trail', subtitle: 'Traçabilité Loi 25 immutable', color: '#2563eb', badge: 'Loi 25' }, /* brand-b1 */
|
{ idx: 11, title: 'Audit trail', subtitle: 'Traçabilité Loi 25 immutable', color: '#2563eb', badge: 'Loi 25' }, /* brand-b1 */
|
||||||
{ idx: 12, title: 'Conformité', subtitle: '9 ordres pros · EFVP CAI', color: '#c026d3', badge: 'MCN' } /* brand-b3 */
|
{ idx: 12, title: 'Conformité', subtitle: '9 ordres pros · EFVP CAI', color: '#c026d3', badge: 'MCN' } /* brand-b3 */
|
||||||
],
|
],
|
||||||
selectedFeature: 1,
|
/* PROCESS NARRATIF — ordre canonique du flow utilisateur (refonte 2026-04-29 v3).
|
||||||
|
Cycle : Capture → Transformation IA → Distribution → Recherche & Gouvernance.
|
||||||
|
13 sub-modes (mode 0 IA inclus) — auto-cycle ≈ 14.3s tour complet. */
|
||||||
|
PROCESS_ORDER: [7, 1, 2, 3, 9, 0, 4, 6, 5, 10, 8, 11, 12],
|
||||||
|
STEPS: [
|
||||||
|
{ id: 1, title: 'Capture', short: 'Capture', subtitle: 'Audio in', subModes: [7, 1], color: '#06b6d4' }, /* brand-b2 */
|
||||||
|
{ id: 2, title: 'Transformation IA', short: 'Transform.', subtitle: 'Le moteur', subModes: [2, 3, 9, 0], color: '#c026d3' }, /* brand-b3 */
|
||||||
|
{ id: 3, title: 'Distribution', short: 'Distrib.', subtitle: 'Partage', subModes: [4, 6, 5, 10], color: '#2563eb' }, /* brand-b1 */
|
||||||
|
{ id: 4, title: 'Recherche & Gouvernance', short: 'Gouv.', subtitle: 'Long terme',subModes: [8, 11, 12], color: '#06b6d4' } /* brand-b2 */
|
||||||
|
],
|
||||||
|
processIdx: 0,
|
||||||
isManual: false,
|
isManual: false,
|
||||||
autoCycleTimer: null,
|
autoCycleTimer: null,
|
||||||
manualResetTimer: null,
|
manualResetTimer: null,
|
||||||
|
get selectedFeature() { return this.PROCESS_ORDER[this.processIdx]; },
|
||||||
get displayMode() { return this.selectedFeature; },
|
get displayMode() { return this.selectedFeature; },
|
||||||
get activeColor() { return this.FEATURES[this.displayMode].color; },
|
get activeColor() { return this.FEATURES[this.displayMode].color; },
|
||||||
|
get activeStep() {
|
||||||
|
const sf = this.selectedFeature;
|
||||||
|
const s = this.STEPS.find(st => st.subModes.includes(sf));
|
||||||
|
return s ? s.id : 1;
|
||||||
|
},
|
||||||
|
get progressPercent() {
|
||||||
|
return Math.round(((this.processIdx + 1) / this.PROCESS_ORDER.length) * 100);
|
||||||
|
},
|
||||||
init() {
|
init() {
|
||||||
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||||
this.startAutoCycle();
|
this.startAutoCycle();
|
||||||
},
|
},
|
||||||
/* Auto-cycle logic : avance 1→12 (skip 0=IA mode, accessible via Brain card), 1100ms each.
|
/* Auto-cycle logic : suit PROCESS_ORDER (13 sub-modes), 1100ms each.
|
||||||
Cycle complet : 12 features × 1100ms ≈ 13.2s puis recommence à 1. */
|
Cycle complet : 13 modes × 1100ms ≈ 14.3s puis recommence à processIdx=0. */
|
||||||
startAutoCycle() {
|
startAutoCycle() {
|
||||||
if (this.autoCycleTimer) clearInterval(this.autoCycleTimer);
|
if (this.autoCycleTimer) clearInterval(this.autoCycleTimer);
|
||||||
this.autoCycleTimer = setInterval(() => {
|
this.autoCycleTimer = setInterval(() => {
|
||||||
/* Avance 1→2→...→12, puis retour à 1 (skip 0=IA) */
|
this.processIdx = (this.processIdx + 1) % this.PROCESS_ORDER.length;
|
||||||
this.selectedFeature = this.selectedFeature >= 12 ? 1 : this.selectedFeature + 1;
|
|
||||||
if (this.selectedFeature === 0) this.selectedFeature = 1;
|
|
||||||
}, 1100);
|
}, 1100);
|
||||||
},
|
},
|
||||||
/* Click manuel sur un bouton feature : sélectionne ce mode + freeze auto 4500ms */
|
/* Click manuel sur un bouton feature : trouve son index dans PROCESS_ORDER + freeze auto 4500ms */
|
||||||
handleManualSelect(i) {
|
handleManualSelect(i) {
|
||||||
this.selectedFeature = i;
|
const idx = this.PROCESS_ORDER.indexOf(i);
|
||||||
|
if (idx >= 0) this.processIdx = idx;
|
||||||
this.isManual = true;
|
this.isManual = true;
|
||||||
if (this.autoCycleTimer) { clearInterval(this.autoCycleTimer); this.autoCycleTimer = null; }
|
if (this.autoCycleTimer) { clearInterval(this.autoCycleTimer); this.autoCycleTimer = null; }
|
||||||
if (this.manualResetTimer) clearTimeout(this.manualResetTimer);
|
if (this.manualResetTimer) clearTimeout(this.manualResetTimer);
|
||||||
@@ -1484,6 +1617,12 @@
|
|||||||
this.startAutoCycle();
|
this.startAutoCycle();
|
||||||
}, 4500);
|
}, 4500);
|
||||||
},
|
},
|
||||||
|
/* Click sur une étape du breadcrumb / bottom step nav : jump au 1er sub-mode de l'étape */
|
||||||
|
goToStep(stepId) {
|
||||||
|
const step = this.STEPS.find(s => s.id === stepId);
|
||||||
|
if (!step || step.subModes.length === 0) return;
|
||||||
|
this.handleManualSelect(step.subModes[0]);
|
||||||
|
},
|
||||||
featureIconPath(i) {
|
featureIconPath(i) {
|
||||||
const paths = {
|
const paths = {
|
||||||
0: '<path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/>',
|
0: '<path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44 2.5 2.5 0 0 1-2.96-3.08 3 3 0 0 1-.34-5.58 2.5 2.5 0 0 1 1.32-4.24 2.5 2.5 0 0 1 1.98-3A2.5 2.5 0 0 1 9.5 2Z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44 2.5 2.5 0 0 0 2.96-3.08 3 3 0 0 0 .34-5.58 2.5 2.5 0 0 0-1.32-4.24 2.5 2.5 0 0 0-1.98-3A2.5 2.5 0 0 0 14.5 2Z"/>',
|
||||||
|
|||||||
@@ -300,6 +300,42 @@ def test_fonctionnalites_how_it_works_reactor_section():
|
|||||||
# Performance metrics : 3 cells with grad-text for 0ms / 24/7
|
# Performance metrics : 3 cells with grad-text for 0ms / 24/7
|
||||||
assert 'grad-text' in body, "Missing grad-text on metrics"
|
assert 'grad-text' in body, "Missing grad-text on metrics"
|
||||||
|
|
||||||
|
# ── REFONTE NARRATIVE PROCESSUS 2026-04-29 v3 ──────────────────────────
|
||||||
|
# PROCESS_ORDER : ordre canonique du flow utilisateur (13 sub-modes, mode 0 inclus)
|
||||||
|
assert 'PROCESS_ORDER' in body, "Missing PROCESS_ORDER array (narrative refactor)"
|
||||||
|
assert '[7, 1, 2, 3, 9, 0, 4, 6, 5, 10, 8, 11, 12]' in body, \
|
||||||
|
"Missing canonical PROCESS_ORDER sequence (Capture → Transform IA → Distribution → Gouvernance)"
|
||||||
|
# 4 STEPS (étapes principales) + helpers
|
||||||
|
assert 'STEPS:' in body, "Missing STEPS array"
|
||||||
|
assert "'Capture'" in body, "Missing étape 1 Capture"
|
||||||
|
assert "'Transformation IA'" in body, "Missing étape 2 Transformation IA"
|
||||||
|
assert "'Distribution'" in body, "Missing étape 3 Distribution"
|
||||||
|
assert "'Recherche & Gouvernance'" in body, "Missing étape 4 Recherche & Gouvernance"
|
||||||
|
assert 'subModes:' in body, "Missing subModes mapping"
|
||||||
|
# Computed states + helpers
|
||||||
|
assert 'processIdx' in body, "Missing processIdx state"
|
||||||
|
assert 'activeStep' in body, "Missing activeStep getter"
|
||||||
|
assert 'goToStep' in body, "Missing goToStep helper"
|
||||||
|
assert 'progressPercent' in body, "Missing progressPercent getter"
|
||||||
|
# Process breadcrumb visible above phone
|
||||||
|
assert 'dictia-process-breadcrumb' in body, "Missing process breadcrumb wrapper"
|
||||||
|
assert 'Étapes du processus DictIA' in body, "Missing breadcrumb aria-label"
|
||||||
|
assert 'aria-current' in body, "Missing aria-current on active step"
|
||||||
|
# Progress bar
|
||||||
|
assert 'role="progressbar"' in body, "Missing progressbar role"
|
||||||
|
assert 'Progression du flow' in body, "Missing progress bar label"
|
||||||
|
# Bottom step navigator (4 buttons étapes au lieu de 6 features)
|
||||||
|
assert 'dictia-step-bottom' in body, "Missing bottom step navigator class"
|
||||||
|
assert 'Étapes du processus' in body, "Missing bottom tablist aria-label"
|
||||||
|
# Right panel : 4 mini-sections par étape
|
||||||
|
assert 'dictia-step-section' in body, "Missing right panel step sections"
|
||||||
|
# Feature info card : context processus (Étape X/4 · Titre)
|
||||||
|
assert 'STEPS[activeStep - 1]' in body, "Missing process context binding in feature card"
|
||||||
|
# Connecting line : travelling dot following processIdx
|
||||||
|
assert 'PROCESS_ORDER.length' in body, "Missing PROCESS_ORDER.length binding (travelling dot or progress)"
|
||||||
|
# Right panel header : updated badge
|
||||||
|
assert '12 fonctions · 4 étapes' in body, "Missing right panel updated badge"
|
||||||
|
|
||||||
|
|
||||||
def test_fonctionnalites_export_formats_section():
|
def test_fonctionnalites_export_formats_section():
|
||||||
client = app.test_client()
|
client = app.test_client()
|
||||||
|
|||||||
Reference in New Issue
Block a user