feat(marketing): refonte stratégique 'Comment ça marche' — 4 catégories × 12 sous-modes
Audit complet des fonctionnalités réelles DictIA (manuel utilisateur + composants production) puis restructuration de l'animation phone container en 4 catégories logiques regroupant 12 sous-modes (6 historiques + 6 nouveaux). CATÉGORIES (bottom tab bar 4 boutons + sub-mode dots indicator) : - Capture (b2 cyan) : Transcription, Recording live, Recherche IA - Transformation IA (b1) : Diarisation, 99+ langues, Résumé+actions, Chat IA - Distribution (b3 fuchsia) : Exports, Intégrations Hub, Partage, Users - Gouvernance (b1 blue) : Audit trail, Conformité Loi 25 + 9 ordres pros NOUVEAUX MODES IMPLÉMENTÉS : - Mode 7 Recording live : minuteur 99s + waveform 24 bars random + 3 boutons sources (mic/système/combiné) selon manuel utilisation v1.0 - Mode 8 Recherche sémantique : query typed + 3 résultats highlight RAG - Mode 9 Résumé + actions : décisions/actions stagger + extraction ICS - Mode 10 Intégrations : hub central DictIA + 8 logos en orbite (Word, Outlook, Teams, Notion, Obsidian, Zapier, Make, n8n) + lignes connexion SVG - Mode 11 Audit trail : 6 events horodatés (INFO/AUTH/PROC/READ/EXP/SHARE) + badge consentement tracé immutable (Loi 25 art. 8) - Mode 12 Conformité Loi 25 : 6 badges (Loi 25/96/EFVP CAI/MCN/AGPL/0 Cloud Act US) + 9 ordres pros (Barreau, CNQ, CPA, ChAD, OACIQ, CMQ, OIIQ, OPQ, OEQ) REFACTOR Alpine dictiaDashboard() : - FEATURES étendu de 7 à 13 entrées (idx 0-12) - CATEGORIES array avec submodes[], iconPath, color, subtitle - activeCategory + handleCategorySelect(ci) en plus de handleManualSelect(i) - Auto-cycle 1100ms entre sous-modes ; switch catégorie quand fin atteinte - Right grid 3×6 → 2×2 categories cards (preview sub-modes dots) - Bottom tab bar 6 modes → 4 catégories (icons larger 18px) + sub-mode dots - Mobile pills par catégorie (au lieu de par mode) Préservé : palette brand-b1/b2/b3 stricte, phone shell statique 280×580, WCAG AA, prefers-reduced-motion, eyebrow text-brand-navy, IA Mistral card, section integrations, architecture, conformité-resume. Tests : 9/9 passent dans test_fonctionnalites_*. Assertions ajoutées pour les 4 catégories + 6 nouveaux modes + handleCategorySelect + 1100ms cycle. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -249,11 +249,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{# ===== COMMENT ÇA MARCHE — DASHBOARD HOLOGRAPHIQUE =====
|
{# ===== COMMENT ÇA MARCHE — DASHBOARD HOLOGRAPHIQUE (4 CATÉGORIES × 12 SOUS-MODES) =====
|
||||||
Reproduction fidèle de DashboardHolographique (Website-Sanity/dictai-narrative.tsx).
|
Reproduction fidèle de DashboardHolographique (Website-Sanity/dictai-narrative.tsx),
|
||||||
Phone container central + 6 modes uniques (Transcription, Diarisation, Langues,
|
refonte stratégique 2026-04-29 : 4 catégories (Capture · IA · Distribution · Gouvernance)
|
||||||
Exports, Users, Share) + IA Mistral 7B premium card + grid 6 features.
|
regroupant 12 sous-modes (6 historiques + 6 nouveaux : Recording, Recherche, Résumé,
|
||||||
Auto-cycle 900ms (1→6→1, skip IA index 0). Click manuel → 4500ms isManual.
|
Intégrations, Audit, Loi 25, AGPL).
|
||||||
|
Phone container central + 12 sous-modes + IA Mistral 7B premium card + grid 4 catégories.
|
||||||
|
Auto-cycle : sous-mode toutes les 1100ms ; passage à catégorie suivante quand fin atteinte.
|
||||||
|
Click manuel → 4500ms isManual. Catégorie 4 (Gouvernance) inclut audit trail / Loi 25 / AGPL.
|
||||||
#}
|
#}
|
||||||
<section class="bg-brand-bg py-20 relative overflow-hidden" aria-labelledby="how-it-works-title">
|
<section class="bg-brand-bg py-20 relative overflow-hidden" aria-labelledby="how-it-works-title">
|
||||||
<style>
|
<style>
|
||||||
@@ -461,8 +464,8 @@
|
|||||||
|
|
||||||
<div class="max-w-[1200px] mx-auto px-6 relative">
|
<div class="max-w-[1200px] mx-auto px-6 relative">
|
||||||
<div class="text-center max-w-2xl mx-auto mb-10">
|
<div class="text-center max-w-2xl mx-auto mb-10">
|
||||||
{# Eyebrow : grad-text uppercase tracking-widest text-xs avec dot pulse brand-b3 #}
|
{# Eyebrow : typo NOIRE sur fond clair, dot pulse brand-b3 pour accent #}
|
||||||
<p class="eyebrow grad-text mb-4 inline-flex items-center gap-2 justify-center px-3 py-1 rounded-full font-mono uppercase tracking-widest text-xs font-bold"
|
<p class="eyebrow text-brand-navy mb-4 inline-flex items-center gap-2 justify-center px-3 py-1 rounded-full font-mono uppercase tracking-widest text-xs font-bold"
|
||||||
style="background:linear-gradient(135deg, rgba(6,182,212,0.10), rgba(192,38,211,0.10)); border:1px solid rgba(192,38,211,0.20);">
|
style="background:linear-gradient(135deg, rgba(6,182,212,0.10), rgba(192,38,211,0.10)); border:1px solid rgba(192,38,211,0.20);">
|
||||||
<span class="dictia-dot-pulse inline-block w-1.5 h-1.5 rounded-full bg-brand-b3" aria-hidden="true"></span>
|
<span class="dictia-dot-pulse inline-block w-1.5 h-1.5 rounded-full bg-brand-b3" aria-hidden="true"></span>
|
||||||
COMMENT ÇA MARCHE
|
COMMENT ÇA MARCHE
|
||||||
@@ -477,8 +480,8 @@
|
|||||||
{# Stats row #}
|
{# Stats row #}
|
||||||
<div class="flex flex-wrap items-center justify-center gap-x-8 gap-y-3 text-brand-navy">
|
<div class="flex flex-wrap items-center justify-center gap-x-8 gap-y-3 text-brand-navy">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<span class="text-2xl font-black grad-text">6</span>
|
<span class="text-2xl font-black grad-text">4</span>
|
||||||
<span class="text-xs font-medium text-brand-navy/60 uppercase tracking-wider">modules</span>
|
<span class="text-xs font-medium text-brand-navy/60 uppercase tracking-wider">catégories · 6 modules</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-px h-6 bg-brand-navy/15 hidden sm:block" aria-hidden="true"></div>
|
<div class="w-px h-6 bg-brand-navy/15 hidden sm:block" aria-hidden="true"></div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
@@ -952,27 +955,279 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
{# Mode 7 : Recording live (waveform live + minuteur + 3 boutons mic/syst/combiné) — Catégorie Capture #}
|
||||||
|
<template x-if="displayMode === 7">
|
||||||
|
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="recModeData()" x-init="init()">
|
||||||
|
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(6,182,212,0.06);border-bottom:1px solid rgba(6,182,212,0.12);">
|
||||||
|
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(6,182,212,0.90);">ENREGISTREMENT LIVE</span>
|
||||||
|
<div class="flex items-center gap-1">
|
||||||
|
<span class="dictia-rec-dot block rounded-full" style="width:5px;height:5px;background:#ef4444;"></span>
|
||||||
|
<span class="font-mono text-[9px] font-bold tracking-widest" style="color:rgba(239,68,68,0.90);">REC</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{# Minuteur central + waveform live #}
|
||||||
|
<div class="flex flex-col items-center justify-center gap-2 flex-1 px-3">
|
||||||
|
<p class="font-mono font-black text-2xl text-white tabular-nums leading-none" x-text="time"></p>
|
||||||
|
<p class="font-mono text-[9px] uppercase tracking-widest" style="color:rgba(6,182,212,0.65);">Microphone + Système</p>
|
||||||
|
{# Waveform 24 barres dynamiques (pattern live aléatoire CSS-only) #}
|
||||||
|
<div class="flex items-center justify-center gap-0.5 mt-1" style="height:36px;" aria-hidden="true">
|
||||||
|
<template x-for="(h, bi) in BARS" :key="bi">
|
||||||
|
<div class="dictia-wave-bar bg-brand-b2"
|
||||||
|
:style="`width:2px;height:${h}px;animation-delay:${bi * 30}ms;`"></div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
{# Estimation taille fichier #}
|
||||||
|
<p class="font-mono text-[9px] mt-2" style="color:rgba(6,182,212,0.55);">~<span x-text="size"></span> Mo · auto-save 5 s</p>
|
||||||
|
</div>
|
||||||
|
{# 3 boutons sources (rouge mic / bleu système / violet combiné) — selon manuel #}
|
||||||
|
<div class="flex items-center justify-center gap-2 px-3 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(8,12,24,0.5);border-top:1px solid rgba(255,255,255,0.06);">
|
||||||
|
<span class="rounded-full flex items-center justify-center" style="width:18px;height:18px;background:rgba(239,68,68,0.30);border:1px solid rgba(239,68,68,0.55);">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="#ef4444" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:9px;height:9px;" aria-hidden="true">
|
||||||
|
<rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="rounded-full flex items-center justify-center" style="width:18px;height:18px;background:rgba(37,99,235,0.30);border:1px solid rgba(37,99,235,0.55);">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="#2563eb" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:9px;height:9px;" aria-hidden="true">
|
||||||
|
<rect x="3" y="4" width="18" height="14" rx="2"/><line x1="9" y1="22" x2="15" y2="22"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<span class="rounded-full flex items-center justify-center dictia-spring" style="width:22px;height:22px;background:rgba(192,38,211,0.30);border:1.5px solid rgba(192,38,211,0.65);">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="#c026d3" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;" aria-hidden="true">
|
||||||
|
<path d="M12 2v8M5 7l7 7 7-7"/>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
{# Mode 8 : Recherche sémantique (search bar + résultats highlight) — Catégorie Capture #}
|
||||||
|
<template x-if="displayMode === 8">
|
||||||
|
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="searchModeData()" x-init="init()">
|
||||||
|
<div class="flex items-center justify-center gap-2 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(6,182,212,0.06);border-bottom:1px solid rgba(6,182,212,0.12);">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;color:rgba(6,182,212,0.90);" aria-hidden="true">
|
||||||
|
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||||
|
</svg>
|
||||||
|
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(6,182,212,0.90);">RECHERCHE IA SÉMANTIQUE</span>
|
||||||
|
</div>
|
||||||
|
{# Search bar simulée (typing query) #}
|
||||||
|
<div class="px-3 py-2 flex-shrink-0">
|
||||||
|
<div class="rounded flex items-center gap-2 px-2 py-1.5"
|
||||||
|
style="background:rgba(255,255,255,0.06);border:1px solid rgba(6,182,212,0.30);">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:9px;height:9px;color:rgba(6,182,212,0.70);" aria-hidden="true">
|
||||||
|
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>
|
||||||
|
</svg>
|
||||||
|
<p class="font-mono text-[10px] flex-1 truncate" style="color:rgba(255,255,255,0.85);">
|
||||||
|
<span x-text="query"></span><span class="dictia-blink ml-px inline-block align-middle bg-brand-b2" style="width:2px;height:9px;"> </span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{# Résultats : 3 transcriptions matchées avec phrase highlight #}
|
||||||
|
<div class="flex-1 dictia-mode-scroll dictia-fade-bottom flex flex-col gap-1.5 px-3 pb-2">
|
||||||
|
<template x-for="(r, ri) in results" :key="ri">
|
||||||
|
<div class="rounded dictia-fade-y"
|
||||||
|
:style="`background-color:${r.c}10;border:1px solid ${r.c}30;padding:6px 8px;animation-delay:${ri * 280 + 600}ms;`">
|
||||||
|
<div class="flex items-center gap-1.5 mb-1">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" :style="`color:${r.c};width:9px;height:9px;`" aria-hidden="true">
|
||||||
|
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/>
|
||||||
|
</svg>
|
||||||
|
<span class="font-mono text-[9px] font-bold" :style="`color:${r.c};`" x-text="r.title"></span>
|
||||||
|
<span class="font-mono text-[9px] ml-auto" style="color:rgba(255,255,255,0.40);" x-text="r.score + ' %'"></span>
|
||||||
|
</div>
|
||||||
|
<p class="font-mono text-[9px] leading-snug" style="color:rgba(255,255,255,0.75);">
|
||||||
|
… <span :style="`background:${r.c}28;color:${r.c};padding:0 2px;border-radius:2px;`" x-text="r.match"></span> …
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
{# Mode 9 : Résumé + extraction d'événements (panel structuré décisions/actions/ICS) — Catégorie IA #}
|
||||||
|
<template x-if="displayMode === 9">
|
||||||
|
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="summaryModeData()" x-init="init()">
|
||||||
|
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(37,99,235,0.06);border-bottom:1px solid rgba(37,99,235,0.12);">
|
||||||
|
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(37,99,235,0.90);">RÉSUMÉ EXÉCUTIF</span>
|
||||||
|
<span class="font-mono text-[9px] text-brand-b1/60">Mistral Nemo 12B</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 dictia-mode-scroll dictia-fade-bottom flex flex-col gap-2 px-3 py-2">
|
||||||
|
{# Décisions #}
|
||||||
|
<div class="dictia-fade-y" style="animation-delay:200ms;">
|
||||||
|
<p class="font-mono text-[9px] font-bold uppercase tracking-wider mb-1" style="color:rgba(37,99,235,0.85);">Décisions</p>
|
||||||
|
<template x-for="(d, di) in decisions.slice(0, dShown)" :key="di">
|
||||||
|
<div class="flex items-start gap-1.5 mb-0.5 dictia-fade-x">
|
||||||
|
<span class="text-brand-b1 leading-none mt-0.5" aria-hidden="true">•</span>
|
||||||
|
<p class="text-[10px] font-mono leading-snug" style="color:rgba(255,255,255,0.85);" x-text="d"></p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
{# Actions à suivre #}
|
||||||
|
<div class="dictia-fade-y" style="animation-delay:900ms;" x-show="dShown >= decisions.length">
|
||||||
|
<p class="font-mono text-[9px] font-bold uppercase tracking-wider mb-1" style="color:rgba(192,38,211,0.85);">Actions à suivre</p>
|
||||||
|
<template x-for="(a, ai) in actions.slice(0, aShown)" :key="ai">
|
||||||
|
<div class="flex items-start gap-1.5 mb-0.5 dictia-fade-x">
|
||||||
|
<span class="rounded flex items-center justify-center mt-0.5 flex-shrink-0" style="width:9px;height:9px;background:rgba(192,38,211,0.30);border:1px solid rgba(192,38,211,0.55);" aria-hidden="true">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="#c026d3" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="width:5px;height:5px;"><polyline points="20 6 9 17 4 12"/></svg>
|
||||||
|
</span>
|
||||||
|
<p class="text-[10px] font-mono leading-snug" style="color:rgba(255,255,255,0.85);" x-text="a"></p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
{# Événement ICS détecté (extraction calendrier) #}
|
||||||
|
<div class="dictia-fade-y rounded mt-1 flex-shrink-0" x-show="aShown >= actions.length"
|
||||||
|
style="background:rgba(6,182,212,0.10);border:1px solid rgba(6,182,212,0.30);padding:5px 8px;animation-delay:1800ms;">
|
||||||
|
<div class="flex items-center gap-1.5 mb-0.5">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="#06b6d4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:9px;height:9px;" aria-hidden="true">
|
||||||
|
<rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/>
|
||||||
|
</svg>
|
||||||
|
<span class="font-mono text-[9px] font-bold tracking-widest" style="color:rgba(6,182,212,0.95);">ÉVÉNEMENT DÉTECTÉ → ICS</span>
|
||||||
|
</div>
|
||||||
|
<p class="font-mono text-[9px]" style="color:rgba(255,255,255,0.70);">Comité de suivi · 21 janv. 14h00</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
{# Mode 10 : Intégrations animées (logos hub Word, Outlook, Teams, Notion, Obsidian, Zapier...) — Catégorie Distribution #}
|
||||||
|
<template x-if="displayMode === 10">
|
||||||
|
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="integModeData()" x-init="init()">
|
||||||
|
<div class="flex items-center justify-center gap-2 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(192,38,211,0.06);border-bottom:1px solid rgba(192,38,211,0.12);">
|
||||||
|
<span class="dictia-dot-pulse block rounded-full" style="width:5px;height:5px;background:#c026d3;"></span>
|
||||||
|
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(192,38,211,0.90);">INTÉGRATIONS HUB</span>
|
||||||
|
</div>
|
||||||
|
{# Hub central + 8 logos en orbite (grille pour mobile, ring pour desktop) #}
|
||||||
|
<div class="flex-1 relative flex items-center justify-center px-3 py-2 overflow-hidden">
|
||||||
|
{# Cercle central DictIA #}
|
||||||
|
<div class="rounded-full flex items-center justify-center relative z-10"
|
||||||
|
style="width:42px;height:42px;background:linear-gradient(135deg,rgba(6,182,212,0.25),rgba(192,38,211,0.25));border:1.5px solid rgba(192,38,211,0.50);box-shadow:0 0 20px rgba(192,38,211,0.30);">
|
||||||
|
<span class="font-black text-[10px] grad-text">Dict</span>
|
||||||
|
</div>
|
||||||
|
{# 8 logos animés en cercle autour du hub (positions absolutes) #}
|
||||||
|
<template x-for="(l, li) in LOGOS" :key="l.name">
|
||||||
|
<div class="absolute dictia-spring rounded flex items-center justify-center"
|
||||||
|
:style="`width:28px;height:28px;left:${l.x}%;top:${l.y}%;transform:translate(-50%,-50%);background:${l.c}18;border:1px solid ${l.c}45;animation-delay:${li * 90 + 200}ms;`">
|
||||||
|
<span class="font-mono font-black text-[9px]" :style="`color:${l.c};`" x-text="l.short"></span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
{# Lignes de connexion SVG hub→logos #}
|
||||||
|
<svg class="absolute inset-0 pointer-events-none" width="100%" height="100%" aria-hidden="true" style="opacity:0.20;">
|
||||||
|
<line x1="50%" y1="50%" x2="20%" y2="20%" stroke="#c026d3" stroke-width="0.5" stroke-dasharray="2 2"/>
|
||||||
|
<line x1="50%" y1="50%" x2="80%" y2="20%" stroke="#06b6d4" stroke-width="0.5" stroke-dasharray="2 2"/>
|
||||||
|
<line x1="50%" y1="50%" x2="20%" y2="80%" stroke="#2563eb" stroke-width="0.5" stroke-dasharray="2 2"/>
|
||||||
|
<line x1="50%" y1="50%" x2="80%" y2="80%" stroke="#c026d3" stroke-width="0.5" stroke-dasharray="2 2"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center gap-3 px-3 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(192,38,211,0.04);border-top:1px solid rgba(192,38,211,0.10);">
|
||||||
|
<span class="font-mono text-[9px] tracking-wide" style="color:rgba(192,38,211,0.75);">REST · Webhook · OAuth2</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
{# Mode 11 : Audit trail (event log horodaté qui a fait quoi) — Catégorie Gouvernance #}
|
||||||
|
<template x-if="displayMode === 11">
|
||||||
|
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="auditModeData()" x-init="init()">
|
||||||
|
<div class="flex items-center justify-between px-3 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(37,99,235,0.06);border-bottom:1px solid rgba(37,99,235,0.12);">
|
||||||
|
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(37,99,235,0.90);">AUDIT TRAIL</span>
|
||||||
|
<span class="font-mono text-[9px]" style="color:rgba(37,99,235,0.55);">Loi 25 art. 8</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 dictia-mode-scroll dictia-fade-bottom flex flex-col gap-1 px-2 py-2">
|
||||||
|
<template x-for="(e, ei) in events.slice(0, shown)" :key="ei">
|
||||||
|
<div class="rounded flex items-start gap-1.5 dictia-fade-x"
|
||||||
|
:style="`background-color:${e.c}0C;border-left:2px solid ${e.c};padding:3px 6px;animation-delay:${ei * 100}ms;`">
|
||||||
|
<span class="font-mono text-[9px] font-bold mt-px" :style="`color:${e.c};`" x-text="e.t"></span>
|
||||||
|
<span class="rounded font-mono text-[8px] font-bold tracking-widest mt-px"
|
||||||
|
style="padding:0 4px;"
|
||||||
|
:style="`background:${e.c}22;color:${e.c};`" x-text="e.lvl"></span>
|
||||||
|
<p class="font-mono text-[9px] leading-snug flex-1" style="color:rgba(255,255,255,0.78);" x-text="e.msg"></p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center gap-2 px-3 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(16,185,129,0.06);border-top:1px solid rgba(16,185,129,0.14);">
|
||||||
|
<span class="dictia-dot-pulse block rounded-full" style="width:5px;height:5px;background:#10b981;"></span>
|
||||||
|
<span class="font-mono text-[9px] font-bold tracking-widest" style="color:rgba(16,185,129,0.90);">CONSENTEMENT TRACÉ · IMMUTABLE</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
{# Mode 12 : Conformité Loi 25 + 9 ordres pros (badges défilants + EFVP CAI) — Catégorie Gouvernance #}
|
||||||
|
<template x-if="displayMode === 12">
|
||||||
|
<div class="dictia-mode-fade w-full h-full flex flex-col overflow-hidden" x-data="loi25ModeData()" x-init="init()">
|
||||||
|
<div class="flex items-center justify-center gap-2 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(192,38,211,0.06);border-bottom:1px solid rgba(192,38,211,0.12);">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;color:rgba(192,38,211,0.90);" aria-hidden="true">
|
||||||
|
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/>
|
||||||
|
</svg>
|
||||||
|
<span class="font-mono text-[10px] font-bold tracking-widest" style="color:rgba(192,38,211,0.90);">CONFORMITÉ QUÉBEC</span>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 dictia-mode-scroll dictia-fade-bottom flex flex-col gap-1.5 px-3 py-2">
|
||||||
|
{# Badges Loi 25 / Loi 96 / EFVP CAI / Cadre IA MCN #}
|
||||||
|
<div class="flex flex-wrap gap-1.5">
|
||||||
|
<template x-for="(b, bi) in BADGES" :key="b.t">
|
||||||
|
<span class="rounded font-mono dictia-spring inline-flex items-center gap-1"
|
||||||
|
:style="`background:${b.c}18;border:1px solid ${b.c}45;color:${b.c};padding:2px 6px;animation-delay:${bi * 110}ms;font-size:9px;font-weight:bold;`">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" style="width:7px;height:7px;" aria-hidden="true">
|
||||||
|
<polyline points="20 6 9 17 4 12"/>
|
||||||
|
</svg>
|
||||||
|
<span x-text="b.t"></span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
{# 9 ordres pros mappés #}
|
||||||
|
<p class="font-mono text-[9px] font-bold uppercase tracking-wider mt-1" style="color:rgba(192,38,211,0.75);">9 ordres professionnels</p>
|
||||||
|
<div class="grid grid-cols-3 gap-1">
|
||||||
|
<template x-for="(o, oi) in ORDERS" :key="o">
|
||||||
|
<span class="rounded font-mono dictia-fade-y text-center text-[9px]"
|
||||||
|
style="padding:2px;background:rgba(192,38,211,0.10);border:1px solid rgba(192,38,211,0.25);color:rgba(255,255,255,0.85);"
|
||||||
|
:style="`animation-delay:${oi * 60 + 800}ms;`"
|
||||||
|
x-text="o"></span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-center gap-2 px-3 py-2 flex-shrink-0"
|
||||||
|
style="background:rgba(16,185,129,0.06);border-top:1px solid rgba(16,185,129,0.14);">
|
||||||
|
<span class="font-mono text-[9px] font-bold tracking-widest" style="color:rgba(16,185,129,0.90);">EFVP CAI · CADRE IA MCN</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# BOTTOM : 6 feature icons + label + tab indicator + Auto pill — hauteur fixe 90px (cadre stable) #}
|
{# BOTTOM : 4 catégories tabs (Capture / IA / Distribution / Gouvernance) + sub-mode dots + Auto pill — hauteur fixe 90px #}
|
||||||
<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 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));">
|
||||||
|
{# Sub-mode dots indicator (shows position within active category) #}
|
||||||
|
<div class="flex items-center gap-1" style="height:6px;" aria-hidden="true">
|
||||||
|
<template x-for="(sm, di) in CATEGORIES[activeCategory].submodes" :key="di">
|
||||||
|
<span class="block rounded-full"
|
||||||
|
:style="`width:${sm === selectedFeature ? '10px' : '4px'};height:4px;background:${sm === selectedFeature ? CATEGORIES[activeCategory].color : 'rgba(255,255,255,0.20)'};transition:width 0.2s, background 0.2s;`"></span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# 4 catégories tabs #}
|
||||||
<div class="flex items-end gap-1">
|
<div class="flex items-end gap-1">
|
||||||
<template x-for="i in [1,2,3,4,5,6]" :key="i">
|
<template x-for="(cat, ci) in CATEGORIES" :key="ci">
|
||||||
<button type="button" @click="handleManualSelect(i)"
|
<button type="button" @click="handleCategorySelect(ci)"
|
||||||
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-cat-btn 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="width:54px;height:42px;border:none;padding:4px 0 6px;background:transparent;"
|
||||||
:aria-pressed="selectedFeature === i ? 'true' : 'false'"
|
:aria-pressed="activeCategory === ci ? 'true' : 'false'"
|
||||||
:aria-label="`Voir : ${FEATURES[i].title}`">
|
:aria-label="`Catégorie : ${cat.title}`">
|
||||||
<span :style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.30)'}; 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;`">
|
<span :style="`color: ${activeCategory === ci ? cat.color : 'rgba(255,255,255,0.30)'}; transition: color 0.2s, transform 0.15s, filter 0.2s; filter: ${activeCategory === ci ? 'drop-shadow(0 0 6px ' + cat.color + 'CC)' : 'none'}; transform: scale(${activeCategory === ci ? 1.15 : 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:16px;height:16px;" 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:18px;height:18px;" aria-hidden="true" x-html="cat.iconPath"></svg>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-[9px] font-mono font-semibold mt-0.5 leading-none uppercase tracking-wider"
|
<span class="text-[9px] font-mono font-bold mt-0.5 leading-none uppercase tracking-wider"
|
||||||
:style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(255,255,255,0.40)'};`"
|
:style="`color: ${activeCategory === ci ? cat.color : 'rgba(255,255,255,0.40)'};`"
|
||||||
x-text="featureShortLabel(i)"></span>
|
x-text="cat.short"></span>
|
||||||
{# Tab indicator bottom border 2px (style tab bar) #}
|
{# Tab indicator bottom border 2px #}
|
||||||
<span x-show="selectedFeature === i" class="absolute" aria-hidden="true"
|
<span x-show="activeCategory === ci" 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: 6px; right: 6px; height: 2px; background: ${cat.color}; border-radius: 1px; box-shadow: 0 0 8px ${cat.color};`"></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -1031,19 +1286,19 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Mobile : pills horizontales scrollables #}
|
{# Mobile : pills catégories horizontales scrollables #}
|
||||||
<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]" :key="i">
|
<template x-for="(cat, ci) in CATEGORIES" :key="ci">
|
||||||
<button type="button" @click="handleManualSelect(i)"
|
<button type="button" @click="handleCategorySelect(ci)"
|
||||||
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 ${activeCategory === ci ? cat.color + '70' : 'rgba(0,0,0,0.10)'}; background-color: ${activeCategory === ci ? cat.color + '18' : 'rgba(0,0,0,0.04)'}; outline: none;`">
|
||||||
<span :style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(0,0,0,0.40)'}; transition: color 0.2s;`">
|
<span :style="`color: ${activeCategory === ci ? cat.color : 'rgba(0,0,0,0.40)'}; transition: color 0.2s;`">
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:11px;height:11px;" 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:12px;height:12px;" aria-hidden="true" x-html="cat.iconPath"></svg>
|
||||||
</span>
|
</span>
|
||||||
<span class="text-[11px] font-medium whitespace-nowrap"
|
<span class="text-[11px] font-bold whitespace-nowrap"
|
||||||
:style="`color: ${selectedFeature === i ? FEATURES[i].color : 'rgba(0,0,0,0.50)'};`"
|
:style="`color: ${activeCategory === ci ? cat.color : 'rgba(0,0,0,0.50)'};`"
|
||||||
x-text="FEATURES[i].title"></span>
|
x-text="cat.title"></span>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -1057,7 +1312,7 @@
|
|||||||
<div class="mb-1 flex items-center gap-2">
|
<div class="mb-1 flex items-center gap-2">
|
||||||
<span class="block w-1 h-6 rounded-full" style="background:linear-gradient(180deg,#06b6d4,#c026d3);" aria-hidden="true"></span>
|
<span class="block w-1 h-6 rounded-full" style="background:linear-gradient(180deg,#06b6d4,#c026d3);" aria-hidden="true"></span>
|
||||||
<div>
|
<div>
|
||||||
<p class="text-xs font-mono font-bold uppercase tracking-widest text-brand-b3">Fonctions clés</p>
|
<p class="text-xs font-mono font-bold uppercase tracking-widest text-brand-b3">4 catégories · 12 sous-modes</p>
|
||||||
<p class="font-bold text-lg leading-snug text-brand-navy">Le moteur IA local</p>
|
<p class="font-bold text-lg leading-snug text-brand-navy">Le moteur IA local</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1155,20 +1410,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Feature grid 3 cols × 6 buttons — fond TOUJOURS dark navy (actif/inactif) pour garantir contraste WCAG AA blanc-sur-dark sur section claire #}
|
{# Category grid 2×2 — 4 catégories avec sous-modes affichés dessous (architecture lisible) #}
|
||||||
<div class="grid grid-cols-3 gap-2">
|
<div class="grid grid-cols-2 gap-2">
|
||||||
<template x-for="i in [1,2,3,4,5,6]" :key="i">
|
<template x-for="(cat, ci) in CATEGORIES" :key="ci">
|
||||||
<button type="button" @click="handleManualSelect(i)"
|
<button type="button" @click="handleCategorySelect(ci)"
|
||||||
class="flex flex-col items-center gap-1.5 rounded-xl py-3 px-2 outline-none transition-all focus-visible:ring-2 focus-visible:ring-white/40"
|
class="flex flex-col items-start gap-2 rounded-xl py-3 px-3 outline-none transition-all focus-visible:ring-2 focus-visible:ring-white/40 text-left"
|
||||||
: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;`"
|
:style="`border: 1px solid ${activeCategory === ci ? cat.color + '70' : 'rgba(255,255,255,0.10)'}; background-color: ${activeCategory === ci ? 'rgba(8,12,24,0.95)' : 'rgba(8,12,24,0.85)'}; box-shadow: ${activeCategory === ci ? '0 0 16px ' + cat.color + '40, inset 0 0 16px ' + cat.color + '20' : 'none'}; cursor: pointer;`"
|
||||||
:aria-pressed="selectedFeature === i ? 'true' : 'false'"
|
:aria-pressed="activeCategory === ci ? 'true' : 'false'"
|
||||||
:aria-label="`Sélectionner : ${FEATURES[i].title}`">
|
:aria-label="`Catégorie : ${cat.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'}; transform: scale(${selectedFeature === i ? 1.20 : 1}) translateY(${selectedFeature === i ? -1 : 0}px); transition: all 0.2s; display:inline-block;`">
|
<div class="flex items-center gap-2 w-full">
|
||||||
<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>
|
<span :style="`color: ${activeCategory === ci ? cat.color : 'rgba(255,255,255,0.55)'}; filter: ${activeCategory === ci ? 'drop-shadow(0 0 6px ' + cat.color + 'BB)' : 'none'}; transition: all 0.2s; display:inline-block;`">
|
||||||
</span>
|
<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="cat.iconPath"></svg>
|
||||||
<span class="text-[10px] font-semibold text-center leading-tight"
|
</span>
|
||||||
:style="`color: ${selectedFeature === i ? 'rgba(255,255,255,0.98)' : 'rgba(255,255,255,0.70)'}; transition: color 0.2s;`"
|
<span class="text-[11px] font-bold leading-tight flex-1"
|
||||||
x-text="FEATURES[i].title"></span>
|
:style="`color: ${activeCategory === ci ? 'rgba(255,255,255,0.98)' : 'rgba(255,255,255,0.85)'};`"
|
||||||
|
x-text="cat.title"></span>
|
||||||
|
</div>
|
||||||
|
<p class="text-[10px] font-mono leading-snug w-full"
|
||||||
|
:style="`color: ${activeCategory === ci ? cat.color + 'EE' : 'rgba(255,255,255,0.55)'};`"
|
||||||
|
x-text="cat.subtitle"></p>
|
||||||
|
{# Mini sous-modes dots dans la card #}
|
||||||
|
<div class="flex items-center gap-0.5 mt-auto" aria-hidden="true">
|
||||||
|
<template x-for="(sm, si) in cat.submodes" :key="si">
|
||||||
|
<span class="block rounded-full"
|
||||||
|
:style="`width:${activeCategory === ci && sm === selectedFeature ? '8px' : '3px'};height:3px;background:${activeCategory === ci ? (sm === selectedFeature ? cat.color : cat.color + '55') : 'rgba(255,255,255,0.15)'};transition:all 0.2s;`"></span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
@@ -1178,20 +1445,64 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Alpine logic — dictiaDashboard + sub-data functions pour les 6 modes #}
|
{# Alpine logic — dictiaDashboard + 4 catégories × 12 sous-modes (refonte stratégique 2026-04-29) #}
|
||||||
<script>
|
<script>
|
||||||
function dictiaDashboard() {
|
function dictiaDashboard() {
|
||||||
return {
|
return {
|
||||||
|
/* FEATURES indexes (0-12) — même structure que v1 (0=IA, 1-6=existants), nouveaux 7-12 ajoutés */
|
||||||
FEATURES: [
|
FEATURES: [
|
||||||
{ idx: 0, title: 'IA intégrée', subtitle: "Résumé, actions, Q&R", color: '#c026d3', badge: 'Mistral 7B' }, /* brand-b3 */
|
{ idx: 0, title: 'IA intégrée', subtitle: "Résumé, actions, Q&R", color: '#c026d3', badge: 'Mistral 7B' }, /* brand-b3 */
|
||||||
{ idx: 1, title: 'Transcription', subtitle: 'Parole → texte en temps réel', color: '#06b6d4', badge: 'Whisper AI' }, /* brand-b2 */
|
{ idx: 1, title: 'Transcription', subtitle: 'Parole → texte en temps réel', color: '#06b6d4', badge: 'Whisper AI' }, /* brand-b2 */
|
||||||
{ idx: 2, title: 'Diarisation', subtitle: 'Identification des locuteurs', color: '#2563eb', badge: null }, /* brand-b1 */
|
{ idx: 2, title: 'Diarisation', subtitle: 'Identification des locuteurs', color: '#2563eb', badge: null }, /* brand-b1 */
|
||||||
{ idx: 3, title: '99+ langues', subtitle: 'Détection automatique', color: '#06b6d4', badge: null }, /* brand-b2 */
|
{ idx: 3, title: '99+ langues', subtitle: 'Détection automatique', color: '#06b6d4', badge: null }, /* brand-b2 */
|
||||||
{ idx: 4, title: 'Exports', subtitle: 'DOCX, SRT, JSON, PDF', color: '#2563eb', badge: null }, /* brand-b1 */
|
{ idx: 4, title: 'Exports', subtitle: 'DOCX, SRT, JSON, PDF', color: '#2563eb', badge: null }, /* brand-b1 */
|
||||||
{ idx: 5, title: 'Utilisateurs illimités', subtitle: 'Toute votre équipe', color: '#c026d3', badge: 'Illimité' }, /* brand-b3 */
|
{ idx: 5, title: 'Utilisateurs illimités', subtitle: 'Toute votre équipe', color: '#c026d3', badge: 'Illimité' }, /* brand-b3 */
|
||||||
{ idx: 6, title: 'Partage & Classement', subtitle: 'Dossiers, tags, recherche', color: '#06b6d4', badge: null } /* brand-b2 */
|
{ idx: 6, title: 'Partage & Classement', subtitle: 'Dossiers, tags, recherche', color: '#06b6d4', badge: null }, /* brand-b2 */
|
||||||
|
/* === Nouveaux modes 2026-04-29 === */
|
||||||
|
{ idx: 7, title: 'Enregistrement live', subtitle: 'Mic + système + minuteur', color: '#06b6d4', badge: 'PWA' }, /* brand-b2 */
|
||||||
|
{ idx: 8, title: 'Recherche IA', subtitle: 'Sémantique sur toute la bibliothèque', color: '#06b6d4', badge: 'RAG' }, /* brand-b2 */
|
||||||
|
{ idx: 9, title: 'Résumé + actions', subtitle: 'Décisions, ICS, événements', color: '#2563eb', badge: 'Nemo 12B' }, /* brand-b1 */
|
||||||
|
{ idx: 10, title: 'Intégrations', subtitle: 'Word, Notion, Teams, Zapier...', color: '#c026d3', badge: '8 hubs' }, /* brand-b3 */
|
||||||
|
{ 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 */
|
||||||
|
],
|
||||||
|
/* CATEGORIES : 4 regroupements logiques. Chaque catégorie a ses sous-modes (ids des FEATURES). */
|
||||||
|
CATEGORIES: [
|
||||||
|
{
|
||||||
|
title: 'Capture',
|
||||||
|
short: 'Capture',
|
||||||
|
subtitle: 'Audio in : upload, recording, recherche',
|
||||||
|
color: '#06b6d4', /* brand-b2 cyan */
|
||||||
|
submodes: [1, 7, 8], /* Transcription (upload), Recording live, Recherche sémantique */
|
||||||
|
iconPath: '<rect x="9" y="2" width="6" height="12" rx="3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Transformation IA',
|
||||||
|
short: 'IA',
|
||||||
|
subtitle: 'Diarisation, langues, résumés, Q&R',
|
||||||
|
color: '#2563eb', /* brand-b1 blue */
|
||||||
|
submodes: [2, 3, 9, 0], /* Diarisation, Langues, Résumé+actions, Chat IA */
|
||||||
|
iconPath: '<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"/>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Distribution',
|
||||||
|
short: 'Distrib',
|
||||||
|
subtitle: 'Exports, intégrations, partage',
|
||||||
|
color: '#c026d3', /* brand-b3 fuchsia */
|
||||||
|
submodes: [4, 10, 6, 5], /* Exports, Intégrations, Partage, Users */
|
||||||
|
iconPath: '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/>'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Gouvernance',
|
||||||
|
short: 'Gouv',
|
||||||
|
subtitle: 'Audit, Loi 25, AGPL, ordres pros',
|
||||||
|
color: '#2563eb', /* brand-b1 blue (sobre, institutionnel) */
|
||||||
|
submodes: [11, 12], /* Audit trail, Conformité Loi 25 */
|
||||||
|
iconPath: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/>'
|
||||||
|
}
|
||||||
],
|
],
|
||||||
selectedFeature: 1,
|
selectedFeature: 1,
|
||||||
|
activeCategory: 0,
|
||||||
isManual: false,
|
isManual: false,
|
||||||
autoCycleTimer: null,
|
autoCycleTimer: null,
|
||||||
manualResetTimer: null,
|
manualResetTimer: null,
|
||||||
@@ -1201,14 +1512,42 @@
|
|||||||
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 dans les sous-modes de la catégorie active toutes les 1100ms.
|
||||||
|
Quand on atteint le dernier sous-mode → switch catégorie suivante (boucle 0→1→2→3→0). */
|
||||||
startAutoCycle() {
|
startAutoCycle() {
|
||||||
if (this.autoCycleTimer) clearInterval(this.autoCycleTimer);
|
if (this.autoCycleTimer) clearInterval(this.autoCycleTimer);
|
||||||
this.autoCycleTimer = setInterval(() => {
|
this.autoCycleTimer = setInterval(() => {
|
||||||
this.selectedFeature = this.selectedFeature < this.FEATURES.length - 1 ? this.selectedFeature + 1 : 1;
|
const cat = this.CATEGORIES[this.activeCategory];
|
||||||
}, 900);
|
const currentIdx = cat.submodes.indexOf(this.selectedFeature);
|
||||||
|
if (currentIdx < cat.submodes.length - 1) {
|
||||||
|
/* Avance dans la catégorie courante */
|
||||||
|
this.selectedFeature = cat.submodes[currentIdx + 1];
|
||||||
|
} else {
|
||||||
|
/* Fin de catégorie → passe à la suivante */
|
||||||
|
this.activeCategory = (this.activeCategory + 1) % this.CATEGORIES.length;
|
||||||
|
this.selectedFeature = this.CATEGORIES[this.activeCategory].submodes[0];
|
||||||
|
}
|
||||||
|
}, 1100);
|
||||||
},
|
},
|
||||||
|
/* Click manuel sur un sous-mode (compatibilité 6 boutons originaux) */
|
||||||
handleManualSelect(i) {
|
handleManualSelect(i) {
|
||||||
this.selectedFeature = i;
|
this.selectedFeature = i;
|
||||||
|
/* Trouve la catégorie qui contient ce sous-mode */
|
||||||
|
for (let ci = 0; ci < this.CATEGORIES.length; ci++) {
|
||||||
|
if (this.CATEGORIES[ci].submodes.includes(i)) { this.activeCategory = ci; break; }
|
||||||
|
}
|
||||||
|
this.isManual = true;
|
||||||
|
if (this.autoCycleTimer) { clearInterval(this.autoCycleTimer); this.autoCycleTimer = null; }
|
||||||
|
if (this.manualResetTimer) clearTimeout(this.manualResetTimer);
|
||||||
|
this.manualResetTimer = setTimeout(() => {
|
||||||
|
this.isManual = false;
|
||||||
|
this.startAutoCycle();
|
||||||
|
}, 4500);
|
||||||
|
},
|
||||||
|
/* Click manuel sur une catégorie : sélectionne son premier sous-mode */
|
||||||
|
handleCategorySelect(ci) {
|
||||||
|
this.activeCategory = ci;
|
||||||
|
this.selectedFeature = this.CATEGORIES[ci].submodes[0];
|
||||||
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);
|
||||||
@@ -1225,12 +1564,19 @@
|
|||||||
3: '<circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>',
|
3: '<circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>',
|
||||||
4: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><polyline points="9 15 12 18 15 15"/>',
|
4: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="12" y1="18" x2="12" y2="12"/><polyline points="9 15 12 18 15 15"/>',
|
||||||
5: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/>',
|
5: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="20" y1="8" x2="20" y2="14"/><line x1="23" y1="11" x2="17" y2="11"/>',
|
||||||
6: '<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>'
|
6: '<path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>',
|
||||||
|
7: '<circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/>',
|
||||||
|
8: '<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.65" y2="16.65"/>',
|
||||||
|
9: '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="9" y1="13" x2="15" y2="13"/><line x1="9" y1="17" x2="15" y2="17"/>',
|
||||||
|
10: '<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/>',
|
||||||
|
11: '<path d="M12 2v6"/><path d="M12 22v-6"/><path d="M4.93 4.93l4.24 4.24"/><path d="M14.83 14.83l4.24 4.24"/><circle cx="12" cy="12" r="4"/>',
|
||||||
|
12: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="M9 12l2 2 4-4"/>'
|
||||||
};
|
};
|
||||||
return paths[i] || '';
|
return paths[i] || '';
|
||||||
},
|
},
|
||||||
featureShortLabel(i) {
|
featureShortLabel(i) {
|
||||||
const labels = { 1: 'Trans', 2: 'Diari', 3: 'Lang', 4: 'Exp', 5: 'Users', 6: 'Part' };
|
const labels = { 1: 'Trans', 2: 'Diari', 3: 'Lang', 4: 'Exp', 5: 'Users', 6: 'Part',
|
||||||
|
7: 'Rec', 8: 'Search', 9: 'Résu', 10: 'Hub', 11: 'Audit', 12: 'Conf' };
|
||||||
return labels[i] || '';
|
return labels[i] || '';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1424,6 +1770,167 @@
|
|||||||
_cleanup() { this._timers.forEach(t => { clearInterval(t); clearTimeout(t); }); this._timers = []; }
|
_cleanup() { this._timers.forEach(t => { clearInterval(t); clearTimeout(t); }); this._timers = []; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === MODES AJOUTÉS 2026-04-29 === */
|
||||||
|
|
||||||
|
/* Mode 7 — Recording live : minuteur incrémentiel + waveform random pattern */
|
||||||
|
function recModeData() {
|
||||||
|
return {
|
||||||
|
sec: 0, BARS: [], _iv: null, _ivBars: null,
|
||||||
|
get time() {
|
||||||
|
const m = String(Math.floor(this.sec / 60)).padStart(2, '0');
|
||||||
|
const s = String(this.sec % 60).padStart(2, '0');
|
||||||
|
return `${m}:${s}`;
|
||||||
|
},
|
||||||
|
get size() { return Math.round(this.sec * 0.16 * 10) / 10; },
|
||||||
|
init() {
|
||||||
|
if (this._iv) clearInterval(this._iv);
|
||||||
|
if (this._ivBars) clearInterval(this._ivBars);
|
||||||
|
this.sec = 0;
|
||||||
|
/* 24 bars hauteur initiale random 6-26px */
|
||||||
|
this.BARS = Array.from({ length: 24 }, () => Math.floor(Math.random() * 20) + 6);
|
||||||
|
this._iv = setInterval(() => {
|
||||||
|
this.sec++;
|
||||||
|
if (this.sec >= 99) { clearInterval(this._iv); setTimeout(() => this.init(), 800); }
|
||||||
|
}, 1000);
|
||||||
|
/* Update bars heights chaque 250ms pour effet "live" */
|
||||||
|
this._ivBars = setInterval(() => {
|
||||||
|
this.BARS = this.BARS.map(() => Math.floor(Math.random() * 20) + 6);
|
||||||
|
}, 250);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mode 8 — Recherche sémantique : query typed + résultats matched */
|
||||||
|
function searchModeData() {
|
||||||
|
return {
|
||||||
|
QUERY: 'préoccupations budgétaires',
|
||||||
|
query: '', results: [], _timers: [],
|
||||||
|
RESULTS_DATA: [
|
||||||
|
{ title: 'CR-Réunion-Jan14', match: 'contraintes budgétaires Q4', score: 94, c: '#06b6d4' },
|
||||||
|
{ title: 'Comité-Direction', match: 'compressions à anticiper', score: 87, c: '#2563eb' },
|
||||||
|
{ title: 'Entretien-Sophie', match: 'inquiétudes financières', score: 82, c: '#c026d3' }
|
||||||
|
],
|
||||||
|
init() {
|
||||||
|
this._cleanup();
|
||||||
|
this.query = ''; this.results = [];
|
||||||
|
let i = 0;
|
||||||
|
const ivType = setInterval(() => {
|
||||||
|
i++; this.query = this.QUERY.slice(0, i);
|
||||||
|
if (i >= this.QUERY.length) {
|
||||||
|
clearInterval(ivType);
|
||||||
|
const t1 = setTimeout(() => { this.results = this.RESULTS_DATA; }, 500);
|
||||||
|
this._timers.push(t1);
|
||||||
|
const t2 = setTimeout(() => this.init(), 5500);
|
||||||
|
this._timers.push(t2);
|
||||||
|
}
|
||||||
|
}, 60);
|
||||||
|
this._timers.push(ivType);
|
||||||
|
},
|
||||||
|
_cleanup() { this._timers.forEach(t => { clearInterval(t); clearTimeout(t); }); this._timers = []; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mode 9 — Résumé + extraction d'événements : décisions/actions stagger */
|
||||||
|
function summaryModeData() {
|
||||||
|
return {
|
||||||
|
decisions: [
|
||||||
|
'Approuvé budget Q4 à l\'unanimité',
|
||||||
|
'Reporter démo à février',
|
||||||
|
'Validation finale 25 janv.'
|
||||||
|
],
|
||||||
|
actions: [
|
||||||
|
'Marc : envoyer CR avant 17h',
|
||||||
|
'Sophie : contacter client vendredi',
|
||||||
|
'Julie : réviser budget avec Luc'
|
||||||
|
],
|
||||||
|
dShown: 0, aShown: 0, _timers: [],
|
||||||
|
init() {
|
||||||
|
this._cleanup();
|
||||||
|
this.dShown = 0; this.aShown = 0;
|
||||||
|
let di = 0;
|
||||||
|
const ivD = setInterval(() => {
|
||||||
|
di++; this.dShown = di;
|
||||||
|
if (di >= this.decisions.length) {
|
||||||
|
clearInterval(ivD);
|
||||||
|
let ai = 0;
|
||||||
|
const t1 = setTimeout(() => {
|
||||||
|
const ivA = setInterval(() => {
|
||||||
|
ai++; this.aShown = ai;
|
||||||
|
if (ai >= this.actions.length) {
|
||||||
|
clearInterval(ivA);
|
||||||
|
const t2 = setTimeout(() => this.init(), 3500);
|
||||||
|
this._timers.push(t2);
|
||||||
|
}
|
||||||
|
}, 600);
|
||||||
|
this._timers.push(ivA);
|
||||||
|
}, 700);
|
||||||
|
this._timers.push(t1);
|
||||||
|
}
|
||||||
|
}, 600);
|
||||||
|
this._timers.push(ivD);
|
||||||
|
},
|
||||||
|
_cleanup() { this._timers.forEach(t => { clearInterval(t); clearTimeout(t); }); this._timers = []; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mode 10 — Intégrations Hub : 8 logos en orbite */
|
||||||
|
function integModeData() {
|
||||||
|
return {
|
||||||
|
LOGOS: [
|
||||||
|
{ name: 'Word', short: 'W', c: '#2563eb', x: 20, y: 30 },
|
||||||
|
{ name: 'Outlook', short: 'O', c: '#06b6d4', x: 80, y: 30 },
|
||||||
|
{ name: 'Teams', short: 'T', c: '#c026d3', x: 15, y: 55 },
|
||||||
|
{ name: 'Notion', short: 'N', c: '#2563eb', x: 85, y: 55 },
|
||||||
|
{ name: 'Obsidian', short: 'Ob', c: '#c026d3', x: 25, y: 80 },
|
||||||
|
{ name: 'Zapier', short: 'Z', c: '#06b6d4', x: 75, y: 80 },
|
||||||
|
{ name: 'Make', short: 'Mk', c: '#2563eb', x: 50, y: 18 },
|
||||||
|
{ name: 'n8n', short: 'n8', c: '#06b6d4', x: 50, y: 88 }
|
||||||
|
],
|
||||||
|
init() {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mode 11 — Audit trail : event log staggered */
|
||||||
|
function auditModeData() {
|
||||||
|
return {
|
||||||
|
events: [
|
||||||
|
{ t: '09:01', lvl: 'INFO', c: '#06b6d4', msg: 'Sophie a uploadé reunion-jan14.mp3' },
|
||||||
|
{ t: '09:02', lvl: 'AUTH', c: '#2563eb', msg: 'Consentement explicite enregistré' },
|
||||||
|
{ t: '09:04', lvl: 'PROC', c: '#c026d3', msg: 'WhisperX terminé · diarisation 3 loc.' },
|
||||||
|
{ t: '09:08', lvl: 'READ', c: '#06b6d4', msg: 'Marc a écouté le segment 02:14-04:30' },
|
||||||
|
{ t: '09:12', lvl: 'EXP', c: '#2563eb', msg: 'Export DOCX par Julie · IP 198.x.x.42' },
|
||||||
|
{ t: '09:15', lvl: 'SHARE', c: '#c026d3', msg: 'Partage interne accordé : Luc (édition)' }
|
||||||
|
],
|
||||||
|
shown: 0, _iv: null,
|
||||||
|
init() {
|
||||||
|
if (this._iv) clearInterval(this._iv);
|
||||||
|
this.shown = 0;
|
||||||
|
this._iv = setInterval(() => {
|
||||||
|
if (this.shown >= this.events.length) {
|
||||||
|
clearInterval(this._iv);
|
||||||
|
setTimeout(() => this.init(), 2400);
|
||||||
|
} else this.shown++;
|
||||||
|
}, 700);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mode 12 — Conformité Loi 25 + 9 ordres pros */
|
||||||
|
function loi25ModeData() {
|
||||||
|
return {
|
||||||
|
BADGES: [
|
||||||
|
{ t: 'Loi 25', c: '#06b6d4' },
|
||||||
|
{ t: 'Loi 96', c: '#2563eb' },
|
||||||
|
{ t: 'EFVP CAI', c: '#c026d3' },
|
||||||
|
{ t: 'Cadre IA MCN', c: '#06b6d4' },
|
||||||
|
{ t: 'AGPL v3', c: '#2563eb' },
|
||||||
|
{ t: '0 Cloud Act US', c: '#c026d3' }
|
||||||
|
],
|
||||||
|
ORDERS: ['Barreau', 'CNQ', 'CPA', 'ChAD', 'OACIQ', 'CMQ', 'OIIQ', 'OPQ', 'OEQ'],
|
||||||
|
init() {}
|
||||||
|
};
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
@@ -190,8 +190,32 @@ def test_fonctionnalites_how_it_works_reactor_section():
|
|||||||
# Auto-cycle + manual select logic
|
# Auto-cycle + manual select logic
|
||||||
assert 'startAutoCycle' in body, "Missing auto-cycle function"
|
assert 'startAutoCycle' in body, "Missing auto-cycle function"
|
||||||
assert 'handleManualSelect' in body, "Missing manual select handler"
|
assert 'handleManualSelect' in body, "Missing manual select handler"
|
||||||
assert '900' in body, "Missing 900ms cycle interval"
|
assert '1100' in body, "Missing 1100ms sub-mode cycle interval (refonte 2026-04-29)"
|
||||||
assert '4500' in body, "Missing 4500ms manual reset timer"
|
assert '4500' in body, "Missing 4500ms manual reset timer"
|
||||||
|
# Refonte 2026-04-29 : 4 catégories × 12 sous-modes
|
||||||
|
assert 'CATEGORIES' in body, "Missing CATEGORIES array (refonte 4 catégories)"
|
||||||
|
assert 'activeCategory' in body, "Missing activeCategory state"
|
||||||
|
assert 'handleCategorySelect' in body, "Missing category click handler"
|
||||||
|
for cat_title in ['Capture', 'Transformation IA', 'Distribution', 'Gouvernance']:
|
||||||
|
assert cat_title in body, f"Missing category : {cat_title}"
|
||||||
|
# Nouveaux modes 7-12
|
||||||
|
assert 'recModeData' in body, "Missing Mode 7 (Recording live)"
|
||||||
|
assert 'searchModeData' in body, "Missing Mode 8 (Recherche sémantique)"
|
||||||
|
assert 'summaryModeData' in body, "Missing Mode 9 (Résumé + actions)"
|
||||||
|
assert 'integModeData' in body, "Missing Mode 10 (Intégrations Hub)"
|
||||||
|
assert 'auditModeData' in body, "Missing Mode 11 (Audit trail)"
|
||||||
|
assert 'loi25ModeData' in body, "Missing Mode 12 (Conformité Loi 25)"
|
||||||
|
# Mode panels content keywords
|
||||||
|
assert 'ENREGISTREMENT LIVE' in body, "Missing recording header"
|
||||||
|
assert 'RECHERCHE IA SÉMANTIQUE' in body, "Missing semantic search header"
|
||||||
|
assert 'RÉSUMÉ EXÉCUTIF' in body, "Missing summary header"
|
||||||
|
assert 'INTÉGRATIONS HUB' in body, "Missing integrations hub header"
|
||||||
|
assert 'AUDIT TRAIL' in body, "Missing audit trail header"
|
||||||
|
assert 'CONFORMITÉ QUÉBEC' in body, "Missing conformity header"
|
||||||
|
assert 'ÉVÉNEMENT DÉTECTÉ' in body, "Missing ICS event detection"
|
||||||
|
assert '9 ordres professionnels' in body, "Missing 9 ordres listing"
|
||||||
|
for ordre in ['Barreau', 'CNQ', 'CPA', 'OIIQ']:
|
||||||
|
assert ordre in body, f"Missing ordre pro: {ordre}"
|
||||||
# Auto / Manual status text
|
# Auto / Manual status text
|
||||||
assert 'Auto' in body, "Missing Auto status indicator"
|
assert 'Auto' in body, "Missing Auto status indicator"
|
||||||
assert 'Auto reprend bientôt' in body, "Missing manual mode hint"
|
assert 'Auto reprend bientôt' in body, "Missing manual mode hint"
|
||||||
@@ -249,8 +273,9 @@ def test_fonctionnalites_how_it_works_reactor_section():
|
|||||||
assert 'dictia-bg-orbs' in body, "Missing floating orbs"
|
assert 'dictia-bg-orbs' in body, "Missing floating orbs"
|
||||||
assert 'dictia-connecting-line' in body, "Missing connecting line phone↔IA"
|
assert 'dictia-connecting-line' in body, "Missing connecting line phone↔IA"
|
||||||
|
|
||||||
# Stats row in section header
|
# Stats row in section header (refonte 2026-04-29 : 4 catégories · 6 modules)
|
||||||
assert 'modules' in body, "Missing stats row 'modules'"
|
assert 'modules' in body, "Missing stats row 'modules'"
|
||||||
|
assert 'catégories' in body, "Missing stats row 'catégories' (refonte)"
|
||||||
assert 'cloud' in body, "Missing stats row 'cloud'"
|
assert 'cloud' in body, "Missing stats row 'cloud'"
|
||||||
assert 'Voir une démo' in body, "Missing demo CTA link"
|
assert 'Voir une démo' in body, "Missing demo CTA link"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user