146 lines
4.4 KiB
TypeScript
146 lines
4.4 KiB
TypeScript
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<PredictionEntry>;
|
|
|
|
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<boolean>;
|
|
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<PredictionDraftValue[]>(initialValues);
|
|
const [persistedValues, setPersistedValues] = useState<PredictionDraftValue[]>(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]);
|
|
}
|