Add the Workshops page and keep each sign-up on the site.
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>
This commit is contained in:
1 parent
974a5ace4c
commit
c0ba3a3798
50 files changed
+2773
-1
No files matched your search
@@ -0,0 +1,114 @@
|
||||
import type { Metadata } from "next";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
import messages from "../../../messages/en/index";
|
||||
import { routeMetadata } from "../../../lib/siteMetadata";
|
||||
import { isDatabaseConfigured } from "../../../lib/server/env";
|
||||
import { prisma } from "../../../lib/server/db";
|
||||
import {
|
||||
grantWorkshopInquiryAdmin,
|
||||
isWorkshopInquiryAdmin,
|
||||
workshopInquiryAdminToken,
|
||||
} from "../../../lib/server/workshopInquiryAdmin";
|
||||
|
||||
export const metadata: Metadata = routeMetadata("/workshop-inquiries", {
|
||||
title: messages.metadata.workshopInquiries.title,
|
||||
});
|
||||
|
||||
async function unlock(formData: FormData) {
|
||||
"use server";
|
||||
const password = String(formData.get("password") ?? "");
|
||||
const ok = await grantWorkshopInquiryAdmin(password);
|
||||
redirect(ok ? "/workshop-inquiries" : "/workshop-inquiries?denied=1");
|
||||
}
|
||||
|
||||
export default async function WorkshopInquiriesPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ denied?: string }>;
|
||||
}) {
|
||||
if (!workshopInquiryAdminToken()) notFound();
|
||||
|
||||
const copy = messages.pages.workshopInquiries;
|
||||
const params = await searchParams;
|
||||
const allowed = await isWorkshopInquiryAdmin();
|
||||
|
||||
if (!allowed) {
|
||||
return (
|
||||
<section className="mx-auto flex w-full max-w-md flex-col gap-4 px-6 py-16">
|
||||
<h1 className="x-large-heading text-[var(--color-content-default-primary)]">{copy.title}</h1>
|
||||
<form action={unlock} className="flex flex-col gap-3">
|
||||
<label className="medium-label" htmlFor="workshop-inquiry-token">
|
||||
{copy.unlockLabel}
|
||||
</label>
|
||||
<input
|
||||
id="workshop-inquiry-token"
|
||||
name="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
className="h-12 rounded-[12px] border border-[var(--color-border-default-tertiary)] bg-black px-4"
|
||||
/>
|
||||
{params.denied ? (
|
||||
<p className="small-paragraph text-[var(--color-content-default-utility-negative)]">
|
||||
{copy.unlockError}
|
||||
</p>
|
||||
) : null}
|
||||
<button
|
||||
type="submit"
|
||||
className="h-12 rounded-full bg-white px-4 font-medium text-black"
|
||||
>
|
||||
{copy.unlockButton}
|
||||
</button>
|
||||
</form>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const rows = isDatabaseConfigured()
|
||||
? await prisma.workshopInquiry.findMany({ orderBy: { createdAt: "desc" }, take: 200 })
|
||||
: [];
|
||||
|
||||
return (
|
||||
<section className="mx-auto flex w-full max-w-6xl flex-col gap-6 px-6 py-12">
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<h1 className="x-large-heading text-[var(--color-content-default-primary)]">{copy.title}</h1>
|
||||
<a href="/api/workshop-inquiries/export" className="medium-label underline">
|
||||
{copy.export}
|
||||
</a>
|
||||
</div>
|
||||
{rows.length === 0 ? (
|
||||
<p className="medium-paragraph">{copy.empty}</p>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full border-collapse text-left text-[14px]">
|
||||
<thead>
|
||||
<tr className="border-b border-[var(--color-border-default-secondary)]">
|
||||
<th className="py-2 pr-4">{copy.columns.when}</th>
|
||||
<th className="py-2 pr-4">{copy.columns.organization}</th>
|
||||
<th className="py-2 pr-4">{copy.columns.kind}</th>
|
||||
<th className="py-2 pr-4">{copy.columns.members}</th>
|
||||
<th className="py-2 pr-4">{copy.columns.route}</th>
|
||||
<th className="py-2 pr-4">{copy.columns.contact}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{rows.map((row) => (
|
||||
<tr key={row.id} className="border-b border-[var(--color-border-default-secondary)] align-top">
|
||||
<td className="py-3 pr-4 whitespace-nowrap">{row.createdAt.toISOString().slice(0, 10)}</td>
|
||||
<td className="py-3 pr-4">{row.organizationName}</td>
|
||||
<td className="py-3 pr-4">{row.organizationKind}</td>
|
||||
<td className="py-3 pr-4">{row.memberCount}</td>
|
||||
<td className="py-3 pr-4">{row.routeSuggestion}</td>
|
||||
<td className="py-3 pr-4">
|
||||
{row.contactName}
|
||||
<br />
|
||||
{row.email}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user