diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx
index 2818994..1a53226 100644
--- a/src/app/(auth)/login/page.tsx
+++ b/src/app/(auth)/login/page.tsx
@@ -1,7 +1,7 @@
"use client";
-import { useState } from "react";
-import { useRouter } from "next/navigation";
+import { Suspense, useState } from "react";
+import { useRouter, useSearchParams } from "next/navigation";
import Link from "next/link";
import { createClient } from "@/lib/supabase/client";
import { Button } from "@/components/ui/button";
@@ -14,9 +14,28 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
-import { MessageSquare } from "lucide-react";
+import { MessageSquare, UsersRound } from "lucide-react";
+// `useSearchParams` opts the component out of static prerendering
+// unless it sits under a Suspense boundary. We split the form into
+// a child component so the outer page can prerender the chrome
+// (background, card frame) while the form hydrates with the query
+// string on the client.
export default function LoginPage() {
+ return (
+
+
+
+ );
+}
+
+function LoginPageInner() {
+ const searchParams = useSearchParams();
+ // Forwarded from `/join/` when the visitor already has an
+ // account. After a successful sign-in we send them to the join
+ // page to accept rather than to /dashboard.
+ const inviteToken = searchParams.get("invite");
+
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState(null);
@@ -40,7 +59,11 @@ export default function LoginPage() {
return;
}
- router.push("/dashboard");
+ if (inviteToken) {
+ router.push(`/join/${encodeURIComponent(inviteToken)}`);
+ } else {
+ router.push("/dashboard");
+ }
};
return (
@@ -48,11 +71,19 @@ export default function LoginPage() {
-
+ {inviteToken ? (
+
+ ) : (
+
+ )}
- Welcome back
+
+ {inviteToken ? "Sign in to accept" : "Welcome back"}
+
- Sign in to your account
+ {inviteToken
+ ? "Sign in and we'll take you to the invitation."
+ : "Sign in to your account"}
@@ -113,7 +144,11 @@ export default function LoginPage() {
Don't have an account?{" "}
Create account
diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx
index 8683d47..c431fe9 100644
--- a/src/app/(auth)/signup/page.tsx
+++ b/src/app/(auth)/signup/page.tsx
@@ -1,7 +1,8 @@
"use client";
-import { useState } from "react";
+import { Suspense, useState } from "react";
import Link from "next/link";
+import { useSearchParams } from "next/navigation";
import { createClient } from "@/lib/supabase/client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -13,9 +14,27 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
-import { MessageSquare, CheckCircle } from "lucide-react";
+import { MessageSquare, CheckCircle, UsersRound } from "lucide-react";
+// `useSearchParams` opts the component out of static prerendering
+// unless wrapped in Suspense — same pattern as /login.
export default function SignupPage() {
+ return (
+
+
+
+ );
+}
+
+function SignupPageInner() {
+ const searchParams = useSearchParams();
+ // When the user lands here from `/join/` we carry the
+ // invite token in the query so it survives the signup → email
+ // verification → redirect round-trip. `emailRedirectTo` below
+ // points back at /join/ so the user lands on the redeem
+ // step after verifying instead of being dropped on /dashboard.
+ const inviteToken = searchParams.get("invite");
+
const [fullName, setFullName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
@@ -41,6 +60,14 @@ export default function SignupPage() {
setLoading(true);
+ // If we have an invite token, point Supabase's verification
+ // email back at the join page so the user can accept after
+ // verifying. Without a token, Supabase uses its default
+ // redirect (the app root).
+ const emailRedirectTo = inviteToken
+ ? `${window.location.origin}/join/${encodeURIComponent(inviteToken)}`
+ : undefined;
+
const { error } = await supabase.auth.signUp({
email,
password,
@@ -48,6 +75,7 @@ export default function SignupPage() {
data: {
full_name: fullName,
},
+ ...(emailRedirectTo ? { emailRedirectTo } : {}),
},
});
@@ -79,7 +107,13 @@ export default function SignupPage() {
-
+