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.
- Nice Passport! Want to make it real? Register for an upcoming
- passport ceremony at step 1.
+ Nice Passport! Want to make it real? Select the next available
+ passport ceremony time at the “Register” step!