import { useTranslation } from "react-i18next"; import { useState, useEffect } from "react"; import Cookies from "js-cookie"; import { Sheet, Input, Button, Checkbox, Chip, IconButton, Alert, Typography, FormControl, FormLabel, ButtonGroup, CircularProgress, } from "@mui/joy"; import { submitFormData } from "../utils/api/form"; import type { FormData, Message } from "../config/interfaces.config"; import PersonIcon from "@mui/icons-material/Person"; import QrCodeIcon from "@mui/icons-material/QrCode"; import TranslateIcon from "@mui/icons-material/Translate"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { confirmUser, fetchInfo } from "../utils/api/users"; import { QRcodeModal } from "../components/modals/QR-CodeModal"; import { SelectUserModal } from "../components/modals/SelectUserModal"; import { useForm } from "@tanstack/react-form"; import { changeTranslation } from "../utils/uxFncs"; import { createFormSchema } from "../config/interfaces.config"; import { validateFieldWithZod } from "../utils/uxFncs"; import { TextField } from "../components/TextField"; import { PAYMENT_METHODS, PAYMENT_LABELS } from "../utils/uxFncs"; export const MainForm = () => { const { t } = useTranslation(); const queryClient = useQueryClient(); // States const [invoice, setInvoice] = useState(false); const [msg, setMsg] = useState(null); const [selectedUser, setSelectedUser] = useState(null); const [selectedDraw, setSelectedDraw] = useState(null); const [showSelectUser, setShowSelectUser] = useState(false); const [QRmodal, setQRmodal] = useState(false); const formSchema = createFormSchema(t, invoice); // Validates the fields and makes sure that the form is maching the zod schema const makeFieldValidator = (fieldName: string) => ({ onSubmit: ({ fieldApi, }: { fieldApi: { form: { state: { values: Record } } }; }) => { const allValues = fieldApi.form.state.values; return validateFieldWithZod(formSchema, fieldName, allValues); }, onBlur: ({ fieldApi, }: { fieldApi: { form: { state: { values: Record } } }; }) => { const allValues = fieldApi.form.state.values; return validateFieldWithZod(formSchema, fieldName, allValues); }, }); // Creates a form with tanstack form const { Field, Subscribe, handleSubmit, setFieldValue } = useForm({ defaultValues: { firstName: "", lastName: "", email: "", phoneNumber: "", tickets: 0, chocolates: false, companyName: "", cmpFirstName: "", cpmLastName: "", cpmEmail: "", cpmPhoneNumber: "", street: "", postalCode: "", paymentMethod: "", }, onSubmit: async ({ value }) => { const result = formSchema.safeParse(value); if (!result.success) return; mutateForm(value as FormData); }, }); // This function returns the errors for one field const getErrors = (field: { state: { meta: { errorMap: Record } }; }) => { const normalizeErrors = (value: unknown) => { if (Array.isArray(value)) return value as string[]; if (typeof value === "string" && value.length > 0) return [value]; return [] as string[]; }; const blurErrors = normalizeErrors(field.state.meta.errorMap["onBlur"]); const submitErrors = normalizeErrors(field.state.meta.errorMap["onSubmit"]); return [...blurErrors, ...submitErrors].filter(Boolean); }; // useEffect that only executes on mount and is checking if there is a user selcted in the cookies useEffect(() => { const savedUser = Cookies.get("selectedUser"); const savedDraw = Cookies.get("selectedDraw"); if (savedDraw) { setSelectedDraw(savedDraw); } if (savedUser) { setSelectedUser(savedUser); } else { setMsg({ type: "warning", headline: t("set-username-headline"), text: t("set-username-text"), }); } }, []); // Fetch users tanstack query const { data: info, isLoading: infoIsLoading } = useQuery({ queryKey: ["info"], queryFn: fetchInfo, }); // Query for validating selcted user const { data: userData } = useQuery({ queryKey: ["user", selectedUser], enabled: !!selectedUser, queryFn: () => confirmUser(selectedUser), }); // Tanstack query (mutate) for sending the form const { mutate: mutateForm, isPending: mutateFormIsPending } = useMutation({ mutationFn: (values: FormData) => submitFormData(values, selectedUser), onSuccess: (_, values) => { queryClient.invalidateQueries({ queryKey: ["user", selectedUser] }); document.location.href = `/success?id=${nextID}&tickets=${values.tickets}`; }, onError: () => { queryClient.invalidateQueries({ queryKey: ["user", selectedUser] }); setMsg({ type: "danger", headline: t("error"), text: t("form-submission-failed"), }); }, }); const nextID = userData?.nextID ?? "N/A"; // function for selecting user const handleUserSelection = (username: string | null) => { if (username == null || username == "") { return; } setSelectedUser(username); }; // function for selecting draw const handleDrawSelection = (draw: string | null) => { if (draw == null || draw == "") { return; } setSelectedDraw(draw); Cookies.set("selectedDraw", draw); }; return ( <>
setShowSelectUser(true)}> setQRmodal(true)}> {`${t("greeting")} ${userData?.fullname ?? t("loading")}`}
{ e.preventDefault(); handleSubmit(); }} className="flex flex-col gap-4" > #{nextID ?? "N/A"} {/* Name row */}
{(field) => ( )} {(field) => ( )}
{(field) => ( )} {(field) => ( )} {/* Tickets + Invoice + Chocolate toggle */}
{t("tickets")} {(field) => { const errors = getErrors(field); return ( <> { const val = e.target.value; field.handleChange(val === "" ? 0 : Number(val)); }} variant="soft" sx={{ borderRadius: "10px" }} /> {errors.length > 0 && ( {errors[0]} )} ); }}
{ const checked = e.target.checked; setInvoice(checked); if (!checked) { setFieldValue("companyName", ""); setFieldValue("cmpFirstName", ""); setFieldValue("cpmLastName", ""); setFieldValue("cpmEmail", ""); setFieldValue("cpmPhoneNumber", ""); setFieldValue("street", ""); setFieldValue("postalCode", ""); } }} label={t("invoice")} variant="outlined" /> {(field) => ( field.handleChange(e.target.checked)} label={t("chocolates")} variant="outlined" /> )}
{/* Invoice details (conditional) */} {invoice && (
{t("invoice-details")} {(field) => ( )}
{(field) => ( )} {(field) => ( )}
{(field) => ( )} {(field) => ( )} {(field) => ( )} {(field) => ( )}
)} {/* Payment method selection */} {t("select-payment-method")} state.values.paymentMethod}> {(paymentMethod) => (
{PAYMENT_METHODS.map((method) => ( ))}
)}
{mutateFormIsPending ? (
) : ( state.values.paymentMethod}> {(paymentMethod) => ( )} )} {msg && ( {msg.headline} {msg.text} )}
); };