- Remplacement intégral index.css par palette Charcoal (DESIGN_SYSTEM.md v2.0)
- Dark = thème par défaut, .light = override via @custom-variant light
- Sidebar navy #0C1528 permanent (identique dark+light)
- Script anti-FOUC inline dans index.html
- Layout : radial-gradient sur <main>, sidebar 230px, max-w-[1100px]
- Renommage tokens Boréal→Charcoal sur ~45 composants
- Inversion dark: → baseline + light: sur primitives shadcn
- Fix logo blanc forcé dans sidebar
- ADR 006 mis à jour
Typecheck: OK · Tests: 122/122 ✅
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
94 lines
2.7 KiB
TypeScript
94 lines
2.7 KiB
TypeScript
/**
|
|
* Modal — suggestions d'idées DeepSeek (tâche G5).
|
|
*
|
|
* Présentationnel pur. La fermeture déclenche `onClose` qui doit appeler
|
|
* `reset()` du hook useIdees côté parent pour vider le cache de mutation.
|
|
*
|
|
* Règle H : aucune logique métier. Règle L : tokens Direction H uniquement.
|
|
*/
|
|
|
|
import { Loader2, Lightbulb } from 'lucide-react'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/shared/components/ui/dialog'
|
|
import type { ApiError } from '@/shared/types/api'
|
|
|
|
function mapIdeesError(err: ApiError | null): string | null {
|
|
if (!err) return null
|
|
switch (err.code) {
|
|
case 'AUTH_REQUIRED':
|
|
return 'Votre session a expiré. Reconnectez-vous.'
|
|
case 'VALIDATION_ERROR':
|
|
case 'INVALID_BODY':
|
|
return 'Écrivez au moins 30 mots avant de demander des suggestions.'
|
|
default:
|
|
return 'Suggestions indisponibles. Réessayez dans quelques instants.'
|
|
}
|
|
}
|
|
|
|
interface Props {
|
|
idees: string[] | null
|
|
isLoading: boolean
|
|
error: ApiError | null
|
|
isOpen: boolean
|
|
onClose: () => void
|
|
}
|
|
|
|
export function IdeesSuggestions({ idees, isLoading, error, isOpen, onClose }: Props) {
|
|
const message = mapIdeesError(error)
|
|
|
|
return (
|
|
<Dialog
|
|
open={isOpen}
|
|
onOpenChange={(open) => {
|
|
if (!open) onClose()
|
|
}}
|
|
>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle className="flex items-center gap-2 text-ink-primary">
|
|
<Lightbulb className="size-5 text-brand-text" aria-hidden="true" />
|
|
Suggestions d'idées
|
|
</DialogTitle>
|
|
<DialogDescription>
|
|
Pour prolonger votre rédaction, inspirez-vous de ces pistes.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
{isLoading && (
|
|
<div className="flex items-center gap-2 text-sm text-ink-secondary" aria-busy="true">
|
|
<Loader2 className="size-4 animate-spin text-brand-text" aria-hidden="true" />
|
|
Génération des idées…
|
|
</div>
|
|
)}
|
|
|
|
{!isLoading && message && (
|
|
<div
|
|
role="alert"
|
|
className="rounded-md border border-danger/40 bg-danger-soft px-3 py-2 text-sm text-danger"
|
|
>
|
|
{message}
|
|
</div>
|
|
)}
|
|
|
|
{!isLoading && !message && idees && idees.length > 0 && (
|
|
<ul className="space-y-2 text-sm text-ink-primary">
|
|
{idees.map((idee, i) => (
|
|
<li key={i} className="flex gap-2">
|
|
<span
|
|
className="mt-[0.4em] size-1.5 shrink-0 rounded-full bg-brand"
|
|
aria-hidden="true"
|
|
/>
|
|
<span>{idee}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|