Executive Summary
While the CleanNami Platform handles customer bookings, the Field Ops App is the mission-critical tool used daily by the workforce. It transforms a complex logistical challenge—managing distributed independent contractors—into an automated, self-regulating system.
Core Achievement: The application successfully decentralizes quality control. By enforcing a strict "Evidence-Based" workflow (GPS + Photos + Checklists), the app ensures standards are met without on-site managers, enabling the platform to scale indefinitely while maintaining high reliability.
Technical Architecture
Tech Stack
- Frontend: React 18 with
react-router-dom(SPA Architecture) - Build Tool: Vite
- UI System: TailwindCSS, Shadcn UI, Lucide React
- Backend/Data: Supabase (PostgreSQL, Realtime, Edge Functions)
- State Management: React Context + Custom Hooks
- Native Features: Capacitor (Camera, Geolocation, Push Notifications)
- Payments: Stripe Connect (Express Accounts)
Architecture Highlights
- Offline-First Capabilities:
JobWorkflowstate restoration ensures data isn't lost if the browser refreshes or loses signal during a clean. - Real-time Synchronization: Supabase Realtime channels listen for job swaps and availability updates instantly.
- RPC-Heavy Logic: Complex business rules (like "swap eligibility" and "reliability scoring") are offloaded to Postgres RPCs to keep the client light and secure.
- Optimistic Locking: Prevents race conditions in the job swap market without complex server-side mutexes.
The "Proof-of-Work" Execution Engine
Feature Overview
The core of the application is a rigid, 4-stage state machine that prevents a cleaner from getting paid until the job is proven complete. This replaces the need for a human supervisor.
File: JobWorkflow.tsx
The Workflow State Machine:
- Instruction Phase: Cleaner reviews notes, access codes, and property specifics.
- Evidence Phase (Arrival):
- GPS Lock:
useLocationTrackinglogs coordinates against property geofence. - Before Photos: Camera capture required for pre-existing damage.
- In-Progress: Timer runs; cleaner executes task list.
- Completion Phase:
- After Photos: Mandatory upload of specific zones (Kitchen, Bath, etc.).
- Checklist Validation: All
requiredboolean flags must be true. - Capture: Triggers
completeJobAndCaptureAPI.
Code Highlight: Evidence Validation
The app uses a dynamic validation logic that scales requirements based on property size (e.g., larger homes require more photos).
// JobWorkflow.tsx
const isEvidenceComplete = () => {
const requiredChecklist = checklist.filter((item) => item.required);
// Dynamic requirements based on property size
// e.g. 2 photos per bath * 3 baths = 6 photos required
const beforePhotosMet = beforePhotos.length >= totalRequiredBefore;
const afterPhotosMet = afterPhotos.length >= totalRequiredAfter;
const checklistMet = requiredChecklist.every((item) => item.completed);
return beforePhotosMet && afterPhotosMet && checklistMet;
};
Technical Challenge: Taming Timezones
The Problem: "It’s Always 6:00 PM in Florida"
To ensure adequate workforce coverage, cleaners must submit their availability for the upcoming two weeks by Sunday at 6:00 PM ET. If they miss this hard deadline, they enter a "Grace Period" until 7:00 PM ET.
Relying on the user's device time (new Date()) was insecure; a cleaner could change their phone's clock or use a VPN to bypass the deadline.
The Solution: Server-Authoritative Logic
We standardized all business logic to ignore the device's local time in favor of a calculated "Target Time." We utilize a utility function, getFloridaDateTime(), which forces the application to evaluate time based on the market's timezone.
File: Availability.tsx
// Availability.tsx
const calculateSubmissionState = (bypass: boolean = false) => {
// 1. Get standardized time, ignoring device locale and VPNs
const now = getFloridaDateTime();
const currentHour = now.getHours();
// 2. Enforce strict business hours (Florida Time)
// Check if today is Sunday and if it matches the bi-weekly epoch cycle
if (now.getDay() === 0 && isSubmissionSunday(now)) {
if (currentHour < 18) return { state: 'open' }; // Before 6:00 PM ET
if (currentHour < 19) return { state: 'grace' }; // 6:00 PM - 7:00 PM ET
}
// 3. Fallback to finding the next valid submission window
const nextSub = getNextSubmissionSunday(addDays(today, 1));
return {
state: 'closed',
nextSubmission: nextSub
};
};
Decentralized Job Swapping
Feature Overview
Cleaners often have emergencies. Instead of calling support, they use the Swap Market to trade shifts. This system must handle high concurrency—if two cleaners try to accept the same job simultaneously, only one can succeed.
Files: JobDetails.tsx, useAvailableSwaps.ts
Solution: Optimistic Locking
We implemented a robust optimistic locking pattern at the database level using Supabase. We do not use a "Check then Update" pattern, which is prone to race conditions. Instead, the UPDATE query itself enforces exclusivity.
// useAvailableSwaps.ts
const acceptSwap = async (swapId: string) => {
const { data, error } = await supabase
.from('swap_requests')
.update({
replacement_cleaner_id: cleanerId,
status: 'accepted'
})
.eq('id', swapId)
.eq('status', 'pending') // <--- THE OPTIMISTIC LOCK
.select()
.single(); // <--- Expects exactly 1 row to change
if (!data) throw new Error("Swap was already taken or expired.");
toast({ title: "Swap Accepted!", description: "Job added to your schedule." });
}
Why this works:
- Atomic Operation: The database executes the
UPDATEstatement atomically. - Zero Rows Affected: If a second user tries to accept, the
statusis no longer 'pending', so the query updates 0 rows. - Graceful Failure: The app catches the
nullreturn and informs the user the job is gone.
Compliance-First Onboarding
The app creates a legal firewall for the platform. A cleaner cannot access the dashboard or see jobs until the multi-stage onboarding is complete.
File: Onboarding.tsx
- Profile Setup: Basic demographics and experience validation.
- Stripe Connect: Direct integration with Stripe's onboarding flow to establish a connected Express account for payouts.
- Legal Signing: Digital signing of Contractor Agreements, W9s, and Liability Waivers. Documents are stored in Supabase Storage with strict RLS policies.
- Capabilities: Self-certification for premium skills (e.g., Hot Tub Maintenance) which unlocks higher-paying job tiers.
Payment Flow Sequence
The interaction below illustrates how the frontend triggers the payment capture only after evidence is secured.
sequenceDiagram
participant C as Cleaner App (JobWorkflow.tsx)
participant S as Supabase (DB & Storage)
participant E as Edge Function (Payment)
participant ST as Stripe API
Note over C: User taps "Submit Evidence & Complete"
C->>S: Upload Photos (Before/After)
S-->>C: Return Public URLs
C->>S: UPDATE evidence_packets (status: 'complete')
C->>E: POST /complete-job-and-capture (jobId)
rect rgb(240, 240, 240)
Note right of E: Server-Side Validation
E->>S: Verify evidence completeness
E->>ST: Stripe.PaymentIntents.capture(paymentIntentId)
ST-->>E: Capture Success
E->>S: INSERT into payouts (status: 'pending')
E-->>C: Return { success: true }
end
C->>S: DELETE from jobs (clean local view)
C->>C: Toast: "Payment Captured!"
Conclusion
The CleanNami Field Ops app is an automated manager. By codifying business rules—like the "Submission Sunday" deadline and the "Evidence Packet" requirement—directly into the React logic, the platform achieves operational consistency that usually requires a large human operations team.
It demonstrates a mastery of Supabase-centric architecture, where the frontend acts as a high-fidelity interface for complex backend logic (RPCs) and real-time data streams.