diff --git a/app/auth/sign-in/page.tsx b/app/auth/sign-in/page.tsx index 58809e1d..a514102e 100644 --- a/app/auth/sign-in/page.tsx +++ b/app/auth/sign-in/page.tsx @@ -1,80 +1,125 @@ // app/auth/sign-in/page.tsx +import { Suspense } from "react"; +import type { Metadata } from "next"; +import Link from "next/link"; + +// UI (shadcn) +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; + +export const metadata: Metadata = { title: "Sign in" }; + +// Server Component wrapper — no hooks here +export default function SignInPage() { + return ( +
+
+
+

Sign in

+

+ Welcome back. Enter your credentials to continue. +

+
+ + {/* Client sub-component wrapped in Suspense so useSearchParams is allowed */} + Loading…
}> + + + +

+ Don’t have an account?{" "} + + Create one + +

+
+ + ); +} + +// ───────────────────────────────────────────────────────────── +// Client piece that actually uses useSearchParams/router/fetch +// ───────────────────────────────────────────────────────────── "use client"; import { useRouter, useSearchParams } from "next/navigation"; import { useState } from "react"; -export default function SignInPage() { +function SignInClient() { const router = useRouter(); - const search = useSearchParams(); - const nextUrl = search.get("next") || "/my/rigs"; + const sp = useSearchParams(); - const [identity, setIdentity] = useState(""); - const [password, setPassword] = useState(""); - const [busy, setBusy] = useState(false); + const [idVal, setIdVal] = useState(""); + const [pwVal, setPwVal] = useState(""); + const [submitting, setSubmitting] = useState(false); + const [err, setErr] = useState(null); - async function onSubmit(e: React.FormEvent) { + const next = sp.get("next") || "/my/rigs"; + + async function onSubmit(e: React.FormEvent) { e.preventDefault(); - setBusy(true); + setErr(null); + setSubmitting(true); try { + // Post both a generic "identity" and the same value under email/username + // so the API route can accept any of them. const res = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, - credentials: "include", // ensure Set-Cookie is honored everywhere - body: JSON.stringify({ identity, password }), + body: JSON.stringify({ + identity: idVal, + email: idVal, + username: idVal, + password: pwVal, + }), }); - const j = await res.json().catch(() => null); - if (!res.ok) throw new Error(j?.error || "Login failed"); - router.replace(nextUrl); - } catch (err) { - alert((err as Error).message); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error || "Login failed"); + + // Cookies (httpOnly) were set server-side; client just navigates. + router.replace(next); + } catch (e: any) { + setErr(e?.message || "Login failed"); } finally { - setBusy(false); + setSubmitting(false); } } return ( -
-

Sign in

-
-
- - setIdentity(e.target.value)} + + {err && ( +
+ {err} +
+ )} +
+ + setIdVal(e.target.value)} required />
-
- - + + setPassword(e.target.value)} autoComplete="current-password" + value={pwVal} + onChange={(e) => setPwVal(e.target.value)} required />
- - -

- Don’t have an account?{" "} - - Sign up - -

-
+ + + {/* keep the next param visible to the client sub-component */} + + ); } diff --git a/app/auth/sign-up/page.tsx b/app/auth/sign-up/page.tsx index d6ebab34..da6b8a5a 100644 --- a/app/auth/sign-up/page.tsx +++ b/app/auth/sign-up/page.tsx @@ -1,91 +1,134 @@ -// app/app/auth/sign-up/page.tsx -"use client"; +// app/auth/sign-up/page.tsx +import { Suspense } from "react"; +import type { Metadata } from "next"; +import Link from "next/link"; -import { useState } from "react"; -import { useRouter } from "next/navigation"; +// UI (shadcn) +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; + +export const metadata: Metadata = { title: "Create account" }; export default function SignUpPage() { - const r = useRouter(); - const [form, setForm] = useState({ username: "", email: "", password: "", agree: false }); - const [busy, setBusy] = useState(false); - const [err, setErr] = useState(null); - const canSubmit = form.username.length >= 3 && form.password.length >= 8 && form.agree && !busy; + return ( +
+
+
+

Create account

+

+ Pick a username and password. Email is optional (recommended for password reset). +

+
- async function submit() { - setBusy(true); setErr(null); + Loading…
}> + + + +

+ Already have an account?{" "} + + Sign in + +

+
+
+ ); +} + +"use client"; + +import { useRouter, useSearchParams } from "next/navigation"; +import { useState } from "react"; + +function SignUpClient() { + const router = useRouter(); + const sp = useSearchParams(); + + const [username, setUsername] = useState(""); + const [email, setEmail] = useState(""); // optional + const [password, setPassword] = useState(""); + const [submitting, setSubmitting] = useState(false); + const [err, setErr] = useState(null); + + const next = sp.get("next") || "/my/rigs"; + + async function onSubmit(e: React.FormEvent) { + e.preventDefault(); + setErr(null); + setSubmitting(true); try { const res = await fetch("/api/auth/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ - username: form.username.trim(), - email: form.email.trim() || undefined, - password: form.password, + username, + email: email || undefined, + password, }), }); - const j = await res.json(); - if (!res.ok) throw new Error(j?.error || "Sign up failed"); + const data = await res.json().catch(() => ({})); + if (!res.ok) throw new Error(data?.error || "Registration failed"); - // Auto-login right after register - const login = await fetch("/api/auth/login", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ identifier: form.email.trim() || form.username.trim(), password: form.password }), - }); - const lj = await login.json(); - if (!login.ok) throw new Error(lj?.error || "Auto login failed"); - - r.replace("/my/rigs"); // or wherever you want to land + // Registration API should already log user in (sets cookies), then redirect + router.replace(next); } catch (e: any) { - setErr(e?.message || "Error"); + setErr(e?.message || "Registration failed"); } finally { - setBusy(false); + setSubmitting(false); } } return ( -
-

Create Account

+
+ {err && ( +
+ {err} +
+ )} -