import { useCallback, useMemo, useState } from "react"; import { areDraftValuesEqual, getDraftValuesFromEntry, serializeChangedDraftToUpsertPayload, validateDraft, } from "@/features/predictions/domain/prediction-serializers"; import type { PredictionCard, PredictionDraftValue, PredictionEntry, UpsertPredictionPayload, } from "@/types/predictions"; type SaveHandler = (cardId: string, body: UpsertPredictionPayload) => Promise; type ControllerInput = { card: PredictionCard; entry?: PredictionEntry; onSave: SaveHandler; disabled?: boolean; }; export type PredictionTileController = { draftValues: PredictionDraftValue[]; persistedValues: PredictionDraftValue[]; dirty: boolean; saving: boolean; errorMessage: string; successMessage: string; setText: (fieldId: string, value: string) => void; setNumber: (fieldId: string, value: number | null) => void; setDate: (fieldId: string, value: string) => void; submit: () => Promise; resetFromEntry: (nextEntry?: PredictionEntry) => void; }; function updateDraftValue( draftValues: PredictionDraftValue[], fieldId: string, updater: (current: PredictionDraftValue) => PredictionDraftValue, ) { return draftValues.map((value) => (value.fieldId === fieldId ? updater(value) : value)); } export function usePredictionTileController({ card, entry, onSave, disabled = false, }: ControllerInput): PredictionTileController { const initialValues = useMemo(() => getDraftValuesFromEntry(card, entry), [card, entry]); const [draftValues, setDraftValues] = useState(initialValues); const [persistedValues, setPersistedValues] = useState(initialValues); const [saving, setSaving] = useState(false); const [errorMessage, setErrorMessage] = useState(""); const [successMessage, setSuccessMessage] = useState(""); const dirty = useMemo( () => !areDraftValuesEqual(draftValues, persistedValues), [draftValues, persistedValues], ); const setText = useCallback((fieldId: string, value: string) => { setDraftValues((current) => updateDraftValue(current, fieldId, (draft) => ({ ...draft, valueText: value })), ); }, []); const setNumber = useCallback((fieldId: string, value: number | null) => { setDraftValues((current) => updateDraftValue(current, fieldId, (draft) => ({ ...draft, valueNumber: value })), ); }, []); const setDate = useCallback((fieldId: string, value: string) => { setDraftValues((current) => updateDraftValue(current, fieldId, (draft) => ({ ...draft, valueDate: value })), ); }, []); const submit = useCallback(async () => { if (disabled) { setErrorMessage("Le jeu est cloture. Les pronostics sont verrouilles."); return false; } const validation = validateDraft(card, draftValues); if (!validation.valid) { setErrorMessage(validation.message); setSuccessMessage(""); return false; } const payload = serializeChangedDraftToUpsertPayload(card, draftValues, persistedValues); if (payload.values.length === 0) { setErrorMessage(""); setSuccessMessage("Aucun changement a enregistrer"); return true; } setSaving(true); setErrorMessage(""); setSuccessMessage(""); try { const updatedEntry = await onSave(card.id, payload); const nextValues = getDraftValuesFromEntry(card, updatedEntry); setDraftValues(nextValues); setPersistedValues(nextValues); setSuccessMessage("Pronostic enregistre"); return true; } catch (error) { setErrorMessage(error instanceof Error ? error.message : "Erreur de sauvegarde"); return false; } finally { setSaving(false); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [card, disabled, draftValues, persistedValues, onSave]); const resetFromEntry = useCallback((nextEntry?: PredictionEntry) => { const nextValues = getDraftValuesFromEntry(card, nextEntry); setDraftValues(nextValues); setPersistedValues(nextValues); setErrorMessage(""); setSuccessMessage(""); }, [card]); return useMemo(() => ({ draftValues, persistedValues, dirty, saving, errorMessage, successMessage, setText, setNumber, setDate, submit, resetFromEntry, }), [draftValues, persistedValues, dirty, saving, errorMessage, successMessage, setText, setNumber, setDate, submit, resetFromEntry]); }