From 538b06bd4b6cfe18509d23dd00d63694474e96e0 Mon Sep 17 00:00:00 2001 From: mdares Date: Sun, 4 Jan 2026 00:53:00 +0000 Subject: [PATCH] In theory all done expect En|es and light/dark --- app/invite/[token]/InviteAcceptForm.tsx | 32 ++++++++++++++---- app/invite/[token]/page.tsx | 44 +++++++++++++++++++++++-- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/app/invite/[token]/InviteAcceptForm.tsx b/app/invite/[token]/InviteAcceptForm.tsx index 4798f5c..59b3bad 100644 --- a/app/invite/[token]/InviteAcceptForm.tsx +++ b/app/invite/[token]/InviteAcceptForm.tsx @@ -10,22 +10,40 @@ type InviteInfo = { expiresAt: string; }; -export default function InviteAcceptForm({ token }: { token: string }) { +type InviteAcceptFormProps = { + token: string; + initialInvite?: InviteInfo | null; + initialError?: string | null; +}; + +export default function InviteAcceptForm({ + token, + initialInvite = null, + initialError = null, +}: InviteAcceptFormProps) { const router = useRouter(); - const [invite, setInvite] = useState(null); - const [loading, setLoading] = useState(true); + const cleanedToken = token.trim(); + const [invite, setInvite] = useState(initialInvite); + const [loading, setLoading] = useState(!initialInvite && !initialError); const [submitting, setSubmitting] = useState(false); - const [error, setError] = useState(null); + const [error, setError] = useState(initialError); const [name, setName] = useState(""); const [password, setPassword] = useState(""); useEffect(() => { + if (initialInvite || initialError) { + setLoading(false); + return; + } + let alive = true; async function loadInvite() { setLoading(true); setError(null); try { - const res = await fetch(`/api/invites/${token}`, { cache: "no-store" }); + const res = await fetch(`/api/invites/${encodeURIComponent(cleanedToken)}`, { + cache: "no-store", + }); const data = await res.json().catch(() => ({})); if (!res.ok || !data.ok) { throw new Error(data.error || "Invite not found"); @@ -42,14 +60,14 @@ export default function InviteAcceptForm({ token }: { token: string }) { return () => { alive = false; }; - }, [token]); + }, [cleanedToken, initialInvite, initialError]); async function onSubmit(e: React.FormEvent) { e.preventDefault(); setError(null); setSubmitting(true); try { - const res = await fetch(`/api/invites/${token}`, { + const res = await fetch(`/api/invites/${encodeURIComponent(cleanedToken)}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ name, password }), diff --git a/app/invite/[token]/page.tsx b/app/invite/[token]/page.tsx index 429dba9..8554941 100644 --- a/app/invite/[token]/page.tsx +++ b/app/invite/[token]/page.tsx @@ -1,12 +1,52 @@ import { cookies } from "next/headers"; import { redirect } from "next/navigation"; +import { prisma } from "@/lib/prisma"; import InviteAcceptForm from "./InviteAcceptForm"; -export default async function InvitePage({ params }: { params: { token: string } }) { +export default async function InvitePage({ params }: { params: { token: string } | Promise<{ token: string }> }) { const session = (await cookies()).get("mis_session")?.value; if (session) { redirect("/machines"); } - return ; + const resolvedParams = await Promise.resolve(params); + const token = String(resolvedParams?.token || "").trim().toLowerCase(); + let invite = null; + let error: string | null = null; + + if (!token) { + error = "Invite not found"; + } else { + invite = await prisma.orgInvite.findFirst({ + where: { + token, + revokedAt: null, + acceptedAt: null, + expiresAt: { gt: new Date() }, + }, + include: { + org: { select: { id: true, name: true, slug: true } }, + }, + }); + if (!invite) { + error = "Invite not found"; + } + } + + return ( + + ); }