import React, { useState, useEffect } from 'react'; import { Lock, PiggyBank, Sparkles, CheckCircle2, XCircle, Plus, Trash2, Mic, Volume2, ShieldCheck, TrendingUp, Gift, RefreshCw, History, Settings, Coins, Image as ImageIcon, Upload, Tv } from 'lucide-react'; const API_URL = import.meta.env.VITE_API_URL || (typeof window !== 'undefined' && window.location.hostname !== 'localhost' ? '/api' : 'http://localhost:4050/api'); const EMOJI_OPTIONS = ['🍕', '🎳', '🌊', '🧗', '🎢', '🎬', '🍦', '🏕️', '🎮', '🚲', '🎨', '⚽']; const PRESET_GALLERY = [ { label: 'Pizza & Cinéma', emoji: '🍕', url: 'https://images.unsplash.com/photo-1513104890138-7c749659a591?auto=format&fit=crop&w=1000&q=80' }, { label: 'Bowling', emoji: '🎳', url: 'https://images.unsplash.com/photo-1538511059256-06459b067f41?auto=format&fit=crop&w=1000&q=80' }, { label: 'Parc Aquatique', emoji: '🌊', url: 'https://images.unsplash.com/photo-1582653291997-079a1c04e5a1?auto=format&fit=crop&w=1000&q=80' }, { label: 'Accrobranche', emoji: '🧗', url: 'https://images.unsplash.com/photo-1542273917363-3b1817f69a2d?auto=format&fit=crop&w=1000&q=80' }, { label: 'Parc Attractions', emoji: '🎢', url: 'https://images.unsplash.com/photo-1513889961551-628c1e5e2ee9?auto=format&fit=crop&w=1000&q=80' }, { label: 'Zoo & Animaux', emoji: '🦁', url: 'https://images.unsplash.com/photo-1534567153574-2b12153a87f0?auto=format&fit=crop&w=1000&q=80' }, { label: 'Soirée Glaces', emoji: '🍦', url: 'https://images.unsplash.com/photo-1497034825429-c343d7c6a68f?auto=format&fit=crop&w=1000&q=80' }, { label: 'Laser Game / Jeux', emoji: '🎮', url: 'https://images.unsplash.com/photo-1511512578047-dfb367046420?auto=format&fit=crop&w=1000&q=80' } ]; export default function App() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState('kids'); // 'kids' | 'parents' | 'alexa' const [toast, setToast] = useState(null); // Quick operation modal for kids/family const [modalOpen, setModalOpen] = useState(false); const [modalConfig, setModalConfig] = useState({ childId: 1, childName: 'Hailey', type: 'DEPOSIT_LOCKED', shells: 4, activityId: '' }); // New activity form in Parent tab (with image support for Alexa) const [newActivity, setNewActivity] = useState({ title: '', description: '', emoji: '🎢', image_url: PRESET_GALLERY[4].url, cost_shells: 30 }); const [uploadingImage, setUploadingImage] = useState(false); const [editingActivity, setEditingActivity] = useState(null); // Bank interest settings form const [interestInput, setInterestInput] = useState(20); const [unlockAfterInterest, setUnlockAfterInterest] = useState(false); const [approvalShells, setApprovalShells] = useState({}); // Modal for Parents Direct Bank Management & Real/Virtual Transfer const [editBankModal, setEditBankModal] = useState({ open: false, child: null, activeMode: 'transfer', // 'transfer' | 'manual' transferType: 'REAL_TO_AVAILABLE', shells: 5, reason: '', manualAvailable: 0, manualLocked: 0, manualEuros: 0 }); // Modal for Parents Direct Contribution to an Activity const [directContributeModal, setDirectContributeModal] = useState({ open: false, activity: null, childId: 1, shells: 5, sourceType: 'PHYSICAL' // 'PHYSICAL' | 'VIRTUAL' }); // Alexa simulator state (Voice + Echo Show visual card) const [alexaResponse, setAlexaResponse] = useState( 'Cliquez sur une commande vocale ci-dessous pour entendre Alexa répondre et voir l\'écran Echo Show !' ); const [alexaCard, setAlexaCard] = useState(null); const [alexaCardsList, setAlexaCardsList] = useState([]); const [alexaLoading, setAlexaLoading] = useState(false); const [customAlexa, setCustomAlexa] = useState({ intent: 'ContributeActivityIntent', ChildName: 'Hailey', ShellCount: 6, AccountType: 'bloqué', ActivityName: 'Bowling en famille' }); function showToast(msg, isError = false) { setToast({ msg, isError }); setTimeout(() => setToast(null), 4500); } async function fetchDashboard() { try { const res = await fetch(`${API_URL}/dashboard`); const json = await res.json(); setData(json); if (json.settings) { setInterestInput(json.settings.interest_rate_percent); } if (!alexaCard && json.activities?.length > 0) { const first = json.activities[0]; setAlexaCard({ title: `${first.emoji} ${first.title}`, subtitle: `${first.current_shells} / ${first.cost_shells} coquillages (${first.progress_percent}%)`, text: first.description, imageUrl: first.image_url, progressPercent: first.progress_percent }); } } catch (err) { showToast('Impossible de contacter le serveur Backend (port 4050).', true); } finally { setLoading(false); } } useEffect(() => { fetchDashboard(); }, []); function speakText(text) { if ('speechSynthesis' in window) { window.speechSynthesis.cancel(); const utterance = new SpeechSynthesisUtterance(text); utterance.lang = 'fr-FR'; utterance.rate = 1.02; window.speechSynthesis.speak(utterance); } } // Upload a local image file from the user's computer/phone async function handleFileUpload(e, isEditing = false) { const file = e.target.files?.[0]; if (!file) return; setUploadingImage(true); const reader = new FileReader(); reader.onload = async () => { try { const res = await fetch(`${API_URL}/upload-image`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ dataUrl: reader.result, filename: file.name }) }); const json = await res.json(); if (!res.ok) { showToast(json.error || 'Erreur upload image', true); return; } if (isEditing && editingActivity) { setEditingActivity({ ...editingActivity, image_url: json.image_url }); } else { setNewActivity((prev) => ({ ...prev, image_url: json.image_url })); } showToast('Image téléchargée et prête pour le Skill Alexa ! 🖼️'); } catch (err) { showToast('Erreur lors de l\'envoi de l\'image', true); } finally { setUploadingImage(false); } }; reader.readAsDataURL(file); } // Create a request (Deposit, Lock, Contribute to Activity, Convert to Euros) async function handleSubmitRequest(e) { e.preventDefault(); try { const res = await fetch(`${API_URL}/requests`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ child_id: modalConfig.childId, type: modalConfig.type, shells: Number(modalConfig.shells), activity_id: modalConfig.activityId ? Number(modalConfig.activityId) : null, source: 'WEB' }) }); const json = await res.json(); if (!res.ok) { showToast(json.error || 'Erreur lors de la demande', true); return; } showToast(`Demande enregistrée ! Les parents peuvent maintenant valider la réception des coquillages.`); setModalOpen(false); fetchDashboard(); } catch (err) { showToast(err.message, true); } } // Approve or Reject a pending physical request (with parent-entered shells count and optional extra metadata) async function handleResolveRequest(reqId, action, defaultShells = 5, extraBody = {}) { try { const shellsCount = Number(approvalShells[reqId] ?? (defaultShells > 0 ? defaultShells : 5)); const res = await fetch(`${API_URL}/requests/${reqId}/${action}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ shells: shellsCount, ...extraBody }) }); const json = await res.json(); if (!res.ok) { showToast(json.error || 'Erreur lors de la validation', true); return; } showToast(json.message); fetchDashboard(); } catch (err) { showToast(err.message, true); } } // Direct Parent attribution of shells to an activity async function handleDirectContribute(e) { e.preventDefault(); if (!directContributeModal.activity) return; try { const res = await fetch(`${API_URL}/activities/${directContributeModal.activity.id}/direct-contribute`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ child_id: Number(directContributeModal.childId), shells: Number(directContributeModal.shells), source_type: directContributeModal.sourceType }) }); const json = await res.json(); if (!res.ok) { showToast(json.error || 'Erreur lors de l\'attribution', true); return; } showToast(json.message); setDirectContributeModal({ ...directContributeModal, open: false }); fetchDashboard(); } catch (err) { showToast(err.message, true); } } // Direct Parent edit or transfer of child's bank async function handleSaveBankEdit(e) { e.preventDefault(); const { child, activeMode, transferType, shells, reason, manualAvailable, manualLocked, manualEuros } = editBankModal; if (!child) return; try { if (activeMode === 'manual') { const res = await fetch(`${API_URL}/children/${child.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ available_shells: Number(manualAvailable), locked_shells: Number(manualLocked), euros_balance: Number(manualEuros) }) }); const json = await res.json(); if (!res.ok) { showToast(json.error || 'Erreur lors de la modification', true); return; } showToast(json.message); } else { const res = await fetch(`${API_URL}/children/${child.id}/transfer`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type: transferType, shells: Number(shells), reason }) }); const json = await res.json(); if (!res.ok) { showToast(json.error || 'Erreur lors du transfert', true); return; } showToast(json.message); } setEditBankModal({ ...editBankModal, open: false }); fetchDashboard(); } catch (err) { showToast(err.message, true); } } // Apply monthly interest engine async function handleApplyMonthlyInterest() { try { const res = await fetch(`${API_URL}/bank/apply-interest`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ unlock_after_interest: unlockAfterInterest }) }); const json = await res.json(); showToast(json.message); fetchDashboard(); } catch (err) { showToast(err.message, true); } } // Start a new month (reset 5€ cap) async function handleNewMonth(unlockSavings) { try { const res = await fetch(`${API_URL}/bank/new-month`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ unlock_savings: unlockSavings }) }); const json = await res.json(); showToast(json.message); fetchDashboard(); } catch (err) { showToast(err.message, true); } } // Update interest rate setting async function handleSaveRate(e) { e.preventDefault(); try { const res = await fetch(`${API_URL}/bank/settings`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ interest_rate_percent: Number(interestInput) }) }); const json = await res.json(); showToast(json.message); fetchDashboard(); } catch (err) { showToast(err.message, true); } } // Create new illustrated activity async function handleCreateActivity(e) { e.preventDefault(); try { const res = await fetch(`${API_URL}/activities`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newActivity) }); const json = await res.json(); if (!res.ok) { showToast(json.error, true); return; } setNewActivity({ title: '', description: '', emoji: '🎢', image_url: PRESET_GALLERY[4].url, cost_shells: 30 }); showToast('Nouvelle activité illustrée ajoutée pour le site et Alexa !'); fetchDashboard(); } catch (err) { showToast(err.message, true); } } // Save edited activity (image/title/cost) async function handleSaveEditedActivity(e) { e.preventDefault(); if (!editingActivity) return; try { const res = await fetch(`${API_URL}/activities/${editingActivity.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(editingActivity) }); if (res.ok) { showToast('Activité et illustration Alexa mises à jour !'); setEditingActivity(null); fetchDashboard(); } } catch (err) { showToast(err.message, true); } } async function handleDeleteActivity(id) { await fetch(`${API_URL}/activities/${id}`, { method: 'DELETE' }); showToast('Activité supprimée.'); fetchDashboard(); } async function handleAdjustChild(childId, field, delta, reason) { await fetch(`${API_URL}/children/${childId}/adjust`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ field, delta, reason }) }); showToast('Compte mis à jour !'); fetchDashboard(); } async function triggerAlexaVoice(intent, slots = {}) { setAlexaLoading(true); try { const res = await fetch(`${API_URL}/alexa/voice-intent`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ intent, slots }) }); const json = await res.json(); setAlexaResponse(json.speech); if (json.card) setAlexaCard(json.card); if (json.cards) setAlexaCardsList(json.cards); else setAlexaCardsList([]); speakText(json.speech); fetchDashboard(); } catch (err) { setAlexaResponse('Erreur de connexion au serveur Backend.'); } finally { setAlexaLoading(false); } } if (loading || !data) { return (
🐚

