Marketing site, sponsor conversion page, and an authenticated team-progress dashboard for AeroBMSCE — the aerospace & UAV club of BMS College of Engineering.
Above Gravity, Beyond Limits.
- Public — landing (
/), sponsor / partner page (/sponsor), login (/login). - Authenticated — team-lead board (
/dashboard) and an all-teams overview (/dashboard/overview), protected by edge proxy + Postgres RLS.
| Layer | Choice |
|---|---|
| Frontend | Next.js 16 (App Router) · TypeScript · Tailwind CSS 3 |
| Auth | Supabase Auth (email/password, invite-only) |
| Database | Supabase Postgres with Row Level Security |
| Storage | Supabase Storage (private progress bucket for attachments) |
| Hosting | Vercel (frontend) + Supabase (backend) |
Fonts: Exo 2 (headings) + DM Sans (body) via next/font/google.
- Node.js 20+ and npm
- A Supabase project (free tier is fine)
npm installCopy the template and fill it in from Supabase → Project Settings → API:
cp .env.example .env.local| Variable | Where it's used | Notes |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
client + server | Project URL |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
client + server | Anon/public key |
SUPABASE_SERVICE_ROLE_KEY |
server only | Secret — bypasses RLS. Never expose to a browser |
NEXT_PUBLIC_SITE_URL |
metadata / redirects | http://localhost:3000 in dev |
Open Supabase → SQL Editor and run the files in
supabase/migrations/ in order:
| Order | File | What it does |
|---|---|---|
| 1 | 0001_schema.sql |
Tables, enums, helper functions, auto-profile trigger |
| 2 | 0002_rls.sql |
Row Level Security policies for every table |
| 3 | 0003_storage.sql |
Private progress bucket + storage RLS |
| 4 | 0004_seed.sql |
Seeds the 9 teams; documents admin + invite SQL |
Using the Supabase CLI instead?
supabase db pushwill apply the same files.
There is no public signup. Accounts are created by an admin.
Bootstrap your first admin:
-
Supabase → Authentication → Users → "Add user" — set an email + password and tick Auto Confirm User. A
profilesrow is auto-created by a trigger. -
Promote it to admin in the SQL editor (replace the email):
update public.profiles p set role = 'admin', full_name = 'Core Admin', team_id = (select id from public.teams where slug = 'aerobmsce-core') from auth.users u where u.id = p.id and u.email = 'aerobmsce@bmsce.ac.in';
Invite a team lead:
-
Authentication → Users → "Invite user" (or "Add user"). Enter their email.
-
Assign their role + team:
update public.profiles p set role = 'team_lead', full_name = 'Their Name', team_id = (select id from public.teams where slug = 'design') from auth.users u where u.id = p.id and u.email = 'lead@example.com';
For a Management Collective member (read-all, no edit) set role = 'management'.
npm run dev # http://localhost:3000| Role | Progress updates | Teams / users |
|---|---|---|
team_lead |
Create / read / update own team only | — |
management |
Read all teams (no edit) | — |
admin |
Read all teams | Manage teams/users |
- A team lead can only see, post to, and edit their own team's updates.
- Uploads land in
progress/{team_id}/{update_id}/…; storage RLS blocks any cross-team read/write. Upload type (images/PDF) and size (< 10 MB) are also validated server-side inapp/dashboard/actions.ts. - The service-role key is only ever imported in server code
(
lib/supabase/admin.ts, guarded byimport 'server-only').
app/
page.tsx # landing
sponsor/page.tsx # conversion page
login/ # sign-in page + server actions (no signup)
dashboard/
layout.tsx # auth shell (requireAuth)
page.tsx # team-lead board
overview/page.tsx # admin/management all-teams view
actions.ts # create/edit update + validated uploads
components/ # site, ui, auth, dashboard components
lib/
content.ts # ALL marketing copy (single source of truth)
supabase/ # client / server / admin / proxy helpers
auth.ts # getSessionProfile / requireAuth / requireRole
progress.ts # update fetching + signed attachment URLs
proxy.ts # protects /dashboard/* (Next 16 proxy convention)
supabase/migrations/ # SQL: schema, RLS, storage, seed
public/ # logo + drop-in photos/partner logos (see README there)
Editing site copy? It all lives in lib/content.ts.
public/logo.svg and public/favicon.svg are branded placeholders. Real photos
and partner logos can be dropped into /public at the paths listed in
public/README.md — the UI shows a graceful placeholder
until each file exists, so no code changes are needed.
Already live once you've run the migrations (steps 3–4).
- Import the repo into Vercel.
- Add the four environment variables from step 2 (set
NEXT_PUBLIC_SITE_URLto your production URL). - In Supabase → Authentication → URL Configuration, add your Vercel domain to the allowed redirect/site URLs.
- Deploy.
npm run buildis the default build command.
| Command | Description |
|---|---|
npm run dev |
Local dev server |
npm run build |
Production build (+ tsc) |
npm run start |
Serve the production build |