Repository navigation
feat: migrate to Next.js, add Supabase schema, implement Auth & Votin… - #2
Merged
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR migrates the project from a static HTML entrypoint to a Next.js App Router application, adding Clerk-based authentication, a Supabase-backed data model (schema + server access), and initial voting flows (dashboard + ballot + vote casting).
Changes:
- Introduces a Next.js App Router structure with global styling and reusable UI components (Topbar, landing, dashboard, election cards).
- Adds Supabase schema (tables + RLS policies) and a Supabase client/admin client used by server routes/actions.
- Implements Clerk auth protection (middleware), a Clerk webhook for user sync, and a voting page + server action to cast votes.
Reviewed changes
Copilot reviewed 27 out of 36 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Adds TypeScript configuration suitable for Next.js App Router. |
| supabase/schema.sql | Introduces Supabase tables (voters/elections/candidates/votes) plus RLS policies. |
| src/middleware.ts | Adds Clerk middleware to protect non-public routes. |
| src/lib/supabase.ts | Adds Supabase browser/admin clients and shared database types. |
| src/components/Topbar.tsx | Adds a fixed top navigation bar with Clerk auth UI. |
| src/components/Topbar.module.css | Styles Topbar. |
| src/components/LandingContent.tsx | Implements landing sign-in/sign-up UI via Clerk components. |
| src/components/LandingContent.module.css | Styles landing layout and visuals. |
| src/components/ElectionCard.tsx | Adds a presentational election card component. |
| src/components/ElectionCard.module.css | Styles ElectionCard. |
| src/app/page.tsx | Redirects signed-in users to dashboard; otherwise shows landing. |
| src/app/page.module.css | Adds (currently unused) page-level CSS module from template. |
| src/app/layout.tsx | Adds root layout with ClerkProvider and global font setup. |
| src/app/globals.css | Defines global design tokens and shared UI utility classes (buttons, Clerk overrides). |
| src/app/election/[id]/vote/VoteForm.tsx | Client-side candidate selection + vote submission UI. |
| src/app/election/[id]/vote/page.tsx | Server-rendered ballot page: loads election, voter, vote state, candidates. |
| src/app/election/[id]/vote/page.module.css | Styles ballot page and candidate cards. |
| src/app/election/[id]/vote/actions.ts | Server action to validate election and insert a vote. |
| src/app/dashboard/page.tsx | Server-rendered dashboard listing live elections. |
| src/app/dashboard/page.module.css | Styles dashboard layout. |
| src/app/api/webhooks/clerk/route.ts | Clerk webhook endpoint to sync new users into Supabase voters table. |
| README.md | Replaces prior README with a Next.js template README. |
| public/window.svg | Adds template asset. |
| public/vercel.svg | Adds template asset. |
| public/next.svg | Adds template asset. |
| public/globe.svg | Adds template asset. |
| public/file.svg | Adds template asset. |
| package.json | Adds Next.js/React/Clerk/Supabase dependencies and scripts. |
| next.config.ts | Adds Next.js config scaffold. |
| index.html | Removes the prior static HTML implementation. |
| eslint.config.mjs | Adds ESLint config using Next.js flat-config presets. |
| CLAUDE.md | Points to AGENTS.md. |
| AGENTS.md | Adds agent guidance note about Next.js breaking changes. |
| .gitignore | Adds Next.js/Node/Env/Clerk ignore patterns. |
Comments suppressed due to low confidence (1)
src/app/api/webhooks/clerk/route.ts:44
- Typo in error message: "occured" → "occurred".
return new Response('Error occured', {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+26
to
+28
| // Get the body | ||
| const payload = await req.json(); | ||
| const body = JSON.stringify(payload); |
|
|
||
| // If there are no headers, error out | ||
| if (!svix_id || !svix_timestamp || !svix_signature) { | ||
| return new Response('Error occured -- no svix headers', { |
Comment on lines
+9
to
+11
| if (!WEBHOOK_SECRET) { | ||
| throw new Error('Please add CLERK_WEBHOOK_SECRET from Clerk Dashboard to .env or .env.local'); | ||
| } |
Comment on lines
+42
to
+48
| <button className="btn-p" style={{ width: 'auto', height: '36px', fontSize: '.8rem' }}> | ||
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"> | ||
| <polyline points="9 11 12 14 22 4"/> | ||
| <path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"/> | ||
| </svg> | ||
| Open Ballot | ||
| </button> |
Comment on lines
+59
to
+64
| <div | ||
| key={c.id} | ||
| className={styles.candidateCard} | ||
| data-selected={selectedId === c.id} | ||
| onClick={() => setSelectedId(c.id)} | ||
| > |
Comment on lines
+1
to
+14
| import { createClient } from '@supabase/supabase-js'; | ||
|
|
||
| // ── Browser client (uses anon key + RLS) ───────────────────────────────────── | ||
| export const supabase = createClient( | ||
| process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://placeholder.supabase.co', | ||
| process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || 'placeholder' | ||
| ); | ||
|
|
||
| // ── Server / Admin client (bypasses RLS — server-side only) ────────────────── | ||
| export const supabaseAdmin = createClient( | ||
| process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://placeholder.supabase.co', | ||
| process.env.SUPABASE_SERVICE_ROLE_KEY || 'placeholder', | ||
| { auth: { autoRefreshToken: false, persistSession: false } } | ||
| ); |
Comment on lines
+64
to
+65
| create policy "voters_update_own" on voters | ||
| for update using (clerk_id = (current_setting('request.jwt.claims', true)::json->>'sub')); |
| .select('id') | ||
| .eq('election_id', election.id) | ||
| .eq('voter_id', voter.id) | ||
| .single(); |
Comment on lines
+43
to
+50
| // 3. Insert the vote | ||
| const { error: voteError } = await supabaseAdmin | ||
| .from('votes') | ||
| .insert({ | ||
| election_id: electionId, | ||
| voter_id: voter.id, | ||
| candidate_id: candidateId | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…g UI