Chargement de la Banque des Coquillages...

); } const { settings, summary, children, activities, pending_requests, transactions } = data; return (
{/* Toast Notification */} {toast && (
{toast.isError ? : } {toast.msg}
)} {/* Header */}
🐚

La Banque des Coquillages

Hailey, Violette & Andreas • Épargne bloquée à{' '} +{settings.interest_rate_percent}% / mois • Max{' '} {settings.max_euros_per_month}€ ({settings.max_shells_euro_month} 🐚)

{/* Navigation Tabs */}
{/* Top Banner if pending requests exist */} {pending_requests.length > 0 && activeTab !== 'parents' && (
🧺

{pending_requests.length} remise(s) de coquillages physiques en attente de validation parentale !

{pending_requests[0].note}

)} {/* ==================== TAB 1: ESPACE ENFANTS & ACTIVITÉS ==================== */} {activeTab === 'kids' && (
{/* Children Cards: Hailey, Violette, Andreas */}

🧜‍♀️🐬🦈 Les Comptes de Hailey, Violette & Andreas

Dépose tes vrais coquillages auprès des parents pour les placer, les bloquer à +{settings.interest_rate_percent}% ou choisir une activité !

Dispo famille : {summary.total_available_shells} 🐚 • Bloqués : {summary.total_locked_shells} 🔒 • Intérêts fin de mois : +{summary.total_projected_interest} 🐚
{children.map((child) => (
{child.avatar}

{child.name}

Total : {child.total_shells} coquillages en banque
Coquillages Disponibles Prêts pour activités ou euros
{child.available_shells} 🐚
Livret Bloqué (+{settings.interest_rate_percent}%/mois) ✨ Rapporte +{child.projected_interest_shells} 🐚 gratuits ce mois-ci !
{child.locked_shells} 🔒
Tirelire Euros (2 🐚 = 1 €) {child.euros_balance.toFixed(2)} €
Convertis ce mois : {child.shells_converted_month}/{settings.max_shells_euro_month} 🐚 Reste : {child.remaining_euros_this_month} € dispo
))}
{/* Illustrated Common Activities Section */}

Catalogue des Activités Illustrées (Site & Écran Alexa)

Chaque activité possède son illustration qui s'affiche aussi sur les écrans Alexa Echo Show et l'application mobile Alexa !

{activities.map((act) => { const isUnlocked = act.current_shells >= act.cost_shells || act.status === 'UNLOCKED'; return (
{/* Activity Image Banner */}
{act.title}
Illustré sur Alexa {isUnlocked && ( 🎉 DÉBLOQUÉE ! )}
Coût {act.cost_shells} 🐚
{act.emoji}

{act.title}

{act.description}

{/* Progress Bar */}
Pot commun : {act.current_shells} / {act.cost_shells} 🐚 ({act.progress_percent}%) {act.remaining_shells === 0 ? 'Objectif atteint !' : `Encore ${act.remaining_shells} 🐚 à réunir`}
{/* Who contributed */}
{act.contributions.length === 0 ? ( Aucun coquillage déposé sur cette activité pour l'instant. ) : ( act.contributions.map((c) => ( {c.avatar} {c.child_name} : {c.total_shells} 🐚 )) )}
{/* Attribution Directe des Coquillages (Parents & Enfants) */}
{children.map((child) => { const isLocked = Array.isArray(act.locked_child_ids) && act.locked_child_ids.includes(child.id); return ( ); })}
); })}
)} {/* ==================== TAB 2: GUICHET PARENTS (VALIDATION, IMAGES ALEXA & INTÉRÊTS) ==================== */} {activeTab === 'parents' && (
{/* 1. Validation des remises physiques en attente */}

🧺 Guichet de Validation des Coquillages Physiques ({pending_requests.length})

Quand Hailey, Violette ou Andreas demandent à ajouter des coquillages sur une activité depuis Alexa, leur bouton se verrouille. Saisissez ici le nombre exact de coquillages physiques qu'ils vous donnent et validez !

{pending_requests.length === 0 ? (
Aucune demande ou remise de coquillages en attente pour le moment. Tout est à jour ! ✨
) : (
{pending_requests.map((req) => { const isProposal = req.type === 'PROPOSE_ACTIVITY'; const currentVal = approvalShells[req.id] ?? (req.shells > 0 ? req.shells : (isProposal ? 25 : 5)); return (
{req.child_avatar}
{req.child_name} Via {req.source} {isProposal ? ( 💡 Proposition d'activité ) : req.activity_title ? ( {req.activity_emoji} {req.activity_title} (Bouton Alexa verrouillé 🔒) ) : null}
{isProposal ? (
🎡 {req.proposed_title || 'Nouvelle Activité'}

{req.note}

) : (

{req.note}

)}
{isProposal ? 'Coût en 🐚 fixé :' : 'Nombre de 🐚 reçus :'} setApprovalShells({ ...approvalShells, [req.id]: e.target.value }) } className="w-16 bg-slate-950 border border-white/15 rounded-lg px-2 py-1 text-center font-extrabold text-amber-300 text-sm" /> {isProposal && 🐚}
); })}
)}
{/* 2. Ajouter une Activité Illustrée (avec Aperçu Écran Alexa Echo Show en direct) */}

Ajouter une Activité Illustrée pour le Skill Alexa

Choisissez une photo dans la galerie rapide, importez une image depuis votre PC/téléphone ou collez un lien d'image : elle s'affichera automatiquement sur les écrans Amazon Echo Show et dans l'application Alexa !

{/* Formulaire de création (7 colonnes) */}
setNewActivity({ ...newActivity, title: e.target.value })} className="w-full bg-slate-950 border border-white/15 rounded-xl px-3.5 py-2.5 text-sm" />
setNewActivity({ ...newActivity, cost_shells: e.target.value })} className="w-full bg-slate-950 border border-white/15 rounded-xl px-3.5 py-2.5 text-sm font-bold text-amber-300" />
setNewActivity({ ...newActivity, description: e.target.value })} className="w-full bg-slate-950 border border-white/15 rounded-xl px-3.5 py-2 text-sm" />
{/* Choix de l'illustration : Galerie 1-clic + Upload Fichier + URL */}
{/* 1. Galerie rapide d'images HD */}
Option 1 : Choisir une illustration prête à l'emploi en 1 clic
{PRESET_GALLERY.map((preset) => ( ))}
{/* 2. Upload depuis l'ordinateur ou URL personnalisée */}
Option 2 : Envoyer une photo depuis votre PC/téléphone
Option 3 : Ou coller l'URL d'une image (HTTPS) setNewActivity({ ...newActivity, image_url: e.target.value })} className="w-full bg-slate-900 border border-white/15 rounded-xl px-3 py-2 text-xs text-slate-200" />
{/* Emoji selector */}
{EMOJI_OPTIONS.map((em) => ( ))}
{/* Aperçu en direct de l'écran Alexa Echo Show (5 colonnes) */}
Aperçu en direct sur écran Alexa Echo Show :
Aperçu Alexa
🐚 LA BANQUE DES COQUILLAGES

{newActivity.emoji} {newActivity.title || 'Titre de votre activité'}

0 / {newActivity.cost_shells || 30} coquillages (0%)

{newActivity.description || `Coût : ${newActivity.cost_shells || 30} 🐚 • Dites "Alexa, Hailey met 5 coquillages pour ${ newActivity.title || 'cette activité' }"`}

Amazon Echo Show • Rendu APL & StandardCard
{/* Liste des activités existantes avec possibilité de les modifier */}

Catalogue actuel (cliquez sur « Modifier l'activité » pour éditer son nom, coût, emoji, description ou photo) :

{activities.map((a) => (
{a.title}
{a.emoji} {a.title} ({a.cost_shells} 🐚)
))}
{/* 3. Moteur d'Intérêts Mensuels & Réglages Bancaires */}

Moteur d'Intérêts Mensuels (Livret Bloqué)

Dernier versement des intérêts : {settings.last_interest_run}

{children.map((c) => (
{c.avatar} {c.name} ({c.locked_shells} 🐚 bloqués)
+{c.projected_interest_shells} 🐚 d'intérêts
))}

Paramètres du Taux & de la Banque

Ajustez le pourcentage d'intérêt mensuel qui récompense l'épargne bloquée des enfants.

setInterestInput(e.target.value)} className="w-32 bg-slate-950 border border-white/15 rounded-xl px-4 py-2.5 text-lg font-bold text-cyan-300" /> % par mois (Ex: 10 🐚 bloqués → +{Math.ceil((10 * Number(interestInput || 0)) / 100)} 🐚 gratuits)

• Règle Tirelire Euros : {settings.shells_per_euro} coquillages = 1,00 €

• Plafond mensuel par enfant :{' '} {settings.max_euros_per_month} € maximum ({settings.max_shells_euro_month} coquillages)

{/* 4. Gestion Complète & Ajustement des Comptes (Parents) */}

Gestion & Édition de la Banque des Enfants

Transférez les coquillages réels reçus ou utilisés, ou modifiez directement les soldes de Hailey, Violette et Andreas.

{children.map((c) => (
{c.avatar}

{c.name}

Total : {c.total_shells} 🐚
Dispo {c.available_shells} 🐚
Bloqué {c.locked_shells} 🔒
Tirelire {c.euros_balance.toFixed(2)} €
{c.locked_shells > 0 && ( )} {c.euros_balance > 0 && ( )}
))}

Dernières opérations enregistrées

{transactions.map((t) => (
{t.description} {new Date(t.created_at).toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' })}
))}
)} {/* ==================== TAB 3: SIMULATEUR VOCAL ALEXA + ÉCRAN ECHO SHOW ==================== */} {activeTab === 'alexa' && (

Simulateur Vocal & Écran Echo Show

Testez les phrases vocales et visualisez la carte illustrée qui s'affiche sur l'écran d'Alexa !

{/* Alexa Echo Bubble */}
Voix d'Alexa

« {alexaLoading ? 'Alexa réfléchit...' : alexaResponse} »

{/* Preset Voice Commands */}

Phrases rapides à tester en 1 clic :

{/* Écran Virtuel Amazon Echo Show + Constructeur sur-mesure (5 colonnes) */}
{/* Écran Echo Show */}
Écran Amazon Echo Show APL + StandardCard
{alexaCard && (
{alexaCard.title}
🐚 LA BANQUE DES COQUILLAGES

{alexaCard.title}

{alexaCard.subtitle && (

{alexaCard.subtitle}

)} {alexaCard.text && (

{alexaCard.text}

)}
{/* Mini-carrousel si plusieurs activités sont retournées */} {alexaCardsList.length > 1 && (
Activités affichées dans le carrousel Alexa (cliquez pour prévisualiser) :
{alexaCardsList.map((c, idx) => ( ))}
)}
)}
{/* Custom Intent Builder */}

🎛️ Tester une commande vocale sur-mesure

setCustomAlexa({ ...customAlexa, ShellCount: e.target.value })} className="w-full bg-slate-950 border border-white/15 rounded-xl px-3 py-2" />
{customAlexa.intent === 'ContributeActivityIntent' && (
)}
)}
{/* Modal Complète pour Modifier une Activité Existante (Titre, Coût, Cagnotte, Statut, Photo) */} {editingActivity && (
⚙️ Modification de l'activité

{editingActivity.emoji} {editingActivity.title}

{/* 1. Titre & Emoji & Coût */}
setEditingActivity({ ...editingActivity, emoji: e.target.value })} className="w-full bg-slate-950 border border-white/15 rounded-xl px-2.5 py-2 text-center text-xl font-bold" />
setEditingActivity({ ...editingActivity, title: e.target.value })} className="w-full bg-slate-950 border border-white/15 rounded-xl px-3.5 py-2 text-sm text-white font-semibold" />
setEditingActivity({ ...editingActivity, cost_shells: Number(e.target.value) })} className="w-full bg-slate-950 border border-white/15 rounded-xl px-3.5 py-2 text-sm font-extrabold text-amber-300 text-center" />
{/* 2. Cagnotte actuelle & Statut */}
setEditingActivity({ ...editingActivity, current_shells: Number(e.target.value) })} className="w-full bg-slate-950 border border-white/15 rounded-xl px-3.5 py-2 text-sm font-extrabold text-emerald-400" />
{/* 3. Description */}