PrepFlow is a pre-order and kitchen-prep automation system for Thai Joint PH, a micro food enterprise in Bacoor, Cavite. It replaces the owner's scattered Facebook/Instagram DM order pipeline with a single order lifecycle, a screenshot-based payment-verification workflow, and an auto-calculated ingredient procurement list.
Two clients share one PHP backend:
- Customer + Admin web app — a responsive React SPA (Vite).
- Customer mobile app — the same React build wrapped in Capacitor, with native camera access for payment screenshots.
For the Structurizr C4 model, deployment view, DFD, order flowchart, sequence diagram, and current ERD, see SYSTEM-DIAGRAMS.md. The C4 source of truth is architecture/workspace.dsl.
┌──────────────────────┐ ┌──────────────────────┐
│ React SPA (Vite) │ │ Capacitor Android │
│ customer + admin │ │ (wraps SPA build) │
│ TanStack Query │ │ Camera plugin │
└─────────┬────────────┘ └──────────┬───────────┘
│ fetch (JSON, cookies) │
└───────────────┬──────────────────┘
▼
┌───────────────────────────┐
│ PHP 8 + Laravel API │
│ sessions + CSRF │
│ JSON responses │
│ PDO prepared stmts │
└───┬───────────────────┬───┘
│ │
▼───────┘ └────────▼
┌──────────────┐ ┌──────────────┐
│ MySQL/MariaDB│ │ Cloudinary │
│ 10 tables │ │ image store │
└──────────────┘ └──────────────┘
- Role-routed SPA. Customer shell: menu, cart, checkout, payment upload, order tracker, history. Admin shell: order dashboard, payment verification queue, prep summary, procurement list, menu CRUD, settings, analytics.
- All data operations go through TanStack Query mutations hitting the PHP API; no full page reloads. Loading skeletons and error toasts on every async path.
- Responsive via Tailwind breakpoints (mobile ≤576px, tablet ≤992px, desktop >992px). Cross-browser: Chrome, Firefox, Edge.
- Capacitor wraps the SPA
dist/. The Camera plugin captures payment screenshots natively, satisfying the mobile-native-feature requirement.
- RESTful JSON endpoints grouped by domain:
auth,menu,orders,payments,prep,analytics,settings. - Session-based auth:
session_regenerate_id(true)on login, idle timeout, role stored in session, per-route middleware guard. Passwords hashed withpassword_hash(bcrypt); login throttled. - JSON responses use an explicit trait:
header('Content-Type: application/json'); echo json_encode($payload);— keeping the graded primitives visible rather than hidden behind Laravel'sresponse()->json(). - CRUD uses PDO prepared statements via Laravel's
DB::connection()->getPdo()->prepare(...)for the rubric-sampled paths; Eloquent elsewhere.
- 10 tables, 3NF, FK constraints, composite indexes on
orders(fulfillment_date, status)andorders(user_id, status). Seedb/schema.sqlanddocs/ERD.md. - The procurement list is the
v_daily_procurementview aggregatingrecipe_ingredients × order_items × ordersfor confirmed orders by fulfillment date.
- Cloudinary for payment-proof and menu image hosting. PHP signs uploads; the client uploads via a signed endpoint. No images are stored on the PHP host.
- GCash / bank / couriers are out-of-band — not API-integrated.
- Customer opens app → SPA
GET /api/menu→ Laravel returns JSON of availablemenu_items. - Customer adds to cart, selects fulfillment mode + date → SPA
GET /api/batches?date=→ Laravel returns capacity/cutoff → UI renders capacity meter. - Checkout →
POST /api/orders(JSON) → Laravel validates, checks batch capacity + cutoff inside a transaction, insertsorders+order_items, returns the order. - Payment screen → customer uploads screenshot →
POST /api/orders/{id}/payment-proof→ Laravel signs a Cloudinary upload → stores URL on the order, setsstatus=payment_uploaded. - Admin opens verification queue →
GET /api/admin/orders?status=payment_uploaded→ verifies →PATCH /api/admin/orders/{id}/status→status=confirmed, audit log written. - Admin opens prep summary →
GET /api/admin/prep?date=→ Laravel queriesv_daily_procurement→ returns aggregated ingredient list.
- Sessions:
session_regenerate_id(true)on auth level change,cookie_httponly,cookie_securebehind HTTPS,use_strict_mode, idle timeout. - Auth: bcrypt hashes; login throttle; role checks on every protected endpoint via middleware.
- Output: all session/DB data escaped on output (Blade
{{ }}/htmlspecialchars) to prevent XSS. - Input: server-side validation (Form Requests) + client-side (zod); prepared statements prevent SQL injection.
- CSRF tokens on session mutations.
| Rubric section (pts) | Where it is satisfied |
|---|---|
| I. Documentation & planning (15) | docs/ (ERD, DFD, architecture, user manual), DECISIONS.md, README.md |
| II. Responsive web (20) | React SPA, Tailwind breakpoints, cross-browser, a11y |
| III. PHP sessions (15) | Laravel session config, login controller (regenerate_id), role middleware, timeout |
| IV. AJAX (20) | TanStack Query fetch to PHP endpoints; loading skeletons; .catch/error toasts; PHP returns structured JSON |
| V. JSON (15) | PHP json_encode + Content-Type; JS .json() parsing; client + server validation |
| VI. Database (10) | db/schema.sql, Laravel migrations, PDO prepared statements, 3NF, indexes |
| VII. Mobile (15) | Capacitor APK talks to PHP REST via JSON; native Camera for payment proof |
| VIII. Presentation (10) | Demo flow + technical-defense notes in docs/ |
PrepFlow/
├── apps/
│ ├── web/ # Vite React SPA (customer + admin)
│ └── mobile/ # Capacitor shell
├── api/ # PHP Laravel backend
├── db/ # schema.sql, seed.sql
├── packages/
│ └── shared/ # TS types + zod schemas mirroring DB
├── docs/ # ERD, DFD, architecture, user manual, planning
├── tools/ # composer.phar
├── DECISIONS.md
├── README.md
└── package.json # bun workspaces
- Dev: XAMPP MySQL +
php artisan serve(api) +bun run dev(web). Capacitor syncs from the web build. - Prod: API on a PHP host / VPS; SPA on Vercel; mobile as a Capacitor APK. Cloudinary for images.