feat: auth flow — sign up, sign in, sign out (#24)#37
Conversation
Co-authored-by: Ona <no-reply@ona.com>
- Add (auth) route group with /sign-in and /sign-up pages - Add (app) route group with auth guard redirecting to /sign-in - Sign-up passes display_name to handle_new_user trigger - Post-auth redirect to user's personal workspace (/[workspaceSlug]) - Sign-out clears session and redirects to /sign-in - GitHub and Google OAuth buttons rendered disabled with 'coming soon' tooltip - Proxy-level auth redirect for unauthenticated users on protected routes - Switch root layout to JetBrains Mono, dark-only oklch theme tokens - Add shadcn/ui Card, Input, Label, Tooltip components - Update architecture and conventions docs Co-authored-by: Ona <no-reply@ona.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Review: ✅ ApprovedAuth flow implementation is solid. Two non-blocking observations left as inline comments:
Neither is blocking. Architecture is clean — two-layer auth protection (proxy + layout), correct use of Ready to merge. |
| className={`${geistSans.variable} ${geistMono.variable} antialiased`} | ||
| > | ||
| <body className="min-h-screen">{children}</body> | ||
| <html lang="en" className={`${jetbrainsMono.variable} antialiased`}> |
There was a problem hiding this comment.
The <html> element is missing class="dark". The shadcn components (button.tsx, input.tsx) use dark: variant classes (e.g. dark:border-input, dark:bg-input/30, dark:hover:bg-input/50) that won't activate without it. The CSS defines @custom-variant dark (&:is(.dark *)) and a .dark block specifically for this.
Since Memo is dark-only, add dark to the class list:
<html lang="en" className={`dark ${jetbrainsMono.variable} antialiased`}>| .maybeSingle(); | ||
|
|
||
| if (membership?.workspaces) { | ||
| const ws = membership.workspaces as unknown as { slug: string }; |
There was a problem hiding this comment.
AGENTS.md: "No as casts unless unavoidable (add a comment explaining why)."
This cast is justified — Supabase's .select() with joined relations doesn't infer the nested object type. Add a comment:
// Supabase join type inference doesn't resolve nested relations
const ws = membership.workspaces as unknown as { slug: string };Same issue in sign-up/page.tsx line 60.
| "next": "16.2.3", | ||
| "react": "19.2.4", | ||
| "react-dom": "19.2.4" | ||
| "react-dom": "19.2.4", |
There was a problem hiding this comment.
shadcn is a CLI tool for scaffolding components — it shouldn't be a production dependency. Move it to devDependencies:
"devDependencies": {
"shadcn": "^4.2.0",
...
}
zacharias-ona
left a comment
There was a problem hiding this comment.
Review complete. Three issues found that should be fixed:
-
Missing
darkclass on<html>— shadcn components usedark:variant classes that won't activate without it. Adddarkto the class list insrc/app/layout.tsx. -
as unknown ascasts without justifying comments — AGENTS.md requires a comment when usingascasts. Add comments in bothsign-in/page.tsxandsign-up/page.tsx. -
shadcnin production dependencies — It's a CLI tool. Move todevDependencies.
See inline comments for details. Not merging until these are addressed.
|
✅ Post-merge verification passed. Routes tested (all returned 200):
Skipped: None — all routes exist and responded correctly. Console errors: None detected. |
|
Summary: Auth page headings use All other checks passed: color tokens, spacing, component usage, button variants, borders, dark mode, accessibility, transitions, responsive layout, and visual rendering. |
Closes #24
What
Implements the complete authentication flow: sign up, sign in, and sign out using Supabase Auth with email/password. After sign-up, the DB trigger creates the user's profile and personal workspace, and the user lands in that workspace.
How
Route structure:
(auth)/sign-in— email/password form with validation and error display(auth)/sign-up— display name + email/password form, passesdisplay_namein signUp options for thehandle_new_usertrigger(app)/— authenticated route group with server-side auth guard(app)/[workspaceSlug]— workspace home page (placeholder for future editor)Auth protection (two layers):
src/lib/supabase/proxy.ts) — optimistic redirect for unauthenticated users on protected routessrc/app/(app)/layout.tsx) — authoritative server-side check viasupabase.auth.getUser()Post-auth redirect: After sign-in/sign-up, fetches the user's workspace membership (joining
members→workspaces) to get the slug, then redirects to/{workspaceSlug}.OAuth: GitHub and Google buttons are rendered on both auth pages but disabled with a "coming soon" tooltip.
Theme: Switched root layout from Geist to JetBrains Mono. Applied dark-only oklch color tokens from design spec with
--radius: 0for sharp corners.New shadcn/ui components: Card, Input, Label, Tooltip (base-nova style with @base-ui/react primitives).
Testing
pnpm lint✅pnpm typecheck✅pnpm test✅ (no test files — auth pages are interactive client components)pnpm build✅ — all routes compile correctlyMobile responsiveness cannot be verified locally but the centered card layout with
max-w-smis inherently responsive.