'use client' import { useState, useEffect } from 'react' import { X, Sparkles, Globe, Check, Loader2, ExternalLink, Trash2, RefreshCw } from 'lucide-react' import { cn } from '@/lib/utils' import { useLanguage } from '@/lib/i18n' import { toast } from 'sonner' import { motion, AnimatePresence } from 'motion/react' import { useAiConsent } from '@/components/legal/ai-consent-provider' type Step = 'loading' | 'published' | 'idle' | 'analyzing' | 'selection' | 'publishing' | 'done' | 'unpublishing' interface NoteItem { id: string title: string include: boolean reason: string } interface NotebookSiteDialogProps { notebookId: string notebookName: string onClose: () => void } const TEMPLATES = [ { id: 'magazine', label: 'Magazine', desc: 'Sombre, Playfair Display' }, { id: 'brief', label: 'Brief', desc: 'Professionnel, sobre' }, { id: 'essay', label: 'Essai', desc: 'Élégant, ivoire' }, ] as const export function NotebookSiteDialog({ notebookId, notebookName, onClose }: NotebookSiteDialogProps) { const { t, language } = useLanguage() const { requestAiConsent } = useAiConsent() const [step, setStep] = useState('loading') const [existingSlug, setExistingSlug] = useState(null) const [notes, setNotes] = useState([]) const [description, setDescription] = useState('') const [template, setTemplate] = useState<'magazine' | 'brief' | 'essay'>('magazine') const [siteUrl, setSiteUrl] = useState('') // Charger l'état existant au montage useEffect(() => { fetch(`/api/notebooks/${notebookId}/publish`) .then(r => r.json()) .then(data => { if (data.site?.slug) { setExistingSlug(data.site.slug) setSiteUrl(`/c/${data.site.slug}`) setTemplate(data.site.template || 'magazine') setStep('published') } else { setStep('idle') } }) .catch(() => setStep('idle')) }, [notebookId]) const handleAnalyze = async () => { const consented = await requestAiConsent() if (!consented) return setStep('analyzing') try { const res = await fetch('/api/ai/notebook-publish', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ notebookId, language }), }) const data = await res.json() if (!res.ok) { toast.error(data.errorKey === 'ai.featureLocked' ? (t('ai.featureLocked') || 'Plan requis') : (data.error || 'Erreur')) setStep(existingSlug ? 'published' : 'idle') return } const items: NoteItem[] = (data.notes || []).map((n: any) => ({ id: n.noteId, title: data.noteTitles?.[n.noteId] || n.noteId, include: n.include, reason: n.reason, })) setNotes(items) setDescription(data.description || '') setStep('selection') window.dispatchEvent(new Event('ai-usage-changed')) } catch (e: any) { toast.error(e.message || 'Erreur') setStep(existingSlug ? 'published' : 'idle') } } const handlePublish = async () => { const selected = notes.filter(n => n.include).map(n => n.id) if (selected.length === 0) { toast.error('Sélectionne au moins une note'); return } setStep('publishing') try { const res = await fetch(`/api/notebooks/${notebookId}/publish`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ selectedNoteIds: selected, template, description }), }) const data = await res.json() if (!res.ok) { toast.error(data.error || 'Erreur'); setStep('selection'); return } setExistingSlug(data.slug) setSiteUrl(data.url) setStep('done') } catch (e: any) { toast.error(e.message || 'Erreur') setStep('selection') } } const handleUnpublish = async () => { setStep('unpublishing') try { const res = await fetch(`/api/notebooks/${notebookId}/publish`, { method: 'DELETE' }) if (!res.ok) { toast.error('Erreur'); setStep('published'); return } setExistingSlug(null) setSiteUrl('') toast.success('Site dépublié') setStep('idle') } catch { toast.error('Erreur') setStep('published') } } const selectedCount = notes.filter(n => n.include).length return (
{/* Header */}

{t('notebookSite.title') || 'Publier en site web'}

{notebookName}

{/* Body */}
{/* Chargement initial */} {step === 'loading' && ( )} {/* Déjà publié */} {step === 'published' && (
)} {/* Dépublication en cours */} {step === 'unpublishing' && (

Dépublication en cours...

)} {/* Étape 1 : explication */} {step === 'idle' && (

{t('notebookSite.howItWorks') || 'Comment ça marche'}

    {[ t('notebookSite.step1') || "L'IA analyse vos notes et recommande lesquelles publier", t('notebookSite.step2') || "Vous validez ou modifiez la sélection", t('notebookSite.step3') || "Votre site est généré avec navigation et table des matières", ].map((s, i) => (
  1. {i + 1} {s}
  2. ))}

{t('notebookSite.quotaWarning') || "L'analyse consomme 1 crédit IA."}

)} {/* Analyse en cours */} {step === 'analyzing' && (

{t('notebookSite.analyzing') || "L'IA analyse vos notes..."}

)} {/* Sélection */} {step === 'selection' && ( {description && (

{t('notebookSite.siteDescription') || 'Description générée'}

{description}

)}

{selectedCount} / {notes.length} {t('notebookSite.notesSelected') || 'notes sélectionnées'}

{notes.map(note => ( ))}

{t('notebookSite.template') || 'Style visuel'}

{TEMPLATES.map(tpl => ( ))}
)} {/* Publication en cours */} {step === 'publishing' && (

{t('notebookSite.publishing') || 'Génération du site...'}

)} {/* Succès */} {step === 'done' && (

{t('notebookSite.published') || 'Site publié !'}

{selectedCount} notes · accessible publiquement

{t('notebookSite.openSite') || 'Ouvrir le site'}

memento-note.com{siteUrl}

)}
{/* Footer actions */}
{(step === 'selection' || step === 'idle') && existingSlug && ( )}
{step === 'idle' && ( )} {step === 'selection' && ( <> )} {(step === 'done' || step === 'published') && ( )}
) }