diff --git a/components/playground.tsx b/components/playground.tsx index dffc46a..6bab839 100644 --- a/components/playground.tsx +++ b/components/playground.tsx @@ -29,7 +29,7 @@ import { Input } from "@/components/ui/input"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { processImage } from "@/lib/process-image"; import { Crop } from "./crop"; -import { useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { ImageResponse } from "next/og"; import Image from "next/image"; import { createPassport, uploadImageToR2 } from "@/lib/actions"; @@ -68,55 +68,40 @@ const ORIGINS = ["The woods", "The deep sea", "The tundra"]; const maxDate = new Date(); -const FormSchema = z - .object({ - surname: z - .string() - .min(1, { - message: "Name must be at least 1 character.", - }) - .trim(), - firstName: z - .string() - .min(1, { - message: "Name must be at least 1 character.", - }) - .trim(), - placeOfOrigin: z.string().max(13), - dateOfBirth: z - .string() - .refine((val) => !isNaN(Date.parse(val)), { - message: "Invalid date format. Please enter a valid date.", - }) - .refine( - (val) => { - const inputDate = new Date(val); - return inputDate < maxDate; - }, - { - message: "Date of birth cannot be later than today.", - }, - ), - ceremonyTime: z - .string() // Don't refine. Causes issues. - .optional(), - image: z.custom((val) => val instanceof File, "Please upload a file"), - passportNumber: z.string().max(4).optional(), - sendToDb: z.boolean().default(false), - }) - .refine( - (data) => { - // If sendToDb is true, ceremonyTime must be defined and not empty - if (data.sendToDb) { - return !!data.ceremonyTime; - } - return true; // If sendToDb is false, no need to validate ceremonyTime - }, - { - path: ["ceremonyTime"], // This points to the field where the error will be displayed - message: "Please choose a ceremony date to register a passport", - }, - ); +const FormSchema = z.object({ + surname: z + .string() + .min(1, { + message: "Name must be at least 1 character.", + }) + .trim(), + firstName: z + .string() + .min(1, { + message: "Name must be at least 1 character.", + }) + .trim(), + placeOfOrigin: z.string().max(13), + dateOfBirth: z + .string() + .refine((val) => !isNaN(Date.parse(val)), { + message: "Invalid date format. Please enter a valid date.", + }) + .refine( + (val) => { + const inputDate = new Date(val); + return inputDate < maxDate; + }, + { + message: "Date of birth cannot be later than today.", + }, + ), + ceremonyTime: z + .string() // Don't refine. Causes issues. + .optional(), + image: z.custom((val) => val instanceof File, "Please upload a file"), + passportNumber: z.string().max(4).optional(), +}); export default function Playground({ userId, @@ -141,7 +126,6 @@ export default function Playground({ ceremonyTime: undefined, image: undefined, passportNumber: "0", - sendToDb: false, }, }); @@ -164,6 +148,22 @@ export default function Playground({ const [secretOptionsEnabled, setSecretOptionsEnabled] = useState(false); const [secretSignatureSigned, setSecretSignatureSigned] = useState(false); + const hasSubmittedRef = useRef(false); + + const formValues = form.watch(); + useEffect(() => { + hasSubmittedRef.current = false; + }, [formValues]); + + const ceremonyTimeFormValue = formValues.ceremonyTime; + const sendToDb = useMemo(() => { + return ( + typeof ceremonyTimeFormValue !== "undefined" && + ceremonyTimeFormValue !== "" && + ceremonyTimeFormValue !== "noPassportCeremony" + ); + }, [ceremonyTimeFormValue]); + useKonamiCode(() => { setSecretOptionsEnabled(true); alert("Secret options enabled 👀✨"); // TODO: do this more elegantly @@ -194,7 +194,6 @@ export default function Playground({ placeOfOrigin: string; dateOfBirth: string; image: File; - sendToDb: boolean; ceremonyTime?: string | undefined; passportNumber?: string | undefined; }) { @@ -206,7 +205,6 @@ export default function Playground({ ceremonyTime: formValues.ceremonyTime, image: formValues.image, passportNumber: formValues.passportNumber, - sendToDb: formValues.sendToDb, }; const validationResult = FormSchema.safeParse(formData); @@ -261,7 +259,7 @@ export default function Playground({ apiFormData.append("userId", userId); } - if (data.sendToDb) { + if (sendToDb) { const { passportNumber } = await createPassport(apiFormData); generatedPassportNumber = String(passportNumber); apiFormData.set("passportNumber", String(passportNumber)); @@ -279,7 +277,7 @@ export default function Playground({ }); updateGenerationStepState("generating_data_page", "completed"); - if (data.sendToDb) { + if (sendToDb) { const generateFullFrameReq = await fetch(`/api/generate-full-frame`, { method: "POST", body: apiFormData, @@ -326,6 +324,8 @@ export default function Playground({ setIsDefaultImage(false); resetGenerationSteps(); setLaunchConfetti(true); + + hasSubmittedRef.current = true; } return ( @@ -478,123 +478,80 @@ export default function Playground({ page into a real-life passport! :D

- If you intend to sign up for a passport-making ceremony, you{" "} - must click this switch and - select an available time. + If you want to sign up for a passport-making ceremony, you{" "} + must select an available time + below. If you don’t select a time, your passport won’t be + registered!

- If there are no open times or the next ceremony is full, you - can’t register this passport yet. Check the Discord server for - information on when the next passport ceremony will run. + If you just want to play around without registering, that’s ok + too :) Also you can replace your data page anytime—just make + sure to re-register every time you want to send a new data + page over.

( - - Register this passport - - { - field.onChange(e); - }} - checked={field.value} - className="" - /> - - - {renderError("sendToDb")} - - )} - /> - {form.getValues("sendToDb") ? ( - -
- ( - - - Ceremony Date - - - - - - - - Upcoming Ceremonies - - - { - field.onChange(getCeremonyTimestamp(e)); - setCeremonyTime(e); - { - if (e == "noPassportCeremony") { - setGenerationSteps( - GENERATION_STEPS.base, - ); - } else { - setGenerationSteps( - GENERATION_STEPS.register, - ); - } - } - }} - > - - Select a Date - - - - - - - - {renderError("ceremonyTime")} - -
-
- )} - /> -
- ) : ( - ( + + Ceremony Date - + + + + + + + Upcoming Ceremonies + + + { + field.onChange(getCeremonyTimestamp(e)); + setCeremonyTime(e); + { + if (e == "noPassportCeremony") { + setGenerationSteps(GENERATION_STEPS.base); + } else { + setGenerationSteps( + GENERATION_STEPS.register, + ); + } + } + }} + > + + Select a Date + + + + + {renderError("ceremonyTime")} - )} - /> - )} + + )} + /> ) : ( @@ -643,7 +600,7 @@ export default function Playground({ )} - {form.getValues("sendToDb") ? ( + {sendToDb ? ( <>