workforcepilot-mobile

WorkforcePilot Mobile — Developer Handoff

A React Native (Expo) starting point for the agent-facing mobile app: view shifts, request shift swaps, bid on overtime, submit holiday requests, and receive push notification broadcasts.

This connects to the same live Supabase project the existing web app uses — same tables, same auth users, same data. It is not a mockup.


What’s actually working right now

What’s genuinely NOT done — and why

The push pipeline is code-complete but not yet deployed or tested against a live device. Everything described above exists as real, working code — the function’s logic (lookup → chunk → POST to Expo’s documented API) is correct — but it has never actually fired a notification, because that needs:

  1. The device_push_tokens table actually created in your Supabase project (supabase/migrations/001_device_push_tokens.sql — not yet run)
  2. The Edge Function actually deployed (supabase functions deploy send-push-broadcast — not yet run)
  3. A real Expo/EAS project with Apple Developer + Firebase credentials, so the mobile app can actually register a real device token
  4. A physical device with the app installed (push cannot be tested in a simulator)

None of this is guesswork or a stub — it’s real code waiting on deployment and credentials only you can provide. Budget time for the deploy-and-test cycle specifically, since the first real send is where integration issues (a wrong project ref, a missing permission, a malformed token) tend to surface, not in the code itself.

Time Off approval-rules engine. The web app runs a leave request through notice-period and capacity checks before deciding whether it auto-approves. This app submits every request as pending and lets your existing supervisor approval screen handle the decision — correct and safe, just without the web app’s auto-approval fast path. Documented inline in TimeOffScreen.tsx if you want to add it later.

App icons, splash screen, real branding assets. app.json points at placeholder paths (./assets/icon.png etc.) that don’t exist yet.

Bundle identifiers. app.json has com.CHANGEME.workforcepilot for both iOS and Android — replace with your actual reverse-DNS identifier before building for real.


Getting it running

npm install
npx expo start

Scan the QR code with Expo Go on a physical phone (fastest way to see it running), or press i/a for a simulator — though remember push notifications need a real device regardless.

Deploying the push backend

# One-time: link this project to your actual Supabase project
npx supabase login
npx supabase link --project-ref hajeuznaysjkvoykalfo

# Run the migration (creates device_push_tokens)
npx supabase db push

# Deploy the Edge Function
npx supabase functions deploy send-push-broadcast

After that, the “Send Push Broadcast” card in OrgSettings.html’s Notifications tab will actually reach real devices — once the mobile app itself has a real EAS/Apple/Firebase setup and at least one agent has signed in and granted notification permission on a physical phone.


Real gotchas worth knowing before you touch this code

Never select('*') on agent_profiles. This is not a style preference — it’s a documented bug the web app’s own code already hit. The _password column has SELECT revoked for anon/authenticated roles, and Postgres rejects the entire query if any requested column is forbidden, not just that one. The web app’s login silently fell back to role 'agent' for every user because of exactly this, caught by an empty catch block. src/lib/auth.tsx lists columns explicitly for this exact reason — if you add a column to that list, make sure it isn’t permission-restricted first.

Overtime opportunities are not a table. They live as a JSON array inside org_settings under the key wfm_ot_opportunities. Only individual bids (overtime_bids) are a real table. This is confirmed directly against the web app’s own code, not assumed — don’t “fix” this into a proper table without checking whether the web app (and any admin tooling that manages opportunities) needs updating too.

Schedule days use dayIndex, not a date field. A day in agent_schedules[].schedule[] is {dayIndex, working, activities} — the actual calendar date is computed as run.start_date + dayIndex days, not stored directly. ShiftsScreen.tsx does this correctly; if you build more schedule-reading screens, copy that pattern rather than looking for a date field that doesn’t exist.

The 'Aptos' font isn’t available on mobile out of the box. The web app’s font stack is 'Aptos', 'Segoe UI', system-ui, sans-serif — Aptos is a Microsoft font, not something you can assume is on a phone. This project falls back to each platform’s native system font (same category of fallback the web app’s own CSS already does). If pixel-identical type matters, you’ll need to license and bundle Aptos via expo-font.


Project structure

src/
  theme/         Design tokens (ported from the real web app CSS) + light/dark context
  lib/           Supabase client, auth, push notification registration
  navigation/    Root navigator (login vs. main app) + bottom tabs
  screens/       The five screens: Login, Shifts, ShiftSwap, OvertimeBid, TimeOff
  components/    Shared UI: Button, Card, StatusPill, EmptyState, Screen wrapper
  types/         TypeScript types, confirmed against real Supabase queries

Suggested next steps, roughly in priority order

  1. Get it running against a real test account, confirm the four screens genuinely show real data for a real agent
  2. Real app icon / splash / bundle identifiers
  3. Push notification backend (the Edge Function + table + EAS/Apple/ Firebase setup)
  4. eas build for an internal TestFlight / Play internal testing build
  5. Polish pass — loading states, error messaging tone, pull-to-refresh everywhere, haptics on key actions
  6. App Store / Play Store submission