my-fullstack-ai-platform/app/onboarding/page.tsx

100 lines
3.3 KiB
TypeScript

import { createClient } from "@/lib/supabase/server";
import { redirect } from "next/navigation";
import { createClient as createAdminClient } from "@supabase/supabase-js";
import { cookies } from "next/headers";
import { createOrganization } from "./actions";
import { Suspense } from "react";
async function OnboardingContent() {
const supabase = await createClient();
const { data } = await supabase.auth.getUser();
if (!data?.user) {
redirect("/auth/login");
}
// If already in an org, redirect to dashboard
const { data: profile } = await supabase
.from("profiles")
.select("organization_id")
.eq("id", data.user.id)
.single();
if (profile?.organization_id) {
redirect("/dashboard");
}
// Check for invite token cookie
const cookieStore = await cookies();
const token = cookieStore.get("invite_token")?.value;
if (token) {
const supabaseAdmin = createAdminClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
// Attempt to redeem it automatically
const { data: invite } = await supabaseAdmin
.from("invitations")
.select("*")
.eq("token", token)
.eq("status", "pending")
.single();
if (invite && new Date(invite.expires_at) > new Date()) {
await supabase.from("profiles").update({ organization_id: invite.organization_id, role: "member" }).eq("id", data.user.id);
await supabaseAdmin.from("invitations").update({ status: "accepted" }).eq("id", invite.id);
// Optional: Clear the cookie, but next redirect will naturally ignore it since organization_id is set
redirect("/dashboard");
}
}
return (
<div className="flex h-screen items-center justify-center bg-background text-foreground">
<div className="w-full max-w-md p-8 bg-card rounded-lg border border-border shadow-sm">
<div className="mb-6 text-center">
<h1 className="text-2xl font-semibold mb-2">Welcome</h1>
<p className="text-sm text-muted-foreground">
Let&apos;s get started by creating an organization for your team.
</p>
</div>
<form action={createOrganization} className="space-y-4">
<div className="space-y-2">
<label htmlFor="name" className="text-sm font-medium">
Organization Name
</label>
<input
id="name"
name="name"
type="text"
required
placeholder="E.g. Acme Corp"
className="w-full px-3 py-2 bg-background border border-border rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-ring"
/>
</div>
<button
type="submit"
className="w-full bg-primary text-primary-foreground py-2 rounded-md text-sm font-medium hover:bg-primary/90 transition-colors"
>
Create Organization
</button>
</form>
<div className="mt-6 text-center text-sm text-muted-foreground">
Waiting for an invite? Ask your team owner to send you a link.
</div>
</div>
</div>
);
}
export default function OnboardingPage() {
return (
<Suspense fallback={<div className="flex h-screen items-center justify-center">Loading...</div>}>
<OnboardingContent />
</Suspense>
);
}