import { useEffect, useState, useRef } from "react"; import { useNavigate, useParams } from "react-router-dom"; import styled from "styled-components"; import { useChildren } from "../state/ChildrenContext"; import { API_BASE_URL, uploadPlanning, uploadAvatar } from "../services/api-client"; const Container = styled.div` max-width: 1400px; margin: 0 auto; `; const Header = styled.header` display: flex; align-items: center; gap: 24px; margin-bottom: 32px; padding: 30px; border-radius: 24px; background: linear-gradient(135deg, rgba(29, 36, 66, 0.95) 0%, rgba(45, 27, 78, 0.95) 100%); border: 1px solid rgba(126, 136, 180, 0.3); box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); `; const BackButton = styled.button` padding: 12px 20px; border-radius: 12px; border: 1px solid rgba(126, 136, 180, 0.4); background: rgba(16, 22, 52, 0.9); color: #d9dcff; font-weight: 600; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 8px; &:hover { transform: translateY(-2px); background: rgba(26, 32, 62, 0.9); } `; const Avatar = styled.div<{ $imageUrl?: string; $color: string }>` width: 120px; height: 120px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 2.5rem; background: ${({ $imageUrl, $color }) => $imageUrl ? `url(${$imageUrl}) center/cover` : $color}; color: ${({ $imageUrl }) => ($imageUrl ? "transparent" : "#040411")}; border: 4px solid ${({ $color }) => $color}; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); `; const HeaderInfo = styled.div` flex: 1; display: flex; flex-direction: column; gap: 12px; `; const Title = styled.h1` margin: 0; font-size: 2rem; font-weight: 700; `; const Meta = styled.div` display: flex; gap: 20px; flex-wrap: wrap; color: var(--text-muted); font-size: 14px; `; const MetaItem = styled.div<{ $icon?: string }>` display: flex; align-items: center; gap: 8px; &::before { content: '${props => props.$icon || "•"}'; color: #66d9ff; } `; const PronoteStatus = styled.div<{ $connected: boolean }>` display: inline-flex; align-items: center; gap: 8px; padding: 8px 16px; border-radius: 20px; font-size: 13px; font-weight: 600; background: ${props => props.$connected ? 'rgba(46, 213, 115, 0.2)' : 'rgba(255, 107, 107, 0.2)'}; color: ${props => props.$connected ? '#2ed573' : '#ff6b6b'}; border: 1px solid ${props => props.$connected ? 'rgba(46, 213, 115, 0.4)' : 'rgba(255, 107, 107, 0.4)'}; &::before { content: '●'; font-size: 8px; } `; const ActionButtons = styled.div` display: flex; gap: 12px; flex-wrap: wrap; margin-top: 20px; `; const Button = styled.button` padding: 12px 24px; border-radius: 12px; border: none; font-weight: 600; cursor: pointer; transition: all 0.3s ease; display: flex; align-items: center; gap: 8px; font-size: 14px; &:hover { transform: translateY(-2px); } &:disabled { opacity: 0.5; cursor: not-allowed; } `; const PrimaryButton = styled(Button)` background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: #ffffff; box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); &:hover { box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6); } `; const SecondaryButton = styled(Button)` background: rgba(255, 255, 255, 0.1); color: white; border: 1px solid rgba(255, 255, 255, 0.2); &:hover { background: rgba(255, 255, 255, 0.2); } `; const PronoteButton = styled(Button)` background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; box-shadow: 0 4px 15px rgba(240, 147, 251, 0.4); &:hover { box-shadow: 0 6px 20px rgba(240, 147, 251, 0.6); } `; const DangerButton = styled(Button)` background: rgba(255, 107, 107, 0.2); color: #ff6b6b; border: 1px solid rgba(255, 107, 107, 0.4); &:hover { background: rgba(255, 107, 107, 0.3); } `; const MainGrid = styled.div` display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 20px; margin-bottom: 20px; `; const Card = styled.section` padding: 25px; border-radius: 20px; background: rgba(29, 36, 66, 0.92); border: 1px solid rgba(126, 136, 180, 0.22); display: flex; flex-direction: column; gap: 20px; transition: all 0.3s ease; &:hover { transform: translateY(-5px); box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3); } `; const CardHeader = styled.div` display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; `; const CardTitle = styled.h2<{ $icon?: string }>` margin: 0; font-size: 20px; font-weight: 700; display: flex; align-items: center; gap: 10px; &::before { content: '${props => props.$icon || ""}'; font-size: 24px; color: #66d9ff; } `; const CardBadge = styled.span` background: rgba(255, 255, 255, 0.2); padding: 6px 12px; border-radius: 20px; font-size: 12px; font-weight: 600; `; const Select = styled.select` padding: 12px 14px; border-radius: 12px; border: 1px solid rgba(126, 136, 180, 0.28); background: rgba(16, 22, 52, 0.9); color: #ffffff; cursor: pointer; width: 100%; font-size: 14px; `; const Label = styled.label` display: flex; flex-direction: column; gap: 8px; font-weight: 600; font-size: 14px; `; const HolidayList = styled.div` display: flex; flex-direction: column; gap: 12px; max-height: 400px; overflow-y: auto; `; const HolidayItem = styled.div` padding: 15px; border-radius: 10px; background: rgba(46, 213, 115, 0.1); border-left: 3px solid #2ed573; `; const HolidayName = styled.div` font-weight: 600; font-size: 14px; margin-bottom: 5px; `; const HolidayDate = styled.div` font-size: 12px; color: rgba(255, 255, 255, 0.7); margin-bottom: 3px; `; const HolidayType = styled.div` font-size: 11px; color: rgba(255, 255, 255, 0.5); font-style: italic; `; const PersonalNotes = styled.textarea` background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 10px; padding: 15px; min-height: 150px; color: white; font-size: 14px; line-height: 1.6; resize: vertical; font-family: inherit; &:focus { outline: none; border-color: #667eea; background: rgba(255, 255, 255, 0.08); } `; const EmptyState = styled.div` text-align: center; padding: 30px; color: rgba(255, 255, 255, 0.5); font-size: 14px; `; const StatusMessage = styled.div` padding: 12px; border-radius: 12px; background: rgba(12, 18, 42, 0.7); border: 1px solid rgba(126, 136, 180, 0.24); color: var(--text-muted); text-align: center; `; const ErrorText = styled.div` color: #ff7b8a; font-size: 0.9rem; `; const SuccessText = styled.div` color: #2ed573; font-size: 0.9rem; `; // Modal components const ModalOverlay = styled.div` position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.7); display: flex; align-items: center; justify-content: center; z-index: 1000; backdrop-filter: blur(5px); `; const ModalContent = styled.div<{ $width?: string }>` background: linear-gradient(135deg, rgba(29, 36, 66, 0.98) 0%, rgba(45, 27, 78, 0.98) 100%); border-radius: 24px; padding: 40px; max-width: ${({ $width }) => $width || '500px'}; width: 90%; border: 1px solid rgba(126, 136, 180, 0.3); box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); max-height: 90vh; overflow-y: auto; `; const ModalTitle = styled.h2` margin: 0 0 24px 0; font-size: 24px; text-align: center; `; const Input = styled.input` width: 100%; padding: 14px 16px; border-radius: 12px; border: 1px solid rgba(126, 136, 180, 0.28); background: rgba(16, 22, 52, 0.9); color: #ffffff; font-size: 14px; margin-bottom: 16px; &:focus { outline: none; border-color: #667eea; } &::placeholder { color: rgba(255, 255, 255, 0.5); } `; const FileInput = styled.input` display: none; `; const FileUploadArea = styled.div<{ $isDragOver: boolean }>` border: 2px dashed ${({ $isDragOver }) => $isDragOver ? '#667eea' : 'rgba(126, 136, 180, 0.4)'}; border-radius: 12px; padding: 40px; text-align: center; background: ${({ $isDragOver }) => $isDragOver ? 'rgba(102, 126, 234, 0.1)' : 'rgba(255, 255, 255, 0.05)'}; cursor: pointer; transition: all 0.3s ease; margin-bottom: 16px; &:hover { background: rgba(255, 255, 255, 0.08); border-color: #667eea; } `; const FileUploadIcon = styled.div` font-size: 48px; margin-bottom: 12px; `; const FileUploadText = styled.div` color: rgba(255, 255, 255, 0.8); font-size: 14px; margin-bottom: 8px; `; const FileUploadHint = styled.div` color: rgba(255, 255, 255, 0.5); font-size: 12px; `; const SelectedFile = styled.div` background: rgba(102, 126, 234, 0.2); padding: 12px 16px; border-radius: 12px; display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; `; const FileName = styled.div` color: #ffffff; font-size: 14px; font-weight: 600; `; const RemoveFileButton = styled.button` background: rgba(255, 107, 107, 0.3); border: none; color: #ff6b6b; padding: 6px 12px; border-radius: 8px; cursor: pointer; font-size: 12px; &:hover { background: rgba(255, 107, 107, 0.5); } `; const ModalButtons = styled.div` display: flex; gap: 12px; margin-top: 24px; `; const ColorPicker = styled.input` width: 60px; height: 40px; border-radius: 8px; border: 1px solid rgba(126, 136, 180, 0.28); background: transparent; cursor: pointer; `; const REGION_LABELS: Record = { "zone-a": "Zone A (Besançon, Bordeaux, Clermont-Ferrand, Dijon, Grenoble, Limoges, Lyon, Poitiers)", "zone-b": "Zone B (Aix-Marseille, Amiens, Caen, Lille, Nancy-Metz, Nantes, Nice, Orléans-Tours, Reims, Rennes, Rouen, Strasbourg)", "zone-c": "Zone C (Créteil, Montpellier, Paris, Toulouse, Versailles)", corse: "Corse", monaco: "Monaco", guadeloupe: "Guadeloupe", guyane: "Guyane", martinique: "Martinique", reunion: "Réunion", mayotte: "Mayotte" }; interface Holiday { id: string; title: string; startDate: string; endDate: string; description?: string; } interface Child { id: string; fullName: string; colorHex: string; email?: string; notes?: string; avatar?: { url: string }; schoolRegion?: string; } export const ChildDetailScreen = () => { const { childId } = useParams<{ childId: string }>(); const navigate = useNavigate(); const { children, updateChild, deleteChild } = useChildren(); const fileInputRef = useRef(null); const editAvatarInputRef = useRef(null); const [child, setChild] = useState(null); const [selectedRegion, setSelectedRegion] = useState(""); const [holidays, setHolidays] = useState([]); const [loadingHolidays, setLoadingHolidays] = useState(false); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [success, setSuccess] = useState(null); // États des modals const [showImportModal, setShowImportModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); // États Import const [selectedFile, setSelectedFile] = useState(null); const [isDragOver, setIsDragOver] = useState(false); const [importing, setImporting] = useState(false); const [importProgress, setImportProgress] = useState(""); // États Edit const [editName, setEditName] = useState(""); const [editEmail, setEditEmail] = useState(""); const [editColor, setEditColor] = useState("#667eea"); const [editAvatar, setEditAvatar] = useState(null); const [editAvatarPreview, setEditAvatarPreview] = useState(null); const [personalNotes, setPersonalNotes] = useState(""); useEffect(() => { const foundChild = children.find((c: Child) => c.id === childId); if (foundChild) { setChild(foundChild); setSelectedRegion(foundChild.schoolRegion ?? ""); setPersonalNotes(foundChild.notes || ""); } }, [childId, children]); useEffect(() => { if (selectedRegion) { loadHolidays(selectedRegion); } else { setHolidays([]); } }, [selectedRegion]); const loadHolidays = async (region: string) => { setLoadingHolidays(true); setError(null); try { const response = await fetch(`${API_BASE_URL}/holidays?region=${region}&year=${new Date().getFullYear()}`); const data = await response.json(); if (data.success) { setHolidays(data.holidays); } else { setError("Impossible de charger les congés"); } } catch (err) { setError("Erreur de connexion au serveur"); } finally { setLoadingHolidays(false); } }; const handleSaveRegion = async () => { if (!child || !childId) return; setSaving(true); setError(null); try { await updateChild(childId, { schoolRegion: selectedRegion || undefined }); setSuccess("Région mise à jour avec succès !"); setTimeout(() => setSuccess(null), 3000); } catch (err) { setError("Impossible de sauvegarder la région"); } finally { setSaving(false); } }; const handleSaveNotes = async () => { if (!child || !childId) return; try { await updateChild(childId, { notes: personalNotes }); setSuccess("Notes sauvegardées !"); setTimeout(() => setSuccess(null), 3000); } catch (err) { setError("Erreur lors de la sauvegarde"); } }; // Import functionality const handleOpenImport = () => { setShowImportModal(true); setSelectedFile(null); setImportProgress(""); setError(null); }; const handleFileSelect = (event: React.ChangeEvent) => { const files = event.target.files; if (files && files.length > 0) { setSelectedFile(files[0]); } }; const handleDragOver = (event: React.DragEvent) => { event.preventDefault(); setIsDragOver(true); }; const handleDragLeave = () => { setIsDragOver(false); }; const handleDrop = (event: React.DragEvent) => { event.preventDefault(); setIsDragOver(false); const files = event.dataTransfer.files; if (files && files.length > 0) { setSelectedFile(files[0]); } }; const handleImport = async () => { if (!selectedFile || !childId) return; setImporting(true); setError(null); setImportProgress("Analyse du fichier en cours..."); try { const result = await uploadPlanning(childId, selectedFile); setImportProgress(`Import réussi ! ${result.schedule.activities?.length || 0} activités détectées.`); setTimeout(() => { setShowImportModal(false); setSuccess("Fichier importé avec succès !"); setTimeout(() => setSuccess(null), 3000); }, 2000); } catch (err) { setError("Erreur lors de l'import du fichier"); setImportProgress(""); } finally { setImporting(false); } }; // Edit functionality const handleOpenEdit = () => { if (!child) return; setEditName(child.fullName); setEditEmail(child.email || ""); setEditColor(child.colorHex); setEditAvatar(null); setEditAvatarPreview(child.avatar?.url || null); setShowEditModal(true); setError(null); }; const handleEditAvatarSelect = (event: React.ChangeEvent) => { const files = event.target.files; if (files && files.length > 0) { const file = files[0]; setEditAvatar(file); // Create preview const reader = new FileReader(); reader.onload = (e) => { setEditAvatarPreview(e.target?.result as string); }; reader.readAsDataURL(file); } }; const handleSaveEdit = async () => { if (!child || !childId) return; setSaving(true); setError(null); try { let avatarData = undefined; // Upload avatar if changed if (editAvatar) { const uploadResult = await uploadAvatar(editAvatar); avatarData = { url: uploadResult.url }; } // Update child await updateChild(childId, { fullName: editName, email: editEmail || undefined, colorHex: editColor, ...(avatarData && { avatar: avatarData }) }); setShowEditModal(false); setSuccess("Profil mis à jour avec succès !"); setTimeout(() => setSuccess(null), 3000); } catch (err) { setError("Erreur lors de la modification du profil"); } finally { setSaving(false); } }; // Delete functionality const handleOpenDelete = () => { setShowDeleteModal(true); setError(null); }; const handleConfirmDelete = async () => { if (!childId) return; setSaving(true); setError(null); try { await deleteChild(childId); navigate("/profiles"); } catch (err) { setError("Erreur lors de la suppression du profil"); setSaving(false); } }; const formatDate = (dateStr: string) => { if (!dateStr) return ''; return new Date(dateStr).toLocaleDateString("fr-FR", { day: "numeric", month: "long", year: "numeric" }); }; if (!child) { return Chargement du profil...; } const initials = child.fullName .split(" ") .filter(Boolean) .map((part) => part.charAt(0)) .join("") .slice(0, 2) .toUpperCase(); return (
navigate("/profiles")}>← Retour {!child.avatar?.url && initials} {child.fullName} {selectedRegion ? REGION_LABELS[selectedRegion]?.split('(')[0] : "Zone non définie"} {child.email && {child.email}} navigate(`/child/${childId}/planning`)}> 📅 Planning 📥 Importer ✏️ Modifier 🗑️ Supprimer
{error && {error}} {success && {success}} {/* Région scolaire et congés */} Congés scolaires {saving ? "Enregistrement..." : "Enregistrer la région"} {loadingHolidays ? ( Chargement des congés... ) : holidays.length > 0 ? ( {holidays.slice(0, 5).map((holiday) => ( {holiday.title} {holiday.startDate === holiday.endDate ? formatDate(holiday.startDate) : `Du ${formatDate(holiday.startDate)} au ${formatDate(holiday.endDate)}`} {holiday.description && {holiday.description}} ))} ) : selectedRegion && ( Aucun congé trouvé )} {/* Notes personnelles */} Notes personnelles setPersonalNotes(e.target.value)} placeholder="Ajoutez vos notes personnelles ici..." /> 💾 Enregistrer les notes {/* Modal Import */} {showImportModal && ( !importing && setShowImportModal(false)}> e.stopPropagation()}> Importer un planning

