/** * FlashcardSheet — génère et sauvegarde des flashcards depuis une note */ import { useState } from 'react' import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, ScrollView } from 'react-native' import { GraduationCap, Check, RefreshCw } from 'lucide-react-native' import { BottomSheet } from './BottomSheet' import { C } from '@/lib/theme' import { apiFetch } from '@/lib/api' import { ENDPOINTS } from '@/lib/config' import { useRouter } from 'expo-router' const STYLES = [ { key: 'qa', label: 'Q&A', desc: 'Questions / Réponses' }, { key: 'concept', label: 'Concept', desc: 'Terme / Définition' }, { key: 'cloze', label: 'Cloze', desc: 'Texte à trous' }, ] as const const COUNTS = [5, 10, 15, 20] interface Props { visible: boolean onClose: () => void noteId: string noteTitle: string } export function FlashcardSheet({ visible, onClose, noteId, noteTitle }: Props) { const router = useRouter() const [style, setStyle] = useState<'qa' | 'concept' | 'cloze'>('qa') const [count, setCount] = useState(10) const [loading, setLoading] = useState(false) const [result, setResult] = useState<{ deckId: string; count: number } | null>(null) const [error, setError] = useState(null) const generate = async () => { setLoading(true) setError(null) setResult(null) try { const res = await apiFetch(ENDPOINTS.flashcardGenerate, { method: 'POST', body: JSON.stringify({ noteId, style, count }), }) const data = await res.json() if (!res.ok) throw new Error(data.error ?? `Erreur ${res.status}`) setResult({ deckId: data.deckId, count: data.count }) } catch (e: any) { setError(e.message ?? 'Erreur de génération') } finally { setLoading(false) } } const goRevise = () => { onClose() router.push({ pathname: '/(tabs)/revision' }) } const reset = () => { setResult(null); setError(null) } return ( Générer des flashcards 📝 {noteTitle} {!result && !loading && ( {/* Style */} Type de cartes {STYLES.map((st) => ( setStyle(st.key)} activeOpacity={0.8} > {st.label} {st.desc} ))} {/* Nombre */} Nombre de cartes {COUNTS.map((n) => ( setCount(n)} activeOpacity={0.8} > {n} ))} {error && {error}} Générer {count} cartes )} {loading && ( Génération en cours… )} {result && ( {result.count} cartes créées ! Votre paquet est prêt pour la révision. Réviser maintenant Regénérer )} ) } const s = StyleSheet.create({ header: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: 4 }, title: { fontSize: 17, fontWeight: '700', color: C.ink }, noteTitle: { fontSize: 13, color: C.concrete, marginBottom: 20 }, sectionLabel: { fontSize: 11, fontWeight: '700', color: C.concrete, letterSpacing: 0.8, textTransform: 'uppercase', marginBottom: 10 }, pills: { flexDirection: 'row', gap: 8, marginBottom: 20 }, pill: { flex: 1, padding: 12, borderRadius: 12, borderWidth: 1.5, borderColor: C.border, backgroundColor: C.paper }, pillActive: { borderColor: C.brand, backgroundColor: 'rgba(164,113,72,0.08)' }, pillLabel: { fontSize: 13, fontWeight: '700', color: C.ink, marginBottom: 2 }, pillLabelActive: { color: C.brand }, pillDesc: { fontSize: 10, color: C.concrete }, pillDescActive: { color: C.brand }, countRow: { flexDirection: 'row', gap: 8, marginBottom: 24 }, countBtn: { flex: 1, paddingVertical: 12, borderRadius: 12, borderWidth: 1.5, borderColor: C.border, alignItems: 'center', backgroundColor: C.paper }, countBtnActive: { borderColor: C.brand, backgroundColor: 'rgba(164,113,72,0.08)' }, countTxt: { fontSize: 15, fontWeight: '700', color: C.ink }, countTxtActive: { color: C.brand }, error: { color: '#e11d48', fontSize: 13, marginBottom: 12, textAlign: 'center' }, generateBtn: { backgroundColor: C.brand, borderRadius: 14, paddingVertical: 14, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8 }, generateTxt: { color: '#fff', fontWeight: '700', fontSize: 15 }, center: { alignItems: 'center', justifyContent: 'center', paddingVertical: 40 }, loadingTxt: { marginTop: 16, color: C.concrete, fontSize: 14 }, resultWrap: { alignItems: 'center', paddingVertical: 20 }, checkCircle: { width: 64, height: 64, borderRadius: 32, backgroundColor: '#dcfce7', alignItems: 'center', justifyContent: 'center', marginBottom: 16 }, resultTitle: { fontSize: 20, fontWeight: '800', color: C.ink, marginBottom: 8 }, resultSub: { fontSize: 14, color: C.concrete, marginBottom: 28 }, resultActions: { width: '100%', gap: 10 }, reviseBtn: { backgroundColor: C.brand, borderRadius: 14, paddingVertical: 14, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 8 }, reviseTxt: { color: '#fff', fontWeight: '700', fontSize: 15 }, retryBtn: { borderRadius: 14, paddingVertical: 12, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 6, borderWidth: 1, borderColor: C.border }, retryTxt: { color: C.concrete, fontWeight: '600', fontSize: 13 }, })