Skip to content

Commit 0e71e38

Browse files
Merge pull request #10 from Eric-Zhang-Developer/feat/routing-auth
Feat: Routing + Auth
2 parents 70a1b48 + e96bd70 commit 0e71e38

File tree

7 files changed

+297
-201
lines changed

7 files changed

+297
-201
lines changed

package-lock.json

Lines changed: 98 additions & 105 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,20 @@
1111
"dependencies": {
1212
"@supabase/ssr": "^0.7.0",
1313
"@supabase/supabase-js": "^2.75.0",
14+
"daisyui": "^5.3.7",
1415
"next": "15.5.3",
1516
"react": "19.1.0",
1617
"react-dom": "19.1.0"
1718
},
1819
"devDependencies": {
1920
"@eslint/eslintrc": "^3",
20-
"@tailwindcss/postcss": "^4",
21+
"@tailwindcss/postcss": "^4.1.14",
2122
"@types/node": "^20",
2223
"@types/react": "^19",
2324
"@types/react-dom": "^19",
2425
"eslint": "^9",
2526
"eslint-config-next": "15.5.3",
26-
"tailwindcss": "^4",
27+
"tailwindcss": "^4.1.14",
2728
"typescript": "^5"
2829
}
2930
}

public/Cq_logo.png

301 KB
Loading

src/app/dashboard/page.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { redirect } from "next/navigation";
2+
import Link from "next/link";
3+
import { createClient as createServerClient } from "../../lib/supabase/server";
4+
5+
async function logout() {
6+
"use server";
7+
const supabase = await createServerClient();
8+
await supabase.auth.signOut();
9+
redirect("/");
10+
}
11+
12+
export default async function DashboardPage() {
13+
const supabase = await createServerClient();
14+
const {
15+
data: { user },
16+
} = await supabase.auth.getUser();
17+
18+
if (!user) {
19+
redirect("/login");
20+
}
21+
22+
return (
23+
<main className="bg-white min-h-dvh p-8">
24+
<div className="flex items-center gap-4">
25+
<h1 className="text-black text-2xl font-semibold">Dashboard</h1>
26+
<Link href="/" className="btn btn-neutral btn-outline" aria-label="Go home">
27+
<span>Home</span>
28+
</Link>
29+
<form action={logout}>
30+
<button
31+
type="submit"
32+
className="btn btn-neutral btn-outline"
33+
>
34+
<span>Log out</span>
35+
</button>
36+
</form>
37+
</div>
38+
39+
40+
41+
<p className="mt-4 text-neutral-600">
42+
You're logged in as: <span className="font-bold">{user.email}</span>.
43+
</p>
44+
45+
</main>
46+
);
47+
}

src/app/globals.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
@import "tailwindcss";
2+
@plugin "daisyui";
23

