Production-ready monorepo for the GB FoodHub food ordering ecosystem in Gilgit Baltistan, Pakistan. Three apps share one Supabase backend:
| App | Path | Purpose |
|---|---|---|
| Mobile | mobile_app/ |
Flutter customer app — browse, cart, checkout, orders, profile |
| Web | web/ |
Next.js dashboard — restaurant management, super admin, POS |
| Backend | supabase/ |
PostgreSQL schema, RLS, seed data, storage, Edge Functions |
- Architecture
- Repository Structure
- Prerequisites
- Quick Start (All Apps)
- Full Flow
- Supabase Backend
- Mobile App (Flutter)
- Web App (Next.js)
- Scripts
- Environment Variables
- Authentication & User Roles
- How the Apps Work Together
- Common Development Tasks
- Design System
- Deployment
- Troubleshooting
flowchart TB
subgraph clients [Client Apps]
Mobile["mobile_app\nFlutter + Riverpod"]
Web["web\nNext.js 15 + React 19"]
end
subgraph supabase [Supabase]
Auth[Auth]
DB[(PostgreSQL + RLS)]
Storage[Storage Buckets]
Realtime[Realtime]
Edge[Edge Functions]
end
Mobile --> Auth
Mobile --> DB
Mobile --> Storage
Web --> Auth
Web --> DB
Web --> Storage
Web --> Realtime
Edge --> DB
Data flow summary
- Both clients authenticate via Supabase Auth and read/write data through the anon key with Row Level Security (RLS) enforcing access.
- The mobile app is the customer-facing ordering experience.
- The web app is the operations layer: restaurant owners manage menus/orders; admins approve restaurants; cashiers run the POS.
- Edge Functions run server-side jobs (notifications, payments, analytics, email workflows) using the service role key — never expose this key in client apps.
gb-food-hub/
├── mobile_app/ # Flutter customer app
│ ├── lib/
│ │ ├── config/ # Supabase bootstrap
│ │ ├── core/ # Theme, routing, shared widgets
│ │ ├── features/ # Feature modules (auth, home, cart, orders, profile)
│ │ ├── services/ # Supabase client, notifications
│ │ └── shared/ # Shared models
│ ├── android/ # Android platform config
│ ├── web/ # Flutter web build assets
│ └── .env.example # Mobile environment template
│
├── web/ # Next.js dashboard + POS + admin
│ ├── app/ # App Router pages
│ ├── components/ # UI components and dashboard shell
│ ├── lib/ # Supabase client, API helpers, hooks, types
│ └── .env.example # Web environment template
│
├── supabase/
│ ├── migrations/ # SQL schema + seed data (applied in order)
│ ├── functions/ # Deno Edge Functions
│ └── config.toml # Local Supabase project config
│
├── scripts/
│ └── seed-notes.md # Notes on demo seed data
│
└── docs/
└── deployment.md # Production deployment checklist
Install these before running any app:
| Tool | Version | Used by |
|---|---|---|
| Flutter SDK | ≥ 3.4 (Dart ≥ 3.4) | mobile_app |
| Node.js | ≥ 18 (LTS recommended) | web |
| Supabase CLI | Latest | supabase |
| Docker Desktop | Latest | Local Supabase (supabase start) |
Optional (production mobile)
- Firebase project +
google-services.json(Android) /GoogleService-Info.plist(iOS) for push notifications - Android Studio or Xcode for device builds
Run these steps once to get the full stack running locally.
From the repository root:
supabase start
supabase db resetsupabase startlaunches local Postgres, Auth, Storage, Studio, and Edge Functions runtime via Docker.supabase db resetapplies all migrations insupabase/migrations/in order and runs seed data.
After start, note the local URLs and keys:
supabase statusTypical local endpoints:
| Service | URL |
|---|---|
| API | http://127.0.0.1:54321 |
| Studio (DB UI) | http://127.0.0.1:54323 |
| Postgres | 127.0.0.1:54322 |
Mobile (mobile_app/.env):
cd mobile_app
cp .env.example .envEdit .env:
SUPABASE_URL=http://127.0.0.1:54321
SUPABASE_ANON_KEY=<anon key from supabase status>
API_BASE_URL=http://127.0.0.1:54321/functions/v1Web (web/.env.local):
cd web
cp .env.example .env.localEdit .env.local:
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=<anon key from supabase status>
SUPABASE_SERVICE_ROLE_KEY=<service role key — server only, optional for local dev>Never commit
.env,.env.local, or real API keys to git.
cd mobile_app
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter devices
flutter run -d <device-id>Run on Chrome (web preview):
flutter run -d chromecd web
npm install
npm run devOpen in browser:
| Route | Purpose |
|---|---|
http://localhost:3000/signup |
Customer registration |
http://localhost:3000/login |
Staff / admin login |
http://localhost:3000/register |
Restaurant partner registration |
http://localhost:3000/welcome |
Customer landing after sign-up |
http://localhost:3000/dashboard |
Restaurant dashboard |
http://localhost:3000/dashboard/menu |
Menu management |
http://localhost:3000/dashboard/orders |
Live order queue |
http://localhost:3000/dashboard/analytics |
Revenue charts |
http://localhost:3000/dashboard/categories |
Category management |
http://localhost:3000/dashboard/restaurants |
Restaurant settings |
http://localhost:3000/admin |
Super admin (approve restaurants) |
http://localhost:3000/pos |
POS billing terminal |
From web/ (requires SUPABASE_SERVICE_ROLE_KEY in .env or .env.local):
cd web
npm run setup:demoThis creates the Gilgit Branch owner and cashier (see Full Flow — Platform setup) so you can log into the dashboard and POS immediately.
supabase functions deploy dispatch-notification
supabase functions deploy payment-hook
supabase functions deploy scheduled-analytics
supabase functions deploy email-workflowsFor local function testing, functions are available at http://127.0.0.1:54321/functions/v1/<function-name>.
End-to-end walkthrough of how the platform is set up and how each user type moves through the system.
flowchart LR
subgraph setup [1. Setup]
S1[Start Supabase] --> S2[Apply migrations]
S2 --> S3[Configure .env files]
S3 --> S4[Run setup:demo]
S4 --> S5[Start mobile + web]
end
subgraph customer [2. Customer]
C1[Web /signup] --> C2[Mobile login]
C2 --> C3[Browse & cart]
C3 --> C4[Checkout COD]
C4 --> C5[Track order]
end
subgraph staff [3. Restaurant staff]
R1[Web /login] --> R2[Dashboard]
R2 --> R3[Manage menu]
R2 --> R4[Advance orders]
end
subgraph admin [4. Platform admin]
A1[Web /admin] --> A2[Approve restaurants]
end
setup --> customer
setup --> staff
customer --> R4
staff --> C5
Run this once per environment (local Docker or hosted Supabase project).
| Step | Command / action | Result |
|---|---|---|
| 1 | supabase start |
Postgres, Auth, Storage, Studio, Edge Functions runtime |
| 2 | supabase db reset |
Applies migrations + seed data (Gilgit Branch, categories, dishes) |
| 3 | Copy .env files |
Mobile and web point at http://127.0.0.1:54321 |
| 4 | cd web && npm run setup:demo |
Creates demo owner + cashier accounts (see below) |
| 5 | flutter run + npm run dev |
Mobile and web apps running |
| Step | Command / action | Result |
|---|---|---|
| 1 | Create project at supabase.com | Hosted Postgres + Auth |
| 2 | supabase login |
Authenticate CLI |
| 3 | supabase link --project-ref <ref> |
Link local repo to remote project |
| 4 | supabase db push |
Apply all migrations to remote |
| 5 | Set web/.env and mobile_app/.env with remote URL + anon key |
Clients connect to hosted backend |
| 6 | cd web && npm run setup:demo |
Demo staff accounts + Gilgit Branch on remote |
| 7 | Deploy Edge Functions + set secrets | Notifications, payments, analytics |
Demo accounts (created by npm run setup:demo):
| Role | Password | Access | |
|---|---|---|---|
| Restaurant owner | owner@gilgit.gb |
GilgitOwner2026! |
Dashboard, menu, orders, POS |
| Cashier | cashier@gilgit.gb |
GilgitCashier2026! |
POS, limited dashboard |
Requires SUPABASE_SERVICE_ROLE_KEY in web/.env or web/.env.local.
Customers sign up on the web, then order on the mobile app with the same credentials.
sequenceDiagram
participant W as Web (signup)
participant M as Mobile App
participant S as Supabase
W->>S: POST /signup — email, password, full_name, phone
S->>S: Auth user + profiles row (role: customer)
W->>W: Redirect to /welcome
M->>S: Sign in (email/password or Google OAuth)
M->>S: Fetch approved restaurants, categories, food_items
M->>M: Add items to cart (in-memory, single restaurant)
M->>S: Create address (if none saved)
M->>S: Insert orders + order_items + payments (COD, pending)
M->>M: /success → /tracking/:id
S-->>M: Realtime order status updates
Step-by-step
- Create account (web) — Open
http://localhost:3000/signup, fill name, email, phone, password. On success you land on/welcome, which explains that ordering happens in the mobile app. - Sign in (mobile) — Use the same email and password on
/login(or Google OAuth). Auth trigger + app upsert ensure aprofilesrow exists. - Browse (mobile) — Home shows featured restaurants and categories. Tap a restaurant →
/restaurant/:id→ dish detail → add to cart. - Cart (mobile) —
/cartshows line items, delivery fee, 5% tax, total. Requires sign-in to proceed. - Checkout (mobile) —
/checkoutcollects delivery address (saved or new), payment method (Cash on Delivery only), order summary. - Place order (mobile) — Inserts into
orders(statuspending),order_items, andpayments(methodcod, statuspending). Cart clears; redirect to/success. - Track (mobile) —
/tracking/:idshows live status. Order history at/orders.
Web routes for customers
| Route | Purpose |
|---|---|
/signup |
Customer registration |
/welcome |
Post-signup landing (directs to mobile app) |
/login |
Shared login — customers redirect here too, then to /welcome |
Customers cannot access /dashboard, /admin, or /pos — middleware redirects them to /welcome.
Staff and owners use the web dashboard and POS. They never order through the web customer signup.
flowchart TD
A[Staff visits /login] --> B{Valid credentials?}
B -->|No| A
B -->|Yes| C{profiles.role}
C -->|restaurant_owner| D[/dashboard]
C -->|restaurant_staff / cashier| D
C -->|admin / super_admin| E[/admin or /dashboard]
C -->|customer| F[/welcome — wrong portal]
D --> G[useWorkspace loads restaurant]
G --> H[Orders — realtime queue]
G --> I[Menu — CRUD food_items]
G --> J[Categories / Settings / Analytics]
G --> K[/pos — walk-in sales]
Step-by-step
- Get access — Run
npm run setup:demofor demo accounts, or have an admin setprofiles.roletorestaurant_owner/restaurant_staff/cashierand linkrestaurants.owner_idorrestaurant_staff. - Login —
http://localhost:3000/loginwith staff credentials.resolvePostLoginPath()sends staff to/dashboard, admins to/admin. - Workspace —
useWorkspaceresolves restaurant: owned → staff assignment → admin fallback (first approved restaurant). - Manage orders —
/dashboard/orderslists orders for the restaurant. Realtime subscription refreshes when mobile customers place orders. Click Advance to move status:pending→confirmed→preparing→out_for_delivery→delivered. - Manage menu —
/dashboard/menuand/dashboard/categoriesread/writefood_itemsandcategoriesscoped byrestaurant_id(RLS enforced). - Analytics —
/dashboard/analyticsand/dashboard/revenueshow metrics fromanalyticsand order data.
Restaurant partner registration (/register) creates a customer account first; an admin must upgrade the role before dashboard access.
Admins and super admins manage the whole platform from /admin.
- Login — Same
/loginpage; roleadminorsuper_adminredirects to/admin. - Platform stats — Total restaurants, users, revenue, orders.
- Approve restaurants — Pending restaurants (
is_approved = false) from partner registration or manual inserts. Approve setsis_approved = true— restaurant appears in the mobile app. - Reject — Removes or marks rejected listings.
- Platform orders — Recent orders across all restaurants.
To create a super admin locally: Supabase Studio → Authentication → create user → Table Editor → profiles → set role = super_admin.
Cashiers and owners record in-store sales without the mobile app.
- Open
http://localhost:3000/pos(requires staff role). - Select items from the restaurant menu (
food_items). - Complete sale —
createPosInvoice()inserts intopos_invoices. - Invoice rolls into
/dashboard/analyticsand/dashboard/revenue.
Use this checklist to verify the entire stack works together:
| # | Actor | Action | Expected result |
|---|---|---|---|
| 1 | Dev | supabase db reset + npm run setup:demo |
Gilgit Branch + demo staff exist |
| 2 | Customer | Sign up at /signup |
Lands on /welcome |
| 3 | Customer | Log in on mobile with same email | Home shows Gilgit Branch |
| 4 | Staff | Log in at /login as owner@gilgit.gb |
Dashboard loads Gilgit Branch |
| 5 | Staff | Open /dashboard/orders |
Empty or existing queue visible |
| 6 | Customer | Add Chapshuro (or any dish) → checkout → Place Order | /success with order number |
| 7 | Staff | Orders page refreshes (realtime) | New order appears as pending |
| 8 | Staff | Click Advance through statuses | Status moves toward delivered |
| 9 | Customer | Open /tracking/:id on mobile |
Status matches dashboard |
| 10 | Staff | Run POS sale at /pos |
Invoice in analytics |
Order status pipeline
pending → confirmed → preparing → out_for_delivery → delivered
↘ cancelled / refunded
Data written on mobile checkout
| Table | Fields |
|---|---|
orders |
user_id, restaurant_id, address_id, totals, status: pending |
order_items |
food_item_id, name_snapshot, unit_price, quantity |
payments |
method: cod, status: pending, amount |
Migrations run in numeric order. Do not rename applied migration files in production.
| File | What it does |
|---|---|
0001_initial_schema.sql |
Full schema: tables, enums, indexes, RLS policies, storage buckets, realtime publication |
0002_seed_demo_data.sql |
Demo restaurant "Gilgit Branch", GB dishes, sample orders, analytics |
0003_restaurant_featured.sql |
Adds is_featured column; marks Gilgit Branch as featured |
0004_auth_profile_trigger.sql |
Auto-creates profiles row when a user signs up via Auth |
Create a new migration:
supabase migration new <descriptive_name>
# Edit the new file in supabase/migrations/
supabase db reset # local: re-apply all migrationsPush to remote Supabase project:
supabase link --project-ref <your-project-ref>
supabase db pushYou can also run migration helpers from web/:
cd web
npm run supabase:start
npm run supabase:reset
npm run supabase:new -- <name>
npm run supabase:push| Table | Purpose |
|---|---|
profiles |
User profile linked to auth.users; stores role, points, contact info |
restaurants |
Restaurant listings (owner, location, fees, approval status) |
restaurant_staff |
Staff/cashier assignments per restaurant |
categories |
Food categories (Traditional, Fast Food, etc.) |
food_items |
Menu items per restaurant |
carts / cart_items |
Per-user shopping cart |
addresses |
Delivery addresses |
orders / order_items |
Placed orders with status tracking |
payments |
Payment records (COD, card, JazzCash, EasyPaisa, Stripe) |
favorites |
Saved restaurants or dishes |
reviews |
Customer reviews with optional photos |
notifications |
In-app notification inbox |
pos_invoices |
Walk-in POS sales |
analytics |
Daily per-restaurant metrics |
- user_role:
customer,restaurant_owner,restaurant_staff,cashier,admin,super_admin - order_status:
pending→confirmed→preparing→out_for_delivery→delivered(orcancelled/refunded) - payment_method:
cod,card,stripe,jazzcash,easypaisa - payment_status:
pending,paid,failed,refunded
RLS is enabled on all public tables. Key rules:
- Customers can only read/write their own carts, addresses, orders, and favorites.
- Restaurant staff (owner, staff, cashier) access data for restaurants they own or are assigned to via
staff_restaurant_ids(). - Admins / super_admins bypass restrictions via
is_admin(). - Public read is allowed for approved restaurants, active categories, and available menu items.
Helper functions (security definer):
is_admin()— true if current user hasadminorsuper_adminrolestaff_restaurant_ids()— restaurant IDs the current user can manage
Created in 0001_initial_schema.sql:
| Bucket | Public | Purpose |
|---|---|---|
food-images |
Yes | Dish photos |
restaurant-images |
Yes | Restaurant logo/cover |
user-avatars |
Yes | Profile pictures |
review-photos |
No | Review attachments (owner/admin read) |
These tables broadcast changes via Supabase Realtime (used by web order dashboard):
ordersnotificationsanalyticspos_invoices
When a user signs up (email or OAuth), migration 0004 runs handle_new_user() to insert a profiles row with role customer. Migration 0006 adds an INSERT RLS policy so the mobile app can optionally upsert the same row when a session exists (PostgREST upsert requires INSERT privilege).
| Function | Method | Purpose |
|---|---|---|
dispatch-notification |
POST | Insert in-app notification (user_id, title, body, type, data) |
payment-hook |
POST | Upsert payment record from external payment provider webhook |
scheduled-analytics |
GET/POST | Aggregate today's orders per restaurant into analytics table |
email-workflows |
POST | Queue email workflow and log a notification (email, workflow, payload) |
All Edge Functions require SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY as Deno environment secrets (set in Supabase dashboard → Edge Functions → Secrets).
Example — dispatch a notification:
curl -X POST http://127.0.0.1:54321/functions/v1/dispatch-notification \
-H "Authorization: Bearer <service-role-key>" \
-H "Content-Type: application/json" \
-d '{"user_id":"<uuid>","title":"Order update","body":"Your order is preparing"}'supabase/config.toml sets:
site_url = "http://localhost:3000"(web app)additional_redirect_urls = ["gbfoodhub://auth"](mobile OAuth deep link)
| Package | Purpose |
|---|---|
flutter_riverpod / riverpod_annotation |
State management |
go_router |
Declarative routing |
supabase_flutter |
Auth + database |
flutter_dotenv |
Environment config |
dio |
HTTP client (Edge Functions / future APIs) |
firebase_messaging + flutter_local_notifications |
Push notifications |
flutter_secure_storage |
Secure token storage |
cached_network_image, shimmer |
Image loading + skeleton UI |
freezed / json_serializable |
Code generation for models |
lib/features/<feature>/
├── data/ # Supabase repositories (API calls)
├── domain/ # Repository interfaces, entities
└── presentation/ # Pages, Riverpod providers
Features:
| Feature | Key files | Supabase tables |
|---|---|---|
| auth | supabase_auth_repository.dart, auth_pages.dart |
auth.users, profiles |
| home | catalog_repository.dart, home_page.dart |
categories, restaurants, food_items |
| cart | cart_notifier.dart, cart_providers.dart |
In-memory + optional carts |
| orders | supabase_order_repository.dart, order_pages.dart |
orders, order_items, payments |
| profile | address_repository.dart, profile_pages.dart |
profiles, addresses |
Defined in mobile_app/lib/core/routing/app_router.dart:
| Route | Screen |
|---|---|
/splash, /onboarding |
App intro |
/login, /register, /forgot-password, /otp |
Authentication |
/ |
Home (featured restaurants, categories) |
/search, /restaurants |
Discovery |
/restaurant/:id, /food/:id |
Detail pages |
/cart, /checkout, /success |
Ordering flow |
/orders, /tracking/:id |
Order history & live tracking |
/favorites, /notifications |
Saved items & alerts |
/profile, /edit-profile, /change-password |
Account |
/addresses, /settings, /reviews, /write-review |
Profile extras |
main.dartloads.envviaflutter_dotenvbootstrap.dartcallsSupabase.initialize()with URL + anon keyProviderScopewraps the app;GoRouterhandles navigation- Theme from
core/theme/app_theme.dart(light + dark)
After changing Riverpod/Freezed/JSON models:
cd mobile_app
dart run build_runner build --delete-conflicting-outputs# List devices
flutter devices
# Android emulator / physical device
flutter run -d <android-device-id>
# iOS simulator (macOS only)
flutter run -d <ios-device-id>
# Chrome (Flutter web)
flutter run -d chrome
# Release builds
flutter build apk --release
flutter build appbundle --release # Play Store
flutter build ipa --release # App Store (macOS + Xcode)NotificationService uses Firebase Cloud Messaging + local notifications for foreground messages. For production:
- Create a Firebase project
- Add
google-services.jsontomobile_app/android/app/ - Add iOS Firebase config and enable push capabilities in Xcode
- Set
FCM_SERVER_KEYor provider credentials for thedispatch-notificationEdge Function
| Package | Purpose |
|---|---|
| Next.js 15 (App Router) | Framework |
| React 19 | UI |
@supabase/ssr |
Server + browser Supabase clients with cookie auth |
| Tailwind CSS | Styling |
| Recharts | Analytics charts |
| Lucide React | Icons |
web/app/
├── page.tsx # Redirects to /dashboard
├── (auth)/
│ ├── login/ # Staff / admin login
│ ├── signup/ # Customer registration
│ ├── register/ # Restaurant partner registration
│ └── forgot-password/ # Password reset
├── welcome/ # Customer landing after sign-up
├── auth/callback/ # OAuth redirect handler
├── dashboard/ # Restaurant owner/staff area
│ ├── page.tsx # Overview stats
│ ├── orders/ # Live order management
│ ├── menu/ # CRUD menu items
│ ├── categories/ # Category management
│ ├── restaurants/ # Restaurant profile
│ ├── analytics/ # Charts
│ ├── revenue/ # Revenue breakdown
│ └── settings/ # Account settings
├── admin/ # Super admin panel
└── pos/ # Point-of-sale terminal
| Path | Purpose |
|---|---|
lib/supabase.ts |
Browser Supabase client |
middleware.ts |
Auth guard for /dashboard, /admin, /pos |
lib/hooks/use-workspace.ts |
Loads current user profile + assigned restaurant |
lib/api/menu.ts |
Menu CRUD |
lib/api/orders.ts |
Order queries and status updates |
lib/api/restaurants.ts |
Restaurant management + admin approval |
lib/api/analytics.ts |
Analytics data |
lib/api/pos.ts |
POS invoice creation |
lib/realtime.ts |
Subscribe to order changes per restaurant |
lib/types.ts |
Shared TypeScript types matching DB schema |
- User logs in at
/loginwith email + password (login-form.tsx) resolvePostLoginPath()routes by role: admin →/admin, staff →/dashboard, customer →/welcome- Supabase session stored in cookies via
@supabase/ssr middleware.tsguards/dashboard,/admin,/pos— redirects unauthenticated users to/login; customers to/welcome- Admin routes (
/admin) requireprofiles.roleofadminorsuper_admin useWorkspaceresolves the restaurant context: owned → staff assignment → admin fallback
See Full Flow for complete customer, staff, admin, and POS journeys.
useWorkspace determines which restaurant the dashboard operates on:
- Restaurant where
owner_id = current user - Else, restaurant from
restaurant_staffassignment - Else (admin only), first approved restaurant as fallback
The orders dashboard uses subscribeToRestaurantOrders() to listen for orders table changes filtered by restaurant_id. The UI refreshes automatically when order status changes from the mobile app or POS.
npm run dev # Start dev server (port 3000)
npm run build # Production build
npm run start # Run production server
npm run lint # ESLint
npm run typecheck # TypeScript check
npm run setup:demo # Create demo owner + cashier accounts
# Supabase helpers (run from web/)
npm run supabase:start
npm run supabase:stop
npm run supabase:status
npm run supabase:reset
npm run supabase:push
npm run supabase:pull
npm run supabase:newThe scripts/ folder contains operational helpers and notes.
Creates demo restaurant owner and cashier accounts in your Supabase project (local or remote):
cd web
npm run setup:demoSee Full Flow — Platform setup for credentials and what it creates.
Documents how demo seed data works:
- Migration
0002_seed_demo_data.sqlinserts a Gilgit Branch restaurant with traditional GB dishes (Chapshuro, Yak Skewers, Dowdo Soup, etc.), sample orders, and 7 days of analytics. - Seed data depends on an existing
super_adminprofile. In production, create users through Supabase Auth first, then update seededowner_id/user_idreferences to match real auth user UUIDs. - Categories, food items, orders, and analytics are only inserted if they do not already exist (idempotent).
| Variable | Required | Description |
|---|---|---|
SUPABASE_URL |
Yes | Supabase project URL (local: http://127.0.0.1:54321) |
SUPABASE_ANON_KEY |
Yes | Public anon key (safe in client) |
API_BASE_URL |
Yes | Edge Functions base URL (<SUPABASE_URL>/functions/v1) |
| Variable | Required | Description |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Yes | Supabase project URL (exposed to browser) |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Yes | Public anon key |
SUPABASE_SERVICE_ROLE_KEY |
Server only | Bypasses RLS — never prefix with NEXT_PUBLIC_ |
| Secret | Required | Description |
|---|---|---|
SUPABASE_URL |
Yes | Auto-injected in hosted Supabase |
SUPABASE_SERVICE_ROLE_KEY |
Yes | For server-side DB writes |
FCM_SERVER_KEY |
Production | Firebase push dispatch (optional locally) |
| Client | Route | Method | Profile creation |
|---|---|---|---|
| Web (customer) | /signup |
Email/password, Google OAuth | Auth trigger → role customer → /welcome |
| Web (partner) | /register |
Email/password, Google OAuth | Auth trigger → role customer (admin upgrades later) |
| Mobile | /register |
Email/password, Google OAuth | Auth trigger + app upsert → role customer |
| Web (staff) | /login |
Email/password only | Existing staff accounts from setup or admin |
| Role | Mobile app | Web dashboard | Admin panel | POS |
|---|---|---|---|---|
customer |
Full ordering | No access* | No | No |
restaurant_owner |
Ordering | Full dashboard for owned restaurant | No | Yes |
restaurant_staff |
Ordering | Dashboard for assigned restaurant | No | Yes |
cashier |
Ordering | Limited dashboard | No | Yes |
admin |
Ordering | Dashboard (fallback restaurant) | Yes | Yes |
super_admin |
Ordering | Dashboard (fallback restaurant) | Yes | Yes |
*Middleware redirects customers away from /dashboard, /admin, and /pos to /welcome. Staff areas require authentication.
- Open Supabase dashboard → Authentication → Providers → enable Google
- Add OAuth redirect URLs:
http://localhost:3000(local web)- Your production web URL
gbfoodhub://auth(mobile deep link — configure in Android/iOS)
- For mobile OAuth, set
redirectTo: 'gbfoodhub://auth'insignInWithGoogle()
Local dev (Supabase Studio):
- Open
http://127.0.0.1:54323 - Create user in Authentication → Users
- In Table Editor →
profiles, setroletosuper_adminorrestaurant_owner - For owners, insert a row in
restaurantswithowner_id = user UUIDandis_approved = true
Production:
- Sign up normally, then update
profiles.rolevia SQL or Studio - Link restaurant:
insert into restaurants (owner_id, name, slug, ...) values (...)
For step-by-step walkthroughs, see Full Flow.
sequenceDiagram
participant C as Mobile App
participant S as Supabase
participant W as Web Dashboard
C->>S: Sign in (Auth)
C->>S: Browse restaurants / food_items (RLS: public approved)
C->>S: Add to cart (local state)
C->>S: Place order → insert orders + order_items
S-->>W: Realtime broadcast on orders table
W->>S: Staff updates order status
S-->>C: Customer sees updated status in /tracking/:id
- Owner logs in at
/login useWorkspaceloads their restaurant- Menu page reads/writes
food_itemsfiltered byrestaurant_id - RLS ensures staff can only edit their assigned restaurant's items
- Cashier opens
/pos - Adds items to cart from
food_items createPosInvoice()inserts intopos_invoices- Invoice appears in analytics/revenue reports
- Super admin opens
/admin fetchPendingRestaurants()loads restaurants whereis_approved = false- Approve sets
is_approved = true— restaurant becomes visible in mobile app
supabase db reset- Insert into
categoriesvia web Categories page or SQL - Mobile app reads active categories automatically via
CatalogRepository.fetchCategories()
Follow the End-to-end test checklist in Full Flow.
Quick version:
- Run
npm run setup:demoand create a customer at/signup - Log into web dashboard as
owner@gilgit.gb - Place order on mobile → watch it appear on
/dashboard/orders - Update status on web → verify tracking page on mobile
supabase functions new my-function
# Edit supabase/functions/my-function/index.ts
supabase functions serve my-function # local test
supabase functions deploy my-function # remote- Apply migration
- Update
web/lib/types.tsto match new columns/enums - Update Flutter models in
mobile_app/lib/shared/models/
cd web
npm run lint
npm run typecheckcd mobile_app
flutter analyzeUI follows warm orange accents, soft white cards, GB-local food imagery, rounded mobile surfaces, and compact dashboard cards.
| App | Theme location |
|---|---|
| Mobile | mobile_app/lib/core/theme/app_theme.dart |
| Web | web/app/globals.css, web/tailwind.config.ts |
Brand tokens in web CSS use names like brand-burnt, brand-cocoa, brand-sand.
See docs/deployment.md for the production checklist. Summary:
- Create project at supabase.com
- Link CLI:
supabase link --project-ref <ref> - Push migrations:
supabase db push - Verify storage buckets exist (created by migration, or create manually in dashboard)
- Deploy all Edge Functions and set secrets
- Configure Auth redirect URLs for production domains
- Set production
SUPABASE_URLandSUPABASE_ANON_KEYinmobile_app/.env - Configure Firebase for push notifications
- Build release artifacts:
flutter build appbundle --release # Google Play
flutter build ipa --release # App StoreDeploy to Vercel, Netlify, or any Node host:
cd web
npm ci
npm run build
npm startSet environment variables in the hosting dashboard. Add your production URL to Supabase Auth redirect allowlist.
| Issue | Solution |
|---|---|
supabase start fails |
Ensure Docker Desktop is running |
| Mobile can't connect to Supabase | Check SUPABASE_URL in .env; use http://127.0.0.1:54321 for local (not localhost on some Android emulators — use 10.0.2.2:54321 for Android emulator) |
429 Too Many Requests on signup |
Supabase rate-limits repeated sign-ups; wait 1 minute or use a new email |
| Web dashboard shows no restaurant | Ensure user has restaurant_owner role + restaurants row, or restaurant_staff assignment |
| Orders not updating live on web | Confirm Realtime is enabled; check subscribeToRestaurantOrders subscription |
| RLS permission denied | Verify user role and restaurant assignment; test query in Studio with user's JWT |
build_runner conflicts |
Run dart run build_runner build --delete-conflicting-outputs |
| Android emulator Supabase URL | Use http://10.0.2.2:54321 instead of 127.0.0.1 |
# Supabase logs
supabase functions logs dispatch-notification
# Flutter verbose run
flutter run -v
# Web production build test
cd web && npm run build && npm startPrivate project — GB FoodHub, Gilgit Baltistan.
login creadential
Demo credentials (after running setup script): Owner: owner@gilgit.gb / GilgitOwner2026! Cashier: cashier@gilgit.gb / GilgitCashier2026!
// update Demo accounts are ready on your remote Supabase project.
Login at http://localhost:3000/login
Role Email Password Owner owner@gilgit.gb GilgitOwner2026! Cashier cashier@gilgit.gb GilgitCashier2026!
new Repo mein default admin login nahi hai — sirf owner/cashier demo accounts hain.
Demo logins (web /login) Role Email Password Restaurant owner owner@gilgit.gb GilgitOwner2026! Cashier cashier@gilgit.gb GilgitCashier2026! Login: http://localhost:3000/login Owner → /dashboard (menu, orders, banners UI, etc.)
Agar ye accounts nahi chalte, pehle ye chalao:
cd web npm run setup:demo Admin / super_admin Koi ready admin@... password seed nahi hai. Khud banana padta hai:
Supabase Dashboard → apna project → Authentication → Users → Add user Email + password set karo (jo chaho) Table Editor → profiles → us user ki row mein role = super_admin (ya admin) Phir /login pe wohi email/password se login → /admin / banners manage Note: Promo banners RLS sirf admin / super_admin ko write allow karti hai. Owner se banner save fail ho sakta hai — banners ke liye super_admin role zaroori hai.
new update Step 1 — SQL run karo Supabase Dashboard → SQL Editor → scripts/create-demo-auth-accounts.sql ka pura content paste → Run
Step 2 — Login credentials Role Email Password Opens Super Admin superadmin@gilgit.gb GilgitSuperAdmin2026! /admin Admin admin@gilgit.gb GilgitAdmin2026! /admin Owner owner@gilgit.gb GilgitOwner2026! /dashboard Cashier cashier@gilgit.gb GilgitCashier2026! /pos Login: http://localhost:3000/login Role dropdown se Super Admin / Admin select karo → email/password auto-fill → Login.