取代 WhatsApp 派工和纸质工单,一站式管理技术员调度、工单追踪、发票生成
* Preview is for reference only. Actual results may vary depending on the AI model, variable values, and tools used.
You are a senior full-stack engineer, expert in Next.js 16, Tailwind CSS 4, Supabase, and mobile-responsive development. Build me a complete Field Service Job Scheduler system.
## Company Background
- Company name: {company_name}
- Service type: {service_type}
- Number of technicians: {num_technicians}
- Service area: {service_area}
- Brand primary color: {primary_color}
## Tech Stack
- Next.js 16 (App Router) + TypeScript
- Tailwind CSS 4
- Supabase (Postgres + Auth + Realtime + Storage)
- Deploy on Vercel
## 1. Database Design (Supabase Postgres)
Create the following complete database structure:
### customers
- id (uuid, PK), company_name, contact_name, phone (+60 format), email, address_line1, address_line2, city (default Kuala Lumpur), state (default Selangor), postcode, latitude, longitude, notes, tags (text[]), created_at, updated_at
### technicians
- id (uuid, PK), user_id (FK auth.users), name, phone, email, skills (text[]), certification (text[]), employment_type (full-time/part-time/contract), hourly_rate (numeric), max_jobs_per_day (default 6), is_active, current_latitude, current_longitude, last_location_update, avatar_url, created_at
### technician_availability
- id (uuid, PK), technician_id (FK), day_of_week (0-6), start_time (default 09:00), end_time (default 18:00), is_available
### technician_leaves
- id (uuid, PK), technician_id (FK), leave_date, leave_type (annual/sick/emergency/public_holiday), reason, approved, created_at
### jobs (core table)
- id (uuid, PK), job_number (unique, auto-generated JOB-YYYYMMDD-XXXX), customer_id (FK), technician_id (FK), title, description, service_type, priority (low/normal/high/urgent), status (new/assigned/en_route/in_progress/on_hold/completed/cancelled/invoiced), scheduled_date, scheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, address fields + lat/lng, estimated_duration_minutes (default 60), actual_duration_minutes, quoted_amount, final_amount, payment_status (unpaid/partial/paid), payment_method (cash/bank_transfer/ewallet/card/invoice_later), customer_signature_url, notes, internal_notes, sla_deadline, created_by, created_at, updated_at
### job_photos
- id (uuid, PK), job_id (FK), photo_url, photo_type (before/during/after/issue/receipt), caption, uploaded_by, created_at
### job_status_history
- id (uuid, PK), job_id (FK), old_status, new_status, changed_by, notes, latitude, longitude, created_at
### job_parts
- id (uuid, PK), job_id (FK), part_name, part_number, quantity, unit_cost, markup_percentage (default 30%), selling_price, is_customer_supplied, notes
### job_services
- id (uuid, PK), job_id (FK), service_name, description, quantity, unit_price, discount_percentage
### invoices
- id (uuid, PK), invoice_number (unique, auto-generated INV-YYYYMMDD-XXXX), job_id (FK), customer_id (FK), subtotal, tax_rate (default 8% SST), tax_amount, total_amount, status (draft/sent/paid/overdue/cancelled), due_date, paid_date, notes, pdf_url, created_at
### feedback
- id (uuid, PK), job_id (FK), customer_id (FK), rating (1-5), comment, feedback_token (unique), submitted_at, created_at
### parts_inventory
- id (uuid, PK), part_name, part_number (unique), category, unit_cost, selling_price, stock_quantity, min_stock_level (default 5), supplier, created_at
Create proper indexes on all foreign keys, status fields, scheduled_date, phone, skills (GIN), job_number.
RLS policies:
- Dispatchers/admins (authenticated + admin role): full CRUD on all tables
- Technicians (authenticated + technician role): read/update only their assigned jobs, update own location
- Customer feedback: anonymous access via unique feedback_token
- Wrap auth.uid() in (select auth.uid()) for RLS performance
Auto-generate job numbers (JOB-YYYYMMDD-XXXX) and invoice numbers (INV-YYYYMMDD-XXXX) via triggers.
## 2. Business Logic
### Smart Job Assignment Algorithm
Implement Server Action `assignOptimalTechnician(jobId)` that:
1. Gets required skills and scheduled time from the job
2. Filters active technicians with matching skills
3. Excludes technicians on leave or at max capacity for that time slot
4. Scores each candidate:
- Skill match (40%): exact match=100, partial=50
- Proximity (30%): Haversine distance to job location. Under 10km=100, 10-20km=70, 20-30km=40, 30km+=10
- Availability (20%): remaining capacity / max_jobs_per_day * 100
- Customer rating (10%): average rating past 30 days * 20
5. Returns top 3 recommended technicians with score breakdown
### SLA Tracking
- Normal priority: 48 hours to complete
- High priority: 24 hours
- Urgent: 4 hours
- Display SLA countdown timer, auto-highlight red when breached, notify admin
### Job Status Flow
new -> assigned (auto-notify technician)
assigned -> en_route (technician taps Depart, GPS recorded)
en_route -> in_progress (technician arrives, takes arrival photo)
in_progress -> completed (fill service report, take completion photos, get signature)
completed -> invoiced (generate invoice)
any -> cancelled (requires reason)
in_progress -> on_hold (waiting for parts, etc.)
Every status change logged to job_status_history with GPS coordinates and timestamp.
## 3. App Router Page Structure
```
src/app/
(public)/feedback/[token]/page.tsx -- Customer feedback (no login)
(auth)/login/page.tsx -- Login
(dashboard)/
layout.tsx -- Sidebar + topbar
page.tsx -- Dashboard home
jobs/page.tsx -- Job list (table+filters)
jobs/new/page.tsx -- Create job
jobs/[id]/page.tsx -- Job detail
jobs/calendar/page.tsx -- Calendar view
technicians/page.tsx -- Technician list
technicians/[id]/page.tsx -- Technician detail + schedule
technicians/map/page.tsx -- Live technician map
customers/page.tsx -- Customer list
customers/[id]/page.tsx -- Customer detail + history
invoices/page.tsx -- Invoice list
invoices/[id]/page.tsx -- Invoice detail/print
inventory/page.tsx -- Parts inventory
settings/page.tsx -- System settings
(mobile)/
layout.tsx -- Mobile bottom nav
my-jobs/page.tsx -- Today tasks
my-jobs/[id]/page.tsx -- Job execution
my-jobs/[id]/checklist/page.tsx -- Service checklist
my-jobs/[id]/photos/page.tsx -- Upload photos
my-jobs/[id]/parts/page.tsx -- Parts used
my-jobs/[id]/signature/page.tsx -- Customer signature
my-jobs/[id]/complete/page.tsx -- Complete job
schedule/page.tsx -- My schedule
profile/page.tsx -- Profile
api/
jobs/route.ts
technicians/route.ts
assign/route.ts -- Smart assignment API
feedback/[token]/route.ts
webhooks/sla-check/route.ts -- Cron job for SLA
```
## 4. UI/UX Detailed Specs
### Dispatcher Dashboard (Desktop)
Top row of stat cards:
- Today jobs count (color-coded by status: blue=new, orange=in-progress, green=completed)
- Today estimated revenue (RM)
- Technician utilization rate
- Weekly customer satisfaction (star rating)
Middle left (60%): Today job timeline/Gantt chart
- X-axis = time (8AM-8PM), Y-axis = technicians
- Color blocks = jobs, colored by status
- Drag blocks to reschedule
- Click block for job summary popup
Middle right (40%): Unassigned jobs queue
- Sorted by priority, urgent jobs flashing red at top
- Each card: job number, customer, address, service type, SLA countdown
- [Smart Assign] button triggers algorithm recommendation
- Drag job to timeline to assign directly
Bottom: Real-time notification bar (technician status updates, SLA warnings, new job alerts)
### Job List Page
- Table view with columns: Job #, Customer, Service Type, Technician, Status (colored Badge), Scheduled Date, Amount, SLA
- Top filter bar: status (multi-select), date range, technician, priority, search
- View toggle: Table / Kanban (by status columns) / Calendar
- Bulk actions: select multiple jobs, batch assign or status change
### Create Job Form
- Left column: Customer info (search existing or create new, phone format +60-XX-XXX XXXX)
- Right column: Job info (service type dropdown, priority, description, scheduled date/time)
- Address with Google Places Autocomplete, auto-fills lat/lng
- Bottom: estimated duration, quoted amount
- Save options: [Save as Draft] or [Save and Assign]
### Technician Mobile View
Designed mobile-first, native app feel:
**Today Jobs** (/my-jobs)
- Top: date picker, today job count
- Card list, each card:
- Left time bar (green=done, blue=in-progress, gray=pending)
- Job number + service type badge
- Customer name + address (truncated)
- Scheduled time
- Bottom action buttons: [Navigate] [Call Customer] [View Details]
- [Navigate] opens Waze first (waze://?ll=lat,lng&navigate=yes), fallback Google Maps
- [Call Customer] direct dial tel:+60xxxxxxxxx
**Job Execution** (/my-jobs/[id])
- Top: large status indicator with next action button
- Status flow buttons:
- assigned: [Depart Now] big blue button
- en_route: [Arrived On Site] big orange button
- in_progress: [Complete Service] big green button
- Customer info card: name, phone (tappable), address (tappable for nav)
- Service details and checklist
- Photo section: camera button, categorized upload (before/during/after)
- Parts used: search from inventory, enter quantity
- Notes text area
**Complete Job Flow** (/my-jobs/[id]/complete)
- Step 1: Confirm completed services and parts used
- Step 2: Upload completion photos (minimum 1)
- Step 3: Add technical notes
- Step 4: Canvas signature pad for customer sign-off
- Step 5: Review and submit
### Customer Feedback (/feedback/[token])
- No login required, accessed via unique token
- Shows: company logo, service date, technician name, service description
- Large 5-star rating (easy tap targets)
- Comment text area
- Thank you page after submission
- Auto-send feedback link via SMS/WhatsApp after job completion
### Invoice Page
- Professional invoice template: company logo + info, customer info, job details, service line items, parts line items, subtotal / SST (8%) / total
- Amounts in RM (Malaysian Ringgit), format RM 1,234.56
- Action buttons: [Download PDF] [Send via WhatsApp] [Send via Email]
- Print-friendly CSS @media print styles
## 5. Malaysian Localization
- Currency: RM (Malaysian Ringgit), format RM X,XXX.XX
- Phone: +60 format, display as +60-12-345 6789
- Address format: [Address], [Postcode] [City], [State]
- Navigation: Waze preferred (most popular in Malaysia), Google Maps fallback
- Tax: SST 8% (Service and Sales Tax)
- Timezone: Asia/Kuala_Lumpur (UTC+8)
- Public holidays: Malaysian federal holiday calendar
- Bilingual: UI primarily English (technical terms), customer-facing documents optionally Chinese
## 6. Sample Data
Pre-populate for demo:
### Company
- Cool Air KL Sdn Bhd
- Services: Aircon repair, installation, maintenance
- Address: Jalan Ampang, 50450 Kuala Lumpur
### Customers (5)
1. Tan Ah Kow - 012-345 6789 - Bangsar, KL
2. Lim Mei Ling - 016-789 0123 - Petaling Jaya, Selangor
3. Ahmad bin Hassan - 019-456 7890 - Mont Kiara, KL
4. Wong Siew Ping - 011-2345 6789 - Subang Jaya, Selangor
5. Raj Kumar - 017-890 1234 - Cheras, KL
### Technicians (4)
1. Ali bin Abu - Skills: installation, repair - Based in Petaling Jaya
2. Muthu Rajan - Skills: repair, maintenance - Based in Cheras
3. Tan Wei Ming - Skills: installation, cleaning - Based in Bangsar
4. Azlan bin Ismail - Skills: repair, inspection - Based in Shah Alam
### Jobs (sample)
1. JOB-20250120-0001 - Tan Ah Kow - Aircon not cooling - Urgent - Assigned to Muthu
2. JOB-20250120-0002 - Lim Mei Ling - New aircon install (3 units) - Normal - Assigned to Ali
3. JOB-20250120-0003 - Ahmad - Annual maintenance - Low priority - Unassigned
## 7. Supabase Realtime
- Technician location updates every 30 seconds (GPS coordinates)
- Job status changes push to dispatcher dashboard in real-time
- New job creation instant notification
- Use Supabase Realtime Broadcast and Postgres Changes
## 8. Key Server Actions
```typescript
async function assignOptimalTechnician(jobId: string): Promise<TechnicianRecommendation[]>
async function updateJobStatus(jobId: string, newStatus: JobStatus, data?: StatusUpdateData): Promise<Job>
async function generateInvoice(jobId: string): Promise<Invoice>
async function sendFeedbackRequest(jobId: string, method: 'sms' | 'whatsapp'): Promise<void>
async function checkSLABreaches(): Promise<SLABreachReport>
```
## 9. Component Requirements
shadcn/ui components needed: button, card, input, textarea, select, badge, dialog, table, tabs, dropdown-menu, sheet, skeleton, separator, tooltip, calendar, popover, command, label, avatar, progress, alert, switch, checkbox
Custom components:
- JobStatusBadge: status-colored badge
- SignaturePad: canvas signature component
- PhotoUploader: camera/photo upload with categories
- TimelineView: job Gantt chart/timeline
- TechnicianCard: tech info with skill tags and availability
- MiniMap: job location mini map
- SLACountdown: countdown timer, turns red on breach
- NavigationButton: one-tap navigation (Waze preferred)
## 10. Responsive Design
- Desktop (>1024px): full dispatcher dashboard, sidebar nav
- Tablet (768-1024px): simplified dashboard, collapsible sidebar
- Mobile (<768px): technician mobile view, bottom tab nav, large buttons for outdoor use
- Mobile critical: min 48px touch targets, min 16px font, no horizontal scroll
Build this system step by step, starting with database schema and base layout, then core dispatch and job management features. Ensure type-safe code, Next.js App Router best practices, and a polished professional UI.