Build a complete order tracking and management system for laundry and dry cleaning businesses
* Preview is for reference only. Actual results may vary depending on the AI model, variable values, and tools used.
You are a professional full-stack developer. Build a complete laundry order tracking and management system for a Malaysian laundry and dry cleaning shop. This system replaces the current manual workflow of WhatsApp group chats and handwritten paper tags.
## Shop Configuration
- Shop Name: {{shop_name}}
- Service Types & Pricing: {{service_types}}
- Standard Turnaround: {{turnaround_days}} days
- Express Turnaround: {{express_hours}} hours
## Tech Stack
- Next.js 14 (App Router) + TypeScript
- Tailwind CSS + shadcn/ui component library
- Supabase (PostgreSQL + Row Level Security)
- SMS/WhatsApp notifications via Twilio or a Malaysian SMS gateway (e.g. Nexmo / Vonage)
- QR code generation: qrcode.react library
- Receipt/tag printing: native window.print() with thermal printer CSS (@media print)
## Database Schema
Create the following tables in Supabase. Always manually create indexes on all foreign key columns (Postgres does NOT auto-index FKs). Enable RLS on every table.
### Table: customers
- id: uuid, primary key, default gen_random_uuid()
- phone: text, unique, not null — primary identifier, format +601X-XXXXXXX
- name: text, not null
- email: text, nullable
- total_orders: integer, default 0 — used for loyalty tier calculation
- loyalty_discount_pct: numeric(5,2), default 0 — auto-updated when total_orders crosses thresholds
- created_at: timestamptz, default now()
### Table: orders
- id: uuid, primary key, default gen_random_uuid()
- order_number: text, unique, not null — format ORD-YYYYMMDD-NNN (e.g. ORD-20240101-001, sequence resets daily)
- customer_id: uuid, references customers(id), index required
- status: text — enum: received | washing | drying | ironing | quality_check | ready | collected
- service_type: text — enum: wash_fold | dry_clean | express | iron_only
- is_express: boolean, default false
- subtotal: numeric(10,2)
- express_surcharge: numeric(10,2), default 0 — calculated as subtotal × 30% when is_express = true
- discount_amount: numeric(10,2), default 0
- total: numeric(10,2) — subtotal + express_surcharge - discount_amount
- payment_status: text — pending | paid | partial
- received_at: timestamptz, default now()
- due_date: timestamptz, not null — standard: now() + {{turnaround_days}} days; express: now() + {{express_hours}} hours
- completed_at: timestamptz, nullable — set when status changes to ready
- collected_at: timestamptz, nullable — set when status changes to collected
- staff_notes: text, nullable
- sms_sent: boolean, default false — set to true after ready notification is dispatched
- created_at: timestamptz, default now()
### Table: order_items
- id: uuid, primary key
- order_id: uuid, references orders(id) ON DELETE CASCADE, index required
- item_type: text — shirt | pants | jacket | dress | coat | bedsheet | pillowcase | curtain | towel | other
- item_label_zh: text — Chinese display name corresponding to item_type
- quantity: integer, not null
- unit_price: numeric(10,2), not null — pulled from pricing_rules at time of order creation
- special_instructions: text, nullable — e.g. "gentle wash", "remove oil stain", "starch collar"
- created_at: timestamptz, default now()
### Table: pricing_rules
- id: uuid, primary key
- item_type: text, not null
- service_type: text, not null
- unit_price: numeric(10,2), not null
- updated_at: timestamptz, default now()
- UNIQUE constraint on (item_type, service_type)
Seed with default pricing based on: {{service_types}}
### Table: payments
- id: uuid, primary key
- order_id: uuid, references orders(id), index required
- amount: numeric(10,2), not null
- method: text — cash | tng_ewallet | duitnow | card
- paid_at: timestamptz, default now()
- received_by: text — staff member name who processed payment
### RLS Policies
- authenticated role: full read/write on all tables (staff)
- anon role: SELECT on orders and order_items filtered by phone (via customers join) or order_number — for public order tracking page only
- anon role: INSERT on orders is not permitted; all writes require authentication
## Core Feature Modules
### 1. New Order Creation Wizard (multi-step form)
Step 1 — Customer Lookup:
- Input field for phone number in +60 format with auto-formatting
- Query customers table; if found, display name, order history count, and current loyalty discount tier
- If new customer, show inline name/email form fields
- Show last 3 orders for returning customers
Step 2 — Item Entry:
- "Add Item" button opens a row: item_type dropdown, service_type dropdown, quantity spinner, unit_price (auto-filled from pricing_rules, editable), special_instructions text input
- Real-time line total: quantity × unit_price
- Support adding unlimited items; show running subtotal
Step 3 — Express & Scheduling:
- Toggle for Express Service; when toggled, show surcharge calculation (subtotal × 30%) and new due_date (now + {{express_hours}} hours)
- Standard due_date displayed as default
- Priority queue badge displayed immediately for express orders
Step 4 — Payment & Confirmation:
- Show full order summary: itemised list, subtotal, express surcharge (if any), loyalty discount (if applicable), grand total in RM
- Payment method selector: Cash / Touch 'n Go eWallet / DuitNow / Card
- Confirm & Print button submits order and opens print dialog
Post-submission:
- Auto-generate order_number: ORD-YYYYMMDD-NNN (query max sequence for today, increment by 1)
- Increment customers.total_orders; recalculate and update loyalty_discount_pct if threshold crossed (10 orders → 5%, 20 orders → 10%)
- Insert payment record
- Trigger receipt + tag print layout
### 2. Order Status Pipeline
Pipeline stages with colour coding:
- Received → blue badge
- Washing → cyan badge
- Drying → orange badge
- Ironing → purple badge
- Quality Check → yellow badge
- Ready → green badge
- Collected → grey badge
Implementation:
- Kanban board view with columns per status, draggable cards for easy status updates
- Quick-update buttons on each card ("Move to Next Stage") — no need to enter detail page
- Record timestamps when status changes (store in a status_history JSONB column or separate table)
- When status changes to "ready": trigger SMS/WhatsApp notification to customer, set sms_sent = true
### 3. Public Order Tracking Page (/track)
- No login required
- Single input: phone number OR order number
- Show visual progress bar of the 7-stage pipeline with current stage highlighted
- Display: order number, items count summary, service type, estimated pickup date, shop phone number
- Bilingual: English + Chinese (Simplified) side by side or toggle
- Mobile-first layout for customers checking on their phone
### 4. Receipt & Tag Printing
Full Receipt (A5 or 80mm thermal roll):
- Shop name: {{shop_name}}, address, phone
- Order number (large, bold)
- Customer name + phone
- Itemised list: item type | service | qty | unit price | line total
- Subtotal / Express Surcharge / Discount / Total (RM)
- Payment method and amount paid
- Pickup date (bold)
- QR code linking to /track?order=ORD-xxx
- "Thank you" message in English and Chinese
Small Item Tag (58mm thermal label):
- Order number (large)
- Customer: last 2 chars of name + last 4 digits of phone (privacy-friendly)
- Item type in Chinese + English
- Special instructions (if any)
- QR code
Use @media print CSS to hide all UI chrome; provide a dedicated printable layout component.
### 5. Express Priority Queue
- All views (kanban, list, dashboard) show ⚡ Express badge in red on express orders
- Sort orders by due_date ASC by default so express orders naturally float to top
- Express surcharge shown as a separate line item in all receipts, invoices, and reports
- Visual countdown timer on orders due within 2 hours
### 6. Overdue Alerts
- Due date < 2 hours away AND status not in (ready, collected): orange warning ring on card
- Due date passed AND status not collected: red "OVERDUE" banner on card
- Dashboard top bar: overdue count badge, click to filter to overdue orders only
- Sort overdue orders to top of list by default
### 7. Loyalty Discount Engine
- After total_orders reaches 10: apply 5% discount to all future orders automatically
- After total_orders reaches 20: apply 10% discount
- Discount shown as a named line item ("Loyalty Discount 5%") in order summary and receipt
- Admin can manually override loyalty_discount_pct per customer
### 8. Pricing Calculator & Rules Manager
- Admin settings page (/admin/settings/pricing) shows a grid of item_type × service_type with editable unit prices
- Inline editing with save; update pricing_rules table in real time
- Note: existing orders retain their unit_price at time of creation (not retroactively updated)
## UI/UX Design
### Navigation & Layout
- Top navbar: dark background (#0F172A), white text, yellow (#FCD34D) accent for active state and CTAs
- Shop name "{{shop_name}}" in top-left, bold
- Desktop: persistent left sidebar — Dashboard / Orders / Customers / Reports / Settings
- Mobile: bottom tab bar with icons
- Responsive grid throughout; cards collapse to full-width on mobile
### Dashboard (/admin)
Top KPI cards (today's data):
- Today's Intake (new orders received)
- Completed Today (status changed to ready)
- Awaiting Pickup (status = ready)
- Overdue Orders (red highlight if > 0)
- Today's Revenue (RM, bold yellow)
Below KPIs: Kanban board with status columns
Right panel (desktop): Quick New Order button, Recent Activity feed
### Orders List (/admin/orders)
- Table columns: Order #, Customer, Items Summary (e.g. "3 shirts, 2 pants"), Service Type, Status badge, Due Date, Total RM, Actions (View / Update Status / Print)
- Search bar: by order number or phone number
- Filter pills: All / Express / Overdue / By Status
- Bulk status update checkbox + dropdown
### Reports (/admin/reports)
- Date range picker (Today / This Week / This Month / Custom)
- Revenue summary cards
- Bar chart: daily revenue over selected period
- Pie chart: revenue breakdown by service type
- Table: top 10 item types by volume
- Export to CSV button
### Customer Management (/admin/customers)
- Search by name or phone
- Customer card: name, phone, total orders, loyalty tier, last order date
- Click to view full order history
- Manual discount adjustment field
## SMS/WhatsApp Notification Template
"Hi [Customer Name], your laundry order [Order Number] at {{shop_name}} is ready for pickup! Please bring your receipt or quote your order number. Thank you for choosing us!"
Chinese version: "您好 [顾客姓名],您在{{shop_name}}的订单 [订单编号] 已洗好,请携带取件单来取衣。谢谢!"
Implement via a Next.js Server Action that calls the SMS gateway API when order status is updated to 'ready'.
## Environment Variables Required
Create .env.local.example with:
- NEXT_PUBLIC_SUPABASE_URL
- NEXT_PUBLIC_SUPABASE_ANON_KEY
- SUPABASE_SERVICE_ROLE_KEY
- TWILIO_ACCOUNT_SID (or SMS gateway equivalent)
- TWILIO_AUTH_TOKEN
- TWILIO_PHONE_NUMBER
- NEXT_PUBLIC_APP_URL
## Deliverables
Provide complete, production-ready code including:
1. Supabase migration SQL file with all tables, indexes, RLS policies, and seed data
2. All Next.js page and component files with full TypeScript types
3. Server Actions for order CRUD and status updates
4. Print-ready receipt and tag components with @media print styles
5. SMS notification Server Action
6. .env.local.example configuration file
7. Step-by-step Vercel deployment instructions
Ensure the UI is mobile-friendly, as shop staff will often use tablets or phones at the counter.