The form is built once to the settled field set, and a valid send is saved before anyone is emailed. Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
2.0 KiB
TypeScript
76 lines
2.0 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { apiRoute } from "../../../../lib/server/apiRoute";
|
|
import { isDatabaseConfigured } from "../../../../lib/server/env";
|
|
import { prisma } from "../../../../lib/server/db";
|
|
import { dbUnavailable, notFound, unauthorized } from "../../../../lib/server/responses";
|
|
import { isWorkshopInquiryAdmin } from "../../../../lib/server/workshopInquiryAdmin";
|
|
|
|
function csvCell(value: string): string {
|
|
if (/[",\n]/.test(value)) return `"${value.replace(/"/g, '""')}"`;
|
|
return value;
|
|
}
|
|
|
|
export const GET = apiRoute("workshop-inquiries.export", async (_request: NextRequest) => {
|
|
if (!(await isWorkshopInquiryAdmin())) {
|
|
return process.env.WORKSHOP_INQUIRY_ADMIN_TOKEN?.trim()
|
|
? unauthorized()
|
|
: notFound();
|
|
}
|
|
if (!isDatabaseConfigured()) return dbUnavailable();
|
|
|
|
const rows = await prisma.workshopInquiry.findMany({
|
|
orderBy: { createdAt: "desc" },
|
|
});
|
|
|
|
const header = [
|
|
"id",
|
|
"createdAt",
|
|
"organizationName",
|
|
"organizationKind",
|
|
"memberCount",
|
|
"largeOrganization",
|
|
"routeSuggestion",
|
|
"contactName",
|
|
"email",
|
|
"role",
|
|
"challenges",
|
|
"challengeOther",
|
|
"unspokenQuestion",
|
|
"sourcePage",
|
|
"sourceUrl",
|
|
];
|
|
|
|
const lines = [header.join(",")];
|
|
for (const row of rows) {
|
|
lines.push(
|
|
[
|
|
row.id,
|
|
row.createdAt.toISOString(),
|
|
row.organizationName,
|
|
row.organizationKind,
|
|
row.memberCount,
|
|
row.largeOrganization ? "yes" : "no",
|
|
row.routeSuggestion,
|
|
row.contactName,
|
|
row.email,
|
|
row.role ?? "",
|
|
row.challenges.join("; "),
|
|
row.challengeOther ?? "",
|
|
row.unspokenQuestion ?? "",
|
|
row.sourcePage,
|
|
row.sourceUrl ?? "",
|
|
]
|
|
.map((cell) => csvCell(String(cell)))
|
|
.join(","),
|
|
);
|
|
}
|
|
|
|
return new NextResponse(lines.join("\n"), {
|
|
status: 200,
|
|
headers: {
|
|
"content-type": "text/csv; charset=utf-8",
|
|
"content-disposition": "attachment; filename=\"workshop-inquiries.csv\"",
|
|
},
|
|
});
|
|
});
|