feat: introduce AI insight block and detailed explanation modal, including visual styling adjustments.

This commit is contained in:
Michael committed 2026-02-23 16:52:05 +01:00
1 parent a6c295413b
commit 15478fd07d
3 files changed
+71 -83

No files matched your search

@@ -35,108 +35,101 @@ export function AiExplanationModal({
const handleClose = () => { const handleClose = () => {
setIsVisible(false); setIsVisible(false);
setTimeout(onClose, 250); setTimeout(onClose, 200);
}; };
if (!isOpen && !isVisible) return null; if (!isOpen && !isVisible) return null;
if (!mounted) return null; if (!mounted) return null;
const getGammeColor = (g?: string | null) => { const getGammeStyles = (g?: string | null) => {
switch (g) { switch (g) {
case "A": return "text-emerald-500 border-emerald-500/50 bg-emerald-500/10"; case "A": return "text-[var(--accent-success)] border-[var(--accent-success-bg)] bg-[var(--accent-success-bg)]";
case "C": return "text-amber-500 border-amber-500/50 bg-amber-500/10"; case "C": return "text-[var(--accent-warning)] border-[var(--accent-warning-bg)] bg-[var(--accent-warning-bg)]";
case "Z": return "text-rose-500 border-rose-500/50 bg-rose-500/10"; case "Z": return "text-[var(--accent-error)] border-[var(--accent-error-bg)] bg-[var(--accent-error-bg)]";
default: return "text-slate-500 border-slate-500/50 bg-slate-500/10"; default: return "text-[var(--text-muted)] border-[var(--border)] bg-[var(--bg-elevated)]";
} }
}; };
const modalContent = ( const modalContent = (
<div <div
className={`fixed inset-0 z-[9999] flex items-center justify-center p-4 transition-all duration-250 ${isVisible ? "opacity-100 backdrop-blur-md" : "opacity-0 backdrop-blur-0"}`} className={`fixed inset-0 z-[9999] flex items-center justify-center p-6 transition-all duration-200 ${isVisible ? "opacity-100 backdrop-blur-sm" : "opacity-0 backdrop-blur-0"}`}
> >
{/* Backdrop */} {/* Backdrop - consistent with Apple overlay */}
<div <div
className="fixed inset-0" className="fixed inset-0 bg-black/40"
style={{ background: "rgba(0,0,0,0.85)" }}
onClick={handleClose} onClick={handleClose}
/> />
{/* Panel */} {/* Panel - Using var(--bg-surface) and strict spacing */}
<div <div
className={`relative w-full max-w-lg rounded-3xl overflow-hidden shadow-[0_0_50px_rgba(0,0,0,0.8)] transition-all duration-250 ${isVisible ? "scale-100 translate-y-0" : "scale-95 translate-y-8"}`} className={`relative w-full max-w-xl rounded-2xl overflow-hidden shadow-2xl border border-[var(--border-strong)] transition-all duration-200 ${isVisible ? "scale-100 translate-y-0" : "scale-[0.98] translate-y-4"}`}
style={{ style={{
background: "#111111", // Opaque black background background: "var(--bg-surface)",
border: "1px solid var(--border-strong)",
}} }}
> >
{/* Header with Mary Icon */} {/* Header - Clean & Monochromatic */}
<div className="p-6 pb-0 flex items-start justify-between"> <div className="px-6 py-5 border-b border-[var(--border)] flex items-center justify-between">
<div className="flex gap-4"> <div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-2xl bg-indigo-500/10 border border-indigo-500/20 flex items-center justify-center shrink-0"> <div className="w-8 h-8 rounded-lg bg-[var(--bg-elevated)] border border-[var(--border)] flex items-center justify-center shrink-0">
<Bot className="w-6 h-6 text-indigo-500" /> <Bot className="w-5 h-5 text-[var(--text-secondary)]" />
</div> </div>
<div> <div>
<h2 className="text-xl font-black tracking-tight" style={{ color: "var(--text-primary)" }}> <h2 className="text-sm font-bold tracking-tight text-[var(--text-primary)]">
Analyse de Mary 🧙 Analyse Décisionnelle IA
</h2> </h2>
<p className="text-xs font-bold opacity-60 uppercase tracking-widest mt-0.5"> <p className="text-[11px] font-medium text-[var(--text-muted)] truncate max-w-[300px]">
{productName} <span className="opacity-40">({productCode})</span> {productName} <span className="opacity-60 tabular-numbers">({productCode})</span>
</p> </p>
</div> </div>
</div> </div>
<button <button
onClick={handleClose} onClick={handleClose}
className="p-2 rounded-xl hover:bg-white/5 text-[var(--text-muted)] transition-colors" className="p-1.5 rounded-md hover:bg-[var(--bg-elevated)] text-[var(--text-muted)] transition-colors"
> >
<X className="w-5 h-5" /> <X className="w-4 h-4" />
</button> </button>
</div> </div>
{/* Content */} {/* Body */}
<div className="p-6"> <div className="p-6 space-y-6">
<div className="space-y-6"> {/* Recommendation Row */}
{/* Recommandation Badge */} <div className="flex items-center justify-between px-1">
<div className="flex items-center gap-2"> <span className="text-[11px] font-bold uppercase tracking-wider text-[var(--text-muted)]">Arbitrage suggéré</span>
<span className="text-[10px] font-black uppercase tracking-widest opacity-40">Verdict :</span> <div className={`px-2.5 py-1 rounded-md border text-[11px] font-bold flex items-center gap-1.5 ${getGammeStyles(recommandation)}`}>
<div className={`px-3 py-1 rounded-full border text-xs font-black flex items-center gap-1.5 ${getGammeColor(recommandation)}`}> {recommandation === "A" && <Sparkles className="w-3 h-3" />}
{recommandation === "A" && <Sparkles className="w-3 h-3" />} {recommandation === "C" && <TrendingUp className="w-3 h-3" />}
{recommandation === "C" && <TrendingUp className="w-3 h-3" />} {recommandation === "Z" && <ShieldAlert className="w-3 h-3" />}
{recommandation === "Z" && <ShieldAlert className="w-3 h-3" />} {recommandation ? `GAMME ${recommandation}` : "NON DÉFINI"}
{recommandation ? `GAMME ${recommandation}` : "AUCUN"} </div>
</div>
{/* Explanation Box - Monospace for figures */}
<div
className="p-5 rounded-xl border border-[var(--border)] bg-[var(--bg-base)]/50 relative overflow-hidden"
>
<div className="flex gap-4">
<History className="w-4 h-4 mt-0.5 text-[var(--accent)] shrink-0 opacity-80" />
<div className="text-[13px] leading-relaxed text-[var(--text-secondary)] font-medium">
<span className="font-mono-nums leading-relaxed whitespace-pre-wrap">
{explanation}
</span>
</div> </div>
</div> </div>
</div>
{/* Explanation Text */} {/* Meta Info */}
<div <div className="flex items-center justify-center gap-4 py-2 border-t border-[var(--border)]">
className="text-[14px] leading-relaxed p-5 rounded-2xl border" <div className="flex items-center gap-2 text-[10px] text-[var(--text-muted)] font-medium italic">
style={{ <span>Basé sur CA, Marge & Volumes réels</span>
background: "var(--bg-elevated)",
borderColor: "var(--border)",
color: "var(--text-secondary)"
}}
>
<div className="flex gap-3">
<History className="w-4 h-4 mt-1 text-indigo-400 shrink-0" />
<div className="whitespace-pre-wrap font-medium italic italic-custom">
"{explanation}"
</div>
</div>
</div>
<div className="flex flex-col gap-2">
<div className="h-px w-full bg-gradient-to-r from-transparent via-[var(--border)] to-transparent" />
<p className="text-[10px] text-center opacity-40 font-bold italic">
Analyse générée en temps réel selon les indicateurs CA, Marge et Régularité.
</p>
</div> </div>
</div> </div>
</div> </div>
{/* Footers / Action */} {/* Footer Action */}
<div className="p-6 pt-0"> <div className="px-6 py-4 bg-[var(--bg-elevated)]/50 border-t border-[var(--border)] flex justify-end">
<button <button
onClick={handleClose} onClick={handleClose}
className="w-full py-3 rounded-2xl font-black text-sm transition-all hover:scale-[1.02] active:scale-95 text-[#f5f5f7] bg-[#1c1c1e] hover:bg-[#2c2c2e] border border-white/10" className="apple-btn-secondary px-6"
> >
Fermer l'Analyse Fermer l'Analyse
</button> </button>
@@ -144,9 +137,9 @@ export function AiExplanationModal({
</div> </div>
<style jsx>{` <style jsx>{`
.italic-custom { .font-mono-nums {
font-family: var(--font-serif, serif); font-family: inherit;
font-style: italic; font-variant-numeric: tabular-nums;
} }
`}</style> `}</style>
</div> </div>
@@ -103,28 +103,19 @@ export function AiInsightBlock({ row }: AiInsightBlockProps) {
return ( return (
<> <>
<div className="flex flex-col gap-1 group/insight" title="Cliquer pour re-analyser"> <div className="flex flex-col gap-1 group/insight" title="Cliquer pour re-analyser">
<div className="flex items-start gap-1.5 min-w-0"> <div className="flex-1 min-w-0 pr-1 group">
<Bot <p
className="w-3.5 h-3.5 mt-0.5 text-indigo-600 dark:text-indigo-400 shrink-0 cursor-pointer hover:scale-110 transition-transform" className="text-[11px] leading-snug font-medium text-[var(--text-secondary)] line-clamp-3 cursor-pointer hover:text-[var(--text-primary)] transition-colors relative"
onClick={(e) => { onClick={(e) => {
e.stopPropagation(); e.stopPropagation();
handleAnalyze(); setIsModalOpen(true);
}} }}
/> >
<div className="flex flex-col min-w-0 flex-1"> {insight.insight}
<div <span className="inline-flex ml-1 opacity-0 group-hover:opacity-100 transition-opacity">
className="flex items-end gap-1 group/text cursor-pointer" <Maximize2 className="w-2.5 h-2.5 text-[var(--accent)]" />
onClick={(e) => { </span>
e.stopPropagation(); </p>
setIsModalOpen(true);
}}
>
<span className={cn("text-[11px] italic line-clamp-2 leading-[1.3] flex-1")} style={{ color: "var(--text-secondary)" }}>
{insight?.insight}
</span>
<Maximize2 className="w-2.5 h-2.5 mb-0.5 opacity-0 group-hover/insight:opacity-40 hover:!opacity-100 transition-opacity shrink-0 text-indigo-500" />
</div>
</div>
</div> </div>
{insight?.isDuplicate && ( {insight?.isDuplicate && (
<span className="inline-flex items-center gap-1 text-[10px] font-bold text-amber-700 dark:text-amber-400 bg-amber-100 dark:bg-amber-900/30 border border-amber-300 dark:border-amber-700 px-1.5 py-0.5 rounded-full w-fit"> <span className="inline-flex items-center gap-1 text-[10px] font-bold text-amber-700 dark:text-amber-400 bg-amber-100 dark:bg-amber-900/30 border border-amber-300 dark:border-amber-700 px-1.5 py-0.5 rounded-full w-fit">
+4
View File
@@ -21,6 +21,7 @@ Activation et initialisation de l'Agent Master BMAD.
- [x] Étape 13 : Implémentation du modal pour les explications IA détaillées - [x] Étape 13 : Implémentation du modal pour les explications IA détaillées
- [x] Étape 14 : Correction du design et du positionnement du modal (z-index/Portal) - [x] Étape 14 : Correction du design et du positionnement du modal (z-index/Portal)
- [x] Étape 15 : Polissage final et validation Michael - [x] Étape 15 : Polissage final et validation Michael
- [x] Étape 16 : Correction graphique selon la charte CollectFlow (Slate-950/Apple)
# Progress Log # Progress Log
@@ -48,3 +49,6 @@ Activation et initialisation de l'Agent Master BMAD.
- Correction du modal : utilisation de `createPortal` et `z-index: 9999` pour passer au-dessus de tout. - Correction du modal : utilisation de `createPortal` et `z-index: 9999` pour passer au-dessus de tout.
- Opacité du fond renforcée (0.85). - Opacité du fond renforcée (0.85).
- Validation Michael reçue (LGTM). - Validation Michael reçue (LGTM).
- Refonte visuelle complète du modal : passage aux variables CSS de `globals.css`.
- Suppression des bordures blanches et du fond noir pur pour le thème `Slate-950`.
- Intégration de `tabular-nums` pour les indicateurs financiers.