refactor(forms): isolate submission persistence
This commit is contained in:
+10
-110
@@ -1,8 +1,8 @@
|
||||
"use server";
|
||||
|
||||
import { db } from "@/db";
|
||||
import { submissions, answers } from "@/db/schema";
|
||||
import { and, count, eq, lt, lte, or } from "drizzle-orm";
|
||||
import { submissions } from "@/db/schema";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth/auth-options";
|
||||
@@ -11,8 +11,8 @@ import { sse } from "@/lib/realtime/sse";
|
||||
import type { FormAnswerPayload } from "@/lib/form/form-answer-values";
|
||||
import {
|
||||
normalizeSubmissionAnswers,
|
||||
type StoredAnswerSnapshot,
|
||||
} from "@/lib/form/submission-intake";
|
||||
import { writeSubmission } from "@/lib/form/submission-write";
|
||||
import { submissionWebhookMessage } from "@/lib/form/submission-webhook";
|
||||
import { isFormAcceptingSubmissions } from "@/lib/form/form-availability";
|
||||
|
||||
@@ -64,113 +64,13 @@ export async function submitForm(
|
||||
}
|
||||
}
|
||||
|
||||
const result = await db.transaction(async (tx) => {
|
||||
const existingSubmission = form.allowMultipleSubmissions
|
||||
? targetSubmissionId
|
||||
? await tx.query.submissions.findFirst({
|
||||
where: (submission, { eq, and }) =>
|
||||
and(
|
||||
eq(submission.id, targetSubmissionId),
|
||||
eq(submission.formId, formId),
|
||||
eq(submission.userDiscordId, discordId)
|
||||
),
|
||||
})
|
||||
: null
|
||||
: await tx.query.submissions.findFirst({
|
||||
where: (submission, { eq, and }) =>
|
||||
and(eq(submission.formId, formId), eq(submission.userDiscordId, discordId)),
|
||||
});
|
||||
|
||||
if (form.allowMultipleSubmissions && targetSubmissionId && !existingSubmission) {
|
||||
throw new Error("ไม่พบคำตอบที่ต้องการแก้ไข");
|
||||
}
|
||||
|
||||
let submissionId = "";
|
||||
let submittedAt = new Date();
|
||||
const isUpdate = Boolean(existingSubmission);
|
||||
let oldAnswersDict: Record<string, StoredAnswerSnapshot> = {};
|
||||
|
||||
if (existingSubmission) {
|
||||
submissionId = existingSubmission.id;
|
||||
submittedAt = existingSubmission.submittedAt;
|
||||
|
||||
const oldAnswersList = await tx.query.answers.findMany({
|
||||
where: (answer, { eq }) => eq(answer.submissionId, submissionId),
|
||||
});
|
||||
|
||||
oldAnswersDict = oldAnswersList.reduce<Record<string, StoredAnswerSnapshot>>((acc, curr) => {
|
||||
acc[curr.questionId] = {
|
||||
value: curr.value,
|
||||
imageCdnId: curr.imageCdnId ?? null,
|
||||
};
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const history = Array.isArray(existingSubmission.editHistory)
|
||||
? [...existingSubmission.editHistory]
|
||||
: [];
|
||||
history.push({
|
||||
editedAt: new Date().toISOString(),
|
||||
oldAnswers: oldAnswersDict,
|
||||
});
|
||||
|
||||
await tx
|
||||
.update(submissions)
|
||||
.set({ editHistory: history })
|
||||
.where(eq(submissions.id, submissionId));
|
||||
|
||||
await tx.delete(answers).where(eq(answers.submissionId, submissionId));
|
||||
} else {
|
||||
const [inserted] = await tx
|
||||
.insert(submissions)
|
||||
.values({
|
||||
formId,
|
||||
userDiscordId: discordId,
|
||||
userName,
|
||||
})
|
||||
.returning();
|
||||
submissionId = inserted.id;
|
||||
submittedAt = inserted.submittedAt;
|
||||
}
|
||||
|
||||
if (normalizedAnswers.length > 0) {
|
||||
await tx.insert(answers).values(
|
||||
normalizedAnswers.map((answer) => ({
|
||||
submissionId,
|
||||
questionId: answer.questionId,
|
||||
value: answer.value,
|
||||
imageCdnId: answer.imageCdnId,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
const [submissionStats] = await tx
|
||||
.select({ totalAnswers: count() })
|
||||
.from(submissions)
|
||||
.where(eq(submissions.formId, formId));
|
||||
const [queueStats] = await tx
|
||||
.select({ queueNumber: count() })
|
||||
.from(submissions)
|
||||
.where(
|
||||
and(
|
||||
eq(submissions.formId, formId),
|
||||
or(
|
||||
lt(submissions.submittedAt, submittedAt),
|
||||
and(
|
||||
eq(submissions.submittedAt, submittedAt),
|
||||
lte(submissions.id, submissionId)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
submissionId,
|
||||
isUpdate,
|
||||
oldAnswersDict,
|
||||
totalAnswers: submissionStats.totalAnswers,
|
||||
queueNumber: queueStats.queueNumber,
|
||||
};
|
||||
const result = await writeSubmission({
|
||||
formId,
|
||||
discordId,
|
||||
userName,
|
||||
allowMultipleSubmissions: form.allowMultipleSubmissions,
|
||||
normalizedAnswers,
|
||||
targetSubmissionId,
|
||||
});
|
||||
|
||||
// Send Discord Webhook
|
||||
|
||||
+5
-2
@@ -59,6 +59,7 @@ import {
|
||||
import { isImageOnlyResponse } from "@/lib/form/question-response-type";
|
||||
import {
|
||||
buildFormSchema,
|
||||
formatQueuePosition,
|
||||
parseStoredDraft,
|
||||
prepareInitialState,
|
||||
type FormFillerClientProps,
|
||||
@@ -451,7 +452,9 @@ export function FormFillerClient({
|
||||
<span>{questions.length} คำถาม</span>
|
||||
<span>{totalAnswers} คำตอบทั้งหมด</span>
|
||||
{queueNumber !== null && (
|
||||
<Badge variant="outline">ลำดับของคุณ #{queueNumber + 1}</Badge>
|
||||
<Badge variant="outline">
|
||||
ลำดับของคุณ {formatQueuePosition(queueNumber)}
|
||||
</Badge>
|
||||
)}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
@@ -996,7 +999,7 @@ export function FormFillerClient({
|
||||
ส่งคำตอบแล้ว
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
บันทึกคำตอบของคุณเรียบร้อย คำตอบของคุณอยู่ลำดับที่ #{(queueNumber ?? 0) + 1} จากทั้งหมด{" "}
|
||||
บันทึกคำตอบของคุณเรียบร้อย คำตอบของคุณอยู่ลำดับที่ {formatQueuePosition(queueNumber ?? 1)} จากทั้งหมด{" "}
|
||||
{totalAnswers} คำตอบ คุณสามารถแก้ไขได้คำตอบตอนไหนก็ได้ที่แบบฟอร์มยังเปิดอยู่
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
|
||||
Reference in New Issue
Block a user