A community membership platform for job seekers and mutual support. Members can connect through a directory, groups, DMs, events, and polls—all in one place.
- Framework: Next.js 16 (App Router)
- Language: TypeScript
- Database & Auth: Supabase (PostgreSQL, RLS, Auth)
- Styling: Tailwind CSS v4 + shadcn/ui (Radix primitives)
- Icons: lucide-react
- Dates: date-fns v3
- Video: Jitsi (public or JWT via
NEXT_PUBLIC_JITSI_APP_ID)
- Node.js 18+
- npm (or yarn/pnpm)
- A Supabase project (free tier works)
git clone https://github.com/What-We-Will/community-platform.git
cd community-platform
npm installCreate .env.local in the project root:
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
SUPABASE_SERVICE_ROLE_KEY=your-service-role-keyOptional (for Jitsi JWT / 8x8 JaaS):
NEXT_PUBLIC_JITSI_APP_ID=your-jaas-app-idGet the Supabase values from your project’s Settings → API in the Supabase dashboard. If the anon or service role keys are not shown there, check the legacy API tab.
If you have not created a blank new Supabase project for this repo please do so now.
Apply migrations so the schema and RLS policies exist:
npx supabase db pushOr, if using Supabase CLI linked to your project:
npx supabase migration upNOTE: If the above command gives an error Cannot find project ref. Have you run supabase link? run these commands and then retry the above command:
npx supabase loginLink to your Supabase project, selecting the appropriate project:
npx supabase linkNOTE: Migrations live in supabase/migrations/. The legacy range 001_*.sql → 057_*.sql runs first in numeric order; new migrations are timestamp-named (YYYYMMDDHHMMSS_<slug>.sql) and sort after. See supabase/migrations/CONVENTIONS.md.
npm run devOpen http://localhost:3000. Sign up via the auth flow;
To manually approve yourself, open Supabase > Table Editor > "profiles". You should see a row for your user. Change the "approval_status" column from "pending" to "approved".
After onboarding you’ll see the main app (dashboard, events, groups, messages, members, profile).
NOTE: If local onboarding fails with permission denied for table profiles, your Supabase project may be missing grants for the authenticated role.
You can check the current permissions with the following command in your local/dev Supabase SQL editor:
select
has_table_privilege('authenticated', 'public.profiles', 'SELECT') as can_select,
has_table_privilege('authenticated', 'public.profiles', 'INSERT') as can_insert,
has_table_privilege('authenticated', 'public.profiles', 'UPDATE') as can_update;If any of those return false, run this in your local/dev Supabase SQL editor:
GRANT SELECT, INSERT, UPDATE ON public.profiles TO authenticated;You will need a GMail account for this to serve as the email sender.
-
Ensure your GMail account has 2-Step Verification turned on:
- On google.com, click your avatar in the top right and click "Manage my google account".
- Go to "Security & sign-in" > "2-Step Verification"
- Turn it on at the bottom of the page.
-
Set up an App Password:
- Use the search bar at the top of the page to navigate to "App Passwords".
- Set any app name and copy the password they give you into your
.env.localfile. See below.
-
Set the following variables in your
.env.localfile:
GMAIL_USER=your-gmail-address-here
GMAIL_APP_PASSWORD=your app password goes here
ADMIN_EMAIL=your-gmail-address-here
BUG_REPORT_EMAIL=engineers@wwwrise.orgNote that as of this writing the group message notification emails are only sent if the recipient hasn't been on the platform today. You may want to comment out this lastSeen logic in messages/route.ts if you are testing locally.
├── app/
│ ├── (app)/ # Authenticated app (sidebar, layout)
│ │ ├── dashboard/ # Dashboard with cards (polls, events, chats, groups)
│ │ ├── events/ # Events list, create, detail, edit
│ │ ├── groups/ # Groups list + [slug] hub (chat, members, events, etc.)
│ │ ├── members/ # Member directory + [userId] profile
│ │ ├── messages/ # Inbox + [conversationId] thread
│ │ ├── onboarding/ # New-user onboarding
│ │ └── profile/ # My profile
│ ├── (auth)/ # Login, signup (no sidebar)
│ ├── auth/callback/ # OAuth callback
│ └── page.tsx # Landing page
├── components/
│ ├── auth/ # OAuth buttons, etc.
│ ├── dashboard/ # Dashboard cards (WelcomeBanner, PollsCard, etc.)
│ ├── events/ # EventCard, CreateEventForm, EditEventForm, RsvpButtons, etc.
│ ├── groups/ # GroupCard, GroupHeader, CreateGroupDialog, etc.
│ ├── landing/ # Landing page sections
│ ├── messages/ # ConversationList, MessageInput, ConversationView, etc.
│ ├── shared/ # UserAvatar (reused everywhere)
│ ├── ui/ # shadcn primitives (Button, Card, Dialog, etc.)
│ └── video/ # Jitsi wrapper, QuickCallButton
├── lib/
│ ├── actions/ # Server Actions (messages, polls, jitsi)
│ ├── supabase/ # createClient (server + client), proxy
│ ├── utils/ # time, status, avatar, events (type config), video
│ ├── conversations.ts # fetchRecentConversations (server)
│ ├── events.ts # createEvent, fetchUpcomingEvents, fetchEventWithDetails
│ ├── groups.ts # createGroup, joinGroup, leaveGroup, slugs
│ ├── messages.ts # findExistingDM, createDMConversation
│ ├── polls.ts # fetchActivePolls
│ ├── storage.ts # File upload helpers
│ ├── types.ts # Shared TS types (Profile, Event, Group, etc.)
│ └── utils.ts # cn() etc.
├── supabase/
│ └── migrations/ # SQL migrations (profiles, messaging, groups, polls, events, storage)
├── docs/
│ └── adr/ # Architecture Decision Records
├── scripts/
│ └── ci/ # CI helper scripts (migration collision gate, etc.)
└── proxy.ts # Next.js proxy (not middleware)
| Area | What it does |
|---|---|
| Auth & onboarding | Sign up / login (email + OAuth), onboarding flow, redirect to dashboard |
| Profiles | Display name, avatar, headline, bio, resume upload, “open to referrals” |
| Members | Directory with search/filters, public profile pages |
| Messages | DMs and group chats, Realtime, typing indicators, unread badges, file attachments |
| Groups | Create/join/leave, group chat, admin member management, join requests, Events tab |
| Dashboard | Welcome banner, new members, recent chats, community polls, my groups, upcoming events |
| Events | Create/edit/delete events, RSVP (going/maybe/declined), list + calendar view, video link (Jitsi) |
| Polls | Community-wide polls, vote, create poll (dialog + server action) |
- Next.js 16:
paramsandsearchParamsare Promises—alwaysawaitthem (e.g.const { eventId } = await params). - Auth in Server Components: Use
supabase.auth.getUser(), notgetSession(). - Server vs client: Prefer Server Components. Add
"use client"only when you need state, event handlers, or browser APIs (e.g. forms, RSVP buttons, realtime). - Server Actions: Live in
app/.../actions.tsorlib/actions/with"use server". Import non–"use server"helpers (e.g.lib/events.ts) from there as needed. - Supabase: Server code uses
createClient()from@/lib/supabase/server; client code uses@/lib/supabase/client. - Styling: Tailwind + shadcn. Reuse
UserAvatar,formatRelativeTime,eventTypeConfig, etc. fromlib/andcomponents/shared/. - New UI: Add components via
npx shadcn@latest add <component>when you need a new primitive.
Author migrations on your own machine against your personal Supabase project (set up in Getting started) — never against the shared preview DB. Migrations reach the shared preview DB only via merged PRs on main.
Scaffold a new migration with:
supabase migration new <slug>This produces a timestamped filename like YYYYMMDDHHMMSS_<slug>.sql. Edit the generated SQL file, then apply it to your own Supabase project:
npx supabase db pushFor naming rules, required-column rules, and the legacy 001–057 range, see supabase/migrations/CONVENTIONS.md.
If you hit pain (filename collision, semantic conflict, contention on the shared preview DB, slow apply cycles), file a GitHub issue with the migration-pain-signal label.
- Read the AI use policy
- Fork the repo and clone your fork.
- Create a branch (e.g.
feature/your-featureorfix/issue-123). - Set up locally (see Getting started) and make your changes.
- Commit — the pre-commit hook auto-fixes lint issues on staged files. The pre-push hook type-checks the project before pushing.
- Open a PR against
mainwith a short description of what you changed and why.
Check the Github Issues in the repo to find some good first issues to tackle.
- UI/UX: Improve accessibility (labels, focus, contrast) or responsive behavior. If you find a bug in the UI, please create an issue ticket, and propose a way to address it.
- Tests: Add tests for utilities in
lib/or key user flows. See the testing standards for conventions and requirements. - Docs: Improve this README or add inline comments in a tricky file.
- Dashboard:
app/(app)/dashboard/,components/dashboard/ - Events:
app/(app)/events/,components/events/,lib/events.ts - Groups:
app/(app)/groups/,components/groups/,lib/groups.ts - Messages:
app/(app)/messages/,components/messages/,lib/conversations.ts,lib/messages.ts - Types and shared logic:
lib/types.ts,lib/utils/
This project uses Husky and lint-staged to enforce code quality before changes leave your machine.
| Hook | What it runs | Why |
|---|---|---|
| pre-commit | lint-staged — runs eslint --fix on staged *.ts / *.tsx files |
Catches lint issues before they enter the commit history |
| pre-push | npx tsc --noEmit |
Type-checks the whole project so broken types never reach the remote |
Hooks are installed automatically via the prepare script (husky) when you run npm install. They are disabled in CI (HUSKY=0).
Both GitHub Actions workflows (preview and production) run the following checks:
- Lint —
npm run lint - Type check —
npx tsc --noEmit - Unit tests —
npm test - Dependency audit —
npm audit --audit-level=high(preview workflow, non-blocking)
The preview workflow (preview.yml) deploys a Vercel preview for every PR and posts the preview URL as a PR comment. The comment step validates the URL against *.vercel.app before posting and is skipped on non-PR triggers or when the deploy step fails.
The production workflow (production.yml) deploys to production on merges to main.
| Command | Description |
|---|---|
npm run dev |
Start dev server (Turbopack) |
npm run build |
Production build |
npm run start |
Start production server |
npm run lint |
Run ESLint |
npm test |
Run unit tests (Vitest) |
npm run test:watch |
Run tests in watch mode |
npm run test:ci |
Run tests once (CI mode) |
npm run test:e2e |
Run Playwright E2E tests (headless) |
npm run test:e2e:debug |
Step through E2E tests with the inspector |
Playwright tests live in e2e/. They cover the landing page and authenticated user flows (approved, unapproved, unonboarded).
The auth specs require test users in your own Supabase project — never the shared preview or production environments. See e2e/README.md for the full setup walkthrough (user creation, profile state SQL, .env.e2e configuration).
The app uses Jitsi for event and group video. By default it uses the community server (meet.jit.si); room names are generated in lib/utils/video.ts (e.g. whatwewill-event-<id>). For 8x8 JaaS, set NEXT_PUBLIC_JITSI_APP_ID and configure JWT in lib/actions/jitsi.ts.
You can deploy to Vercel (or any Next.js host). Set the same env vars in the project settings and ensure the Supabase project allows your deployment URL in Auth and (if used) redirect URLs.