From 6a36965e8b06ac8497e79a1dbdcb5f81f60a761c Mon Sep 17 00:00:00 2001 From: Gunshiz Date: Sat, 30 May 2026 01:35:33 +0700 Subject: [PATCH] feat: add allowOther field to questions and update image upload to return data URIs --- app/actions/questions.ts | 4 + app/api/upload/route.ts | 10 +- app/form/admin/client.tsx | 35 +- app/form/admin/result/[id]/client.tsx | 10 +- app/form/client.tsx | 102 ++++- db/schema/form.ts | 1 + drizzle/0006_concerned_mimic.sql | 1 + drizzle/meta/0006_snapshot.json | 544 ++++++++++++++++++++++++++ drizzle/meta/_journal.json | 7 + nginx.conf | 2 +- 10 files changed, 689 insertions(+), 27 deletions(-) create mode 100644 drizzle/0006_concerned_mimic.sql create mode 100644 drizzle/meta/0006_snapshot.json diff --git a/app/actions/questions.ts b/app/actions/questions.ts index 2ebc9eb..39e7f3f 100644 --- a/app/actions/questions.ts +++ b/app/actions/questions.ts @@ -37,6 +37,7 @@ export async function createQuestion( required: data.required, displayOrder: data.displayOrder, options: data.options ?? [], + allowOther: false, }); revalidatePath("/form"); revalidatePath("/form/admin"); @@ -52,6 +53,7 @@ export async function updateQuestion( displayOrder: number; options: string[]; imageUrl: string | null; + allowOther: boolean; }> ) { await assertAdmin(); @@ -77,6 +79,7 @@ export async function bulkUpdateQuestions( required: boolean; displayOrder: number; options: string[]; + allowOther: boolean; }[] ) { await assertAdmin(); @@ -92,6 +95,7 @@ export async function bulkUpdateQuestions( required: u.required, displayOrder: u.displayOrder, options: u.options ?? [], + allowOther: u.allowOther, }) .where(eq(questions.id, u.id)) ) diff --git a/app/api/upload/route.ts b/app/api/upload/route.ts index 9ea768c..82d19b4 100644 --- a/app/api/upload/route.ts +++ b/app/api/upload/route.ts @@ -36,14 +36,12 @@ export async function POST(req: Request) { return NextResponse.json({ error: "Invalid image extension" }, { status: 400 }); } - const filename = `${crypto.randomUUID()}.${originalExt}`; - const uploadDir = path.join(process.cwd(), "public", "form"); - - await mkdir(uploadDir, { recursive: true }); const buffer = Buffer.from(await file.arrayBuffer()); - await writeFile(path.join(uploadDir, filename), buffer); + const base64 = buffer.toString("base64"); + const mimeType = file.type; + const dataUri = `data:${mimeType};base64,${base64}`; - return NextResponse.json({ url: `/form/${filename}` }); + return NextResponse.json({ url: dataUri }); } catch (error: any) { console.error("Upload error:", error); return NextResponse.json({ error: error.message || "Internal server error" }, { status: 500 }); diff --git a/app/form/admin/client.tsx b/app/form/admin/client.tsx index 78a65af..cfb4253 100644 --- a/app/form/admin/client.tsx +++ b/app/form/admin/client.tsx @@ -31,6 +31,7 @@ interface Question { displayOrder: number; required: boolean; options: string[] | null; + allowOther: boolean; } interface FormEditorClientProps { @@ -102,6 +103,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC const formattedUpdates = questions.map(q => ({ ...q, options: q.options === null ? [] : q.options, + allowOther: q.allowOther, })); await bulkUpdateQuestions(form.id, formattedUpdates); toast.success("Questions saved!"); @@ -338,6 +340,7 @@ export default function FormEditorClient({ form, initialQuestions }: FormEditorC height={200} src={q.imageUrl} alt={q.label} + unoptimized className="w-32 h-auto rounded-md border object-contain" />