import type { AllergyView } from "@batch-cooking/shared"; import { ErrorCode } from "@batch-cooking/shared"; import { type FormEvent, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { ApiError, apiClient } from "../../api/client"; import { AllergySelect } from "../../features/profile/AllergySelect"; import { errorMessageService } from "../../services/error-message.service"; import "./onboarding.scss"; /** * Last step of the post-signup onboarding wizard, routed at * `/onboarding/allergenes`. Starts from an empty selection — a freshly * signed-up profile has none yet, so there's no need for the extra * `GET /profile/allergies` round trip a "resume where I left off" flow * would require (the `/parametres/preferences` settings page is the * always-fetch source of truth for editing an existing selection later). * * Also fetches the current household on mount, only to know whether the * `/onboarding/sources` step ran before this one (household step created * one and it had a source to configure) — the step count shown reflects * whichever path the visitor actually took, "3 sur 3" or "4 sur 4" * (there's no shared wizard state to read this from — see * `OnboardingHouseholdPage`'s doc comment on why each step is independent). */ export function OnboardingAllergensPage() { const { t } = useTranslation(); const navigate = useNavigate(); const [allergies, setAllergies] = useState([]); const [allergyIds, setAllergyIds] = useState([]); const [totalSteps, setTotalSteps] = useState(3); const [isLoading, setIsLoading] = useState(true); const [formError, setFormError] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); useEffect(() => { let cancelled = false; Promise.all([apiClient.getAllergies(), apiClient.getCurrentHouse()]) .then(([allergiesResult, house]) => { if (cancelled) return; setAllergies(allergiesResult); setTotalSteps(house !== null ? 4 : 3); }) .catch(() => { // Neither piece is essential for this last step to render — the // allergy list just stays empty (nothing to pick from) and the // step count keeps its 3-of-3 default, same "don't strand the // visitor over a transient failure" reasoning as // OnboardingSourcesPage's own catch, just with nothing to // navigate away to since this already is the last step. }) .finally(() => { if (!cancelled) setIsLoading(false); }); return () => { cancelled = true; }; }, []); /** Finishes the wizard — the profile journey is complete, back to the app itself. */ async function handleSubmit(e: FormEvent) { e.preventDefault(); setFormError(null); setIsSubmitting(true); try { await apiClient.updateAllergyIds(allergyIds); void navigate("/"); } catch (err) { const code = err instanceof ApiError ? err.code : ErrorCode.INTERNAL_ERROR; setFormError(errorMessageService.getLabel(code)); } finally { setIsSubmitting(false); } } return (

{t("onboarding.step", { current: totalSteps, total: totalSteps })}

{t("onboarding.allergens.title")}

{isLoading ? (

{t("onboarding.loading")}

) : ( <> allergy.kind === "ALLERGY")} value={allergyIds} onChange={setAllergyIds} /> allergy.kind === "INTOLERANCE")} value={allergyIds} onChange={setAllergyIds} /> )} {formError &&

{formError}

}
); }