A geospatial safety platform for reporting and tracking snake encounters in the Philippines. Citizens can report sightings safely (heatmap only, no exact coordinates exposed), responders handle incidents with exact GPS access, and admins manage verification and analytics.
| Feature | Citizens | Responders | Admins |
|---|---|---|---|
| View heatmap (no exact pins) | β | β | β |
| Report sightings (click-to-place map) | β | β | β |
| Respond to incidents (exact GPS pins + 100m search zones) | β | β | β |
| Manage users & roles | β | β | β |
| Verify responder applications | β | β | β |
| System audit logs | β | β | β |
| Analytics dashboard | β | β | β |
- Next.js 16.2.6 (App Router, Turbopack)
- React 19.2.4
- TailwindCSS v4 (@import "tailwindcss" + @theme directives)
- Framer Motion 12.38 (animations, page transitions)
- Leaflet / OpenStreetMap (all map views, zero API keys)
- @tanstack/react-query (data caching)
- Supabase (Auth, PostgreSQL, RLS, Realtime, Storage)
- Row Level Security (20+ policies, privacy-first)
- Auth: Email/password + OAuth (Google, GitHub)
src/
βββ proxy.ts # Next.js proxy middleware (auth + role guard)
βββ app/
β βββ layout.tsx # Root layout (providers, fonts)
β βββ page.tsx # Citizen landing: hero, heatmap, safety cards
β βββ globals.css # Tailwind v4 design system (@theme, @utility)
β βββ providers.tsx # Framer Motion LazyMotion
β βββ login/page.tsx # Login + signup (Google/GitHub OAuth, email/password)
β βββ report/page.tsx # Click-to-place map report form
β βββ auth/
β β βββ callback/route.ts # OAuth callback handler
β β βββ logout/route.ts # Server-side logout (cookie clearing)
β βββ responder/
β β βββ page.tsx # Responder dashboard (cases, map, profile)
β β βββ apply/page.tsx # Responder application form
β βββ admin/
β βββ layout.tsx
β βββ page.tsx # Admin panel (5 tabs: Overview, Users, Verification, Sightings, Logs)
βββ components/
β βββ UserProvider.tsx # User context (user + role)
β βββ map/
β β βββ HeatmapMap.tsx # Citizen view β translucent circles, no pins
β β βββ ResponderMap.tsx # Responder view β exact pins + 100m zones
β β βββ AdminMap.tsx # Admin view β full overlay
β βββ report/
β β βββ ReportMap.tsx # Click-to-place pin map
β βββ ui/
β βββ Navbar.tsx # Role-aware navigation
β βββ GlassCard.tsx, GlassPanel.tsx
β βββ Button.tsx, Badge.tsx
β βββ Input.tsx, Modal.tsx
β βββ LoadingSpinner.tsx, Skeleton.tsx
β βββ Toast.tsx, Sidebar.tsx
βββ lib/
β βββ supabase.ts # Singleton createBrowserClient + all types
β βββ supabase-provider.tsx # Supabase context provider
β βββ query-provider.tsx # React Query client
β βββ utils.ts # cn() helper
βββ hooks/
βββ useSightings.ts # Fetch + realtime subscription
βββ useRealtime.ts # Generic realtime hook
- Node.js 18+ (for Next.js 16)
- npm or yarn
- Supabase project (free tier works)
-
Clone the repo
git clone https://github.com/qppd/snake-sightings.git cd snake-sightings -
Install dependencies
npm install --legacy-peer-deps
-
Environment setup Copy
.env.exampleto.env.localand fill in your Supabase credentials:cp .env.example .env.local
Required variables:
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_anon_key SUPABASE_SERVICE_ROLE_KEY=your_service_role_key -
Run database migration Open your Supabase SQL Editor and run:
supabase/migrations/001_initial_schema.sql(complete schema + RLS)supabase/rls.sql(RLS policies β already included in migration)
-
Start the dev server
npm run dev
npm run build
npm run start- Push to GitHub
- Import repo on Vercel
- Add environment variables (same as
.env.local) - Deploy β no build config needed
Uses single createBrowserClient from @supabase/ssr (cookie-based session storage):
| Source | Library | Creates Instance? |
|---|---|---|
src/lib/supabase.ts |
@supabase/ssr createBrowserClient() |
Yes β singleton |
src/lib/supabase-provider.tsx |
@supabase/ssr |
No β reuses singleton |
src/proxy.ts |
@supabase/ssr createServerClient() |
Per-request |
auth/callback/route.ts |
@supabase/ssr createServerClient() |
Per-request |
auth/logout/route.ts |
@supabase/ssr createServerClient() |
Per-request |
Server-side POST route clears ALL cookies via maxAge: 0. Navbar uses <form action="/auth/logout" method="POST"> for reliable logout.
src/proxy.ts runs on every request:
- Checks session via
createServerClient - Redirects unauthenticated users to
/login - Fetches user role from
profilestable - Enforces role-based route access
- Public routes pass through
Three role-separated map components (Leaflet/OpenStreetMap with SSR-safe dynamic() imports):
- HeatmapMap β Citizen view: translucent risk-colored circles over metro areas. No pins, no exact coords.
- ResponderMap β Exact GPS pins + 100m search zone circles for assigned incidents.
- AdminMap β Full data overlay with all sightings.
Privacy model: Citizens see heatmap only. Responders see exact pins + zones for assigned incidents. Admins see everything.
| Role | Default Route | Access |
|---|---|---|
citizen |
/ |
Heatmap, report sightings |
responder |
/responder |
Dashboard, assigned cases |
admin |
/admin |
Full system governance |
sub_admin |
/admin |
Limited moderation |
- profiles β User profiles (extends
auth.users, auto-created on signup) - sightings β Core data: risk level, GPS coords, description, visibility mode
- incidents β Links responders to sightings they handle
- responder_applications β Verification applications
- system_logs β Audit trail for admin actions
- 20+ policies enforcing privacy-first access
- Citizens: INSERT own sightings, SELECT own + heatmap_only
- Responders: SELECT only assigned incident sightings
- Admins: Full read/write
get_user_role()function withSECURITY DEFINERto avoid recursion
npm test # Run all tests
npm run test:watch # Watch mode
npm run test:coverage # Coverage report- No frontend trust β RLS enforces all access control server-side
- Heatmap privacy β exact coords never exposed to citizens
- Cookie-based sessions β HttpOnly cookies managed server-side
- Audit logging β All admin actions logged to
system_logs - Input sanitization β RLS prevents unauthorized inserts/updates
MIT β see LICENSE
- Fork the repo
- Create a feature branch
- Make changes
- Run
npm run buildβ ensure it compiles - Submit a PR
Built with β€οΈ for community safety