Importez un fichier Excel, PDF, Image (JPG/PNG) ou JSON contenant le planning.

{!selectedFile ? ( fileInputRef.current?.click()} > 📁 Glissez-déposez un fichier ici ou cliquez pour parcourir Formats acceptés : Excel (.xlsx, .xls), PDF, Images (.jpg, .png), JSON ) : ( 📄 {selectedFile.name} setSelectedFile(null)}> Retirer )} {importProgress && ( {importProgress} )} {error && {error}} setShowImportModal(false)} disabled={importing} style={{ flex: 1 }} > Annuler {importing ? "Import en cours..." : "Importer"}
)} {/* Modal Edit */} {showEditModal && ( !saving && setShowEditModal(false)}> e.stopPropagation()}> Modifier le profil {error && {error}} setShowEditModal(false)} disabled={saving} style={{ flex: 1 }} > Annuler {saving ? "Enregistrement..." : "Enregistrer"} )} {/* Modal Delete */} {showDeleteModal && ( !saving && setShowDeleteModal(false)}> e.stopPropagation()}> Supprimer le profil

Êtes-vous sûr de vouloir supprimer le profil de {child.fullName} ?

Cette action déplacera le profil dans les archives. Vous pourrez le restaurer depuis les paramètres.

{error && {error}} setShowDeleteModal(false)} disabled={saving} style={{ flex: 1 }} > Annuler {saving ? "Suppression..." : "Supprimer"}
)}
); };