34
:root {
45
--background: #ffffff;

src/app/login/page.tsx

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { useRouter } from "next/navigation";
5+
import { createClient } from "../../lib/supabase/client";
6+
7+
export default function AuthPage() {
8+
const router = useRouter();
9+
const supabase = createClient();
10+
11+
const [mode, setMode] = useState<"login" | "signup">("login");
12+
const [email, setEmail] = useState("");
13+
const [password, setPassword] = useState("");
14+
const [busy, setBusy] = useState(false);
15+
const [err, setErr] = useState<string | null>(null);
16+
17+
async function onSubmit(e: React.FormEvent) {
18+
e.preventDefault();
19+
setBusy(true);
20+
setErr(null);
21+
22+
try {
23+
if (mode === "login") {
24+
const { error } = await supabase.auth.signInWithPassword({ email, password });
25+
if (error) throw error;
26+
} else {
27+
const { error } = await supabase.auth.signUp({ email, password });
28+
if (error) throw error;
29+
}
30+
router.push("/dashboard");
31+
} catch (e: any) {
32+
setErr(e?.message ?? "Something went wrong");
33+
} finally {
34+
setBusy(false);
35+
}
36+
}
37+
38+
return (
39+
<main className="min-h-dvh flex items-center justify-center p-6 bg-gray-900">
40+
<div className="card w-full max-w-md bg-base-100 shadow-xl">
41+
<div className="card-body">
42+
{/* Tabs */}
43+
<div className="join w-full mb-4">
44+
<button
45+
type="button"
46+
onClick={() => setMode("login")}
47+
className={`join-item btn w-1/2 ${mode === "login" ? "btn-primary" : "btn-ghost"}`}
48+
>
49+
Login
50+
</button>
51+
<button
52+
type="button"
53+
onClick={() => setMode("signup")}
54+
className={`join-item btn w-1/2 ${mode === "signup" ? "btn-primary" : "btn-ghost"}`}
55+
>
56+
Sign up
57+
</button>
58+
</div>
59+
60+
{/* Optional info for signup */}
61+
{mode === "signup" && (
62+
<div role="alert" className="alert alert-soft mb-2">
63+
<span>
64+
Create an account with email & password.
65+
</span>
66+
</div>
67+
)}
68+
69+
{/* Error alert */}
70+
{err && (
71+
<div role="alert" className="alert alert-error mb-2">
72+
<span>{err}</span>
73+
</div>
74+
)}
75+
76+
{/* Form */}
77+
<form onSubmit={onSubmit} className="space-y-3">
78+
<div className="form-control">
79+
<label className="label">
80+
<span className="label-text">Email</span>
81+
</label>
82+
<input
83+
type="email"
84+
required
85+
value={email}
86+
onChange={(e) => setEmail(e.target.value)}
87+
placeholder="[email protected]"
88+
className="input input-bordered w-full"
89+
autoComplete="email"
90+
/>
91+
</div>
92+
93+
<div className="form-control">
94+
<label className="label">
95+
<span className="label-text">Password</span>
96+
</label>
97+
<input
98+
type="password"
99+
required
100+
value={password}
101+
onChange={(e) => setPassword(e.target.value)}
102+
placeholder="••••••••"
103+
className="input input-bordered w-full"
104+
autoComplete={mode === "login" ? "current-password" : "new-password"}
105+
/>
106+
</div>
107+
108+
<div className="card-actions mt-4">
109+
<button
110+
type="submit"
111+
disabled={busy}
112+
className={`btn btn-primary w-full ${busy ? "loading" : ""}`}
113+
>
114+
{mode === "login" ? "Log in" : "Create account"}
115+
</button>
116+
</div>
117+
</form>
118+
</div>
119+
</div>
120+
</main>
121+
);
122+
}

src/app/page.tsx

Lines changed: 26 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,35 @@
1+
import Link from "next/link";
12
import Image from "next/image";
3+
import "./globals.css";
24

3-
export default function Home() {
5+
export default function LandingPage() {
46
return (
5-
<div className="font-sans grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20">
6-
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
7-
<Image
8-
className="dark:invert"
9-
src="/next.svg"
10-
alt="Next.js logo"
11-
width={180}
12-
height={38}
13-
priority
14-
/>
15-
<ol className="font-mono list-inside list-decimal text-sm/6 text-center sm:text-left">
16-
<li className="mb-2 tracking-[-.01em]">
17-
Get started by editing{" "}
18-
<code className="bg-black/[.05] dark:bg-white/[.06] font-mono font-semibold px-1 py-0.5 rounded">
19-
src/app/page.tsx
20-
</code>
21-
.
22-
</li>
23-
<li className="tracking-[-.01em]">
24-
Save and see your changes instantly.
25-
</li>
26-
</ol>
27-
28-
<div className="flex gap-4 items-center flex-col sm:flex-row">
29-
<a
30-
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
31-
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
32-
target="_blank"
33-
rel="noopener noreferrer"
34-
>
7+
<main className="min-h-dvh bg-white flex flex-col items-center justify-center pt-2 pb-8 px-8">
8+
<div className="max-w-xl text-center">
9+
<div className="mb-8 flex justify-center">
10+
<div className="relative w-[500px] h-[100px] overflow-hidden rounded-lg">
3511
<Image
36-
className="dark:invert"
37-
src="/vercel.svg"
38-
alt="Vercel logomark"
39-
width={20}
40-
height={20}
12+
src="/Cq_logo.png"
13+
alt="CodeQuest Logo"
14+
fill
15+
className="object-cover object-center"
4116
/>
42-
Deploy now
43-
</a>
44-
<a
45-
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
46-
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
47-
target="_blank"
48-
rel="noopener noreferrer"
17+
</div>
18+
</div>
19+
<h1 className="text-4xl md:text-5xl font-bold text-neutral-600">Welcome to CodeQuest</h1>
20+
<p className="mt-3 text-neutral-600">
21+
Simple flow: Landing → Login/Signup → Dashboard
22+
</p>
23+
<div className="mt-6 flex items-center justify-center gap-3">
24+
25+
<Link
26+
href="/dashboard"
27+
className="btn btn-sof"
4928
>
50-
Read our docs
51-
</a>
29+
Go to dashboard
30+
</Link>
5231
</div>
53-
</main>
54-
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
55-
<a
56-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
57-
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
58-
target="_blank"
59-
rel="noopener noreferrer"
60-
>
61-
<Image
62-
aria-hidden
63-
src="/file.svg"
64-
alt="File icon"
65-
width={16}
66-
height={16}
67-
/>
68-
Learn
69-
</a>
70-
<a
71-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
72-
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
73-
target="_blank"
74-
rel="noopener noreferrer"
75-
>
76-
<Image
77-
aria-hidden
78-
src="/window.svg"
79-
alt="Window icon"
80-
width={16}
81-
height={16}
82-
/>
83-
Examples
84-
</a>
85-
<a
86-
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
87-
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
88-
target="_blank"
89-
rel="noopener noreferrer"
90-
>
91-
<Image
92-
aria-hidden
93-
src="/globe.svg"
94-
alt="Globe icon"
95-
width={16}
96-
height={16}
97-
/>
98-
Go to nextjs.org →
99-
</a>
100-
</footer>
101-
</div>
32+
</div>
33+
</main>
10234
);
10335
}

0 commit comments

Comments
 (0)