'use client'; import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; import { AlertCircle, ListTodo, RefreshCw, Smartphone, User } from 'lucide-react'; import { toast } from 'sonner'; import { adminService } from '@/services/api/adminService'; import { formatCurrency } from '@/lib/utils'; import { CustomSelect } from '@/components/ui/CustomSelect'; import { PhoneInput } from '@/components/ui/PhoneInput'; import { validatePhone, validateEmail } from '@/lib/validation'; interface ServiceCatalogItem { service_id: string; name: string; base_price: number; description?: string | null; estimated_duration_minutes?: number; workflow_type?: string; } const EMPTY_DEVICE = { brand: '', custom_brand: '', model: '', custom_model: '', model_number: '', imei_primary: '', imei_secondary: '', color: '', notes: '', storage_capacity: '', }; const EMPTY_INTAKE = { power_status: 'POWERS_ON', screen_condition: 'CRACKED', body_condition: 'GOOD', back_condition: 'GOOD', camera_condition: 'WORKING', accessories: 'NONE', customer_notes: '', technician_notes: '', }; const POWER_OPTIONS = [ { value: 'POWERS_ON', label: 'Powers on & boots' }, { value: 'NO_POWER', label: 'No power / dead' }, { value: 'BOOT_LOOP', label: 'Boot loop' }, ]; const SCREEN_OPTIONS = [ { value: 'EXCELLENT', label: 'Excellent (flawless)' }, { value: 'MINOR_SCRATCHES', label: 'Minor scratches' }, { value: 'CRACKED', label: 'Cracked glass' }, { value: 'SHATTERED', label: 'Shattered display & touch dead' }, ]; const BODY_OPTIONS = [ { value: 'GOOD', label: 'Good (no dents)' }, { value: 'DENTED', label: 'Minor scuffs / dented corners' }, { value: 'BENT', label: 'Bent housing' }, ]; export default function WalkInIntakePage() { const router = useRouter(); const [catalog, setCatalog] = useState([]); const [brands, setBrands] = useState([]); const [allModels, setAllModels] = useState([]); const [filteredModels, setFilteredModels] = useState([]); const [existingCustomers, setExistingCustomers] = useState([]); const [customerSearchQuery, setCustomerSearchQuery] = useState(''); const [loadingCatalog, setLoadingCatalog] = useState(true); const [isSubmitting, setIsSubmitting] = useState(false); const [errorMsg, setErrorMsg] = useState(null); const [customerType, setCustomerType] = useState<'EXISTING' | 'NEW'>('EXISTING'); const [customerId, setCustomerId] = useState(''); const [customerName, setCustomerName] = useState(''); const [customerPhone, setCustomerPhone] = useState(''); const [customerEmail, setCustomerEmail] = useState(''); const [selectedServiceId, setSelectedServiceId] = useState(''); const [customServiceName, setCustomServiceName] = useState(''); const [customServicePrice, setCustomServicePrice] = useState(''); const [deviceDetails, setDeviceDetails] = useState(EMPTY_DEVICE); const [intakeDetails, setIntakeDetails] = useState(EMPTY_INTAKE); const loadCatalog = async () => { setLoadingCatalog(true); try { const [items, brandItems, modelItems, custItems] = await Promise.all([ adminService.fetchServiceCatalog(), adminService.fetchBrands(), adminService.fetchDeviceModels(), adminService.fetchCustomers().catch(() => []), ]); setCatalog(items); setBrands(brandItems); setAllModels(modelItems); setExistingCustomers(custItems || []); if (custItems && custItems.length > 0) { setCustomerId(custItems[0].customer_id); } return { items, brandItems }; } catch (err: any) { toast.error(err.message || 'Failed to load catalog services.'); return { items: [] as ServiceCatalogItem[], brandItems: [] as any[] }; } finally { setLoadingCatalog(false); } }; const resetForm = (nextBrands = brands, nextCatalog = catalog) => { setCustomerType('EXISTING'); setCustomerId(''); setCustomerName(''); setCustomerPhone(''); setCustomerEmail(''); setCustomServiceName(''); setCustomServicePrice(''); setSelectedServiceId(nextCatalog[0]?.service_id || ''); setDeviceDetails({ ...EMPTY_DEVICE, brand: nextBrands[0]?.brand_id || 'OTHER', }); setIntakeDetails({ ...EMPTY_INTAKE }); setErrorMsg(null); }; useEffect(() => { loadCatalog().then(({ items, brandItems }) => { resetForm(brandItems, items); }); }, []); useEffect(() => { if (deviceDetails.brand && deviceDetails.brand !== 'OTHER') { const filtered = allModels.filter((m) => m.brand_id === deviceDetails.brand); setFilteredModels(filtered); if (filtered.length > 0) { setDeviceDetails((prev) => ({ ...prev, model: filtered[0].model_id })); } else { setDeviceDetails((prev) => ({ ...prev, model: 'OTHER' })); } } else { setFilteredModels([]); setDeviceDetails((prev) => ({ ...prev, model: 'OTHER' })); } }, [deviceDetails.brand, allModels]); const handleIntakeSubmit = async (e: React.FormEvent) => { e.preventDefault(); if (customerType === 'EXISTING' && !customerId) { setErrorMsg('Customer ID is required.'); toast.error('Customer ID is required.'); return; } if (customerType === 'NEW') { if (!customerName) { setErrorMsg('Customer name is required.'); toast.error('Customer name is required.'); return; } if (customerPhone) { const phoneErr = validatePhone(customerPhone); if (phoneErr) { setErrorMsg(phoneErr); toast.error(phoneErr); return; } } if (customerEmail) { const emailErr = validateEmail(customerEmail); if (emailErr) { setErrorMsg(emailErr); toast.error(emailErr); return; } } } if (!selectedServiceId) { setErrorMsg('Service selection is required.'); toast.error('Service selection is required.'); return; } if (selectedServiceId === 'OTHER_SERVICE' && !customServiceName) { setErrorMsg('Custom service name is required.'); toast.error('Custom service name is required.'); return; } setIsSubmitting(true); setErrorMsg(null); try { let finalBrand = ''; if (deviceDetails.brand === 'OTHER') { finalBrand = deviceDetails.custom_brand || 'Other'; } else { const found = brands.find((b) => b.brand_id === deviceDetails.brand); finalBrand = found ? found.name : 'Unknown'; } let finalModel = ''; if (deviceDetails.model === 'OTHER') { finalModel = deviceDetails.custom_model || 'Other'; } else { const found = allModels.find((m) => m.model_id === deviceDetails.model); finalModel = found ? found.name : 'Unknown'; } const payload = { device_id: undefined, new_device: { brand: finalBrand, model: finalModel, model_number: deviceDetails.model_number, imei_primary: deviceDetails.imei_primary, imei_secondary: deviceDetails.imei_secondary, color: deviceDetails.color, notes: deviceDetails.notes, device_type: 'Smartphone', storage_capacity: deviceDetails.storage_capacity, }, service_id: selectedServiceId, source: 'WALK_IN', appointment: undefined, customer_id: customerType === 'EXISTING' ? customerId : undefined, customer_name: customerType === 'NEW' ? customerName : undefined, customer_phone: customerType === 'NEW' ? customerPhone : undefined, customer_email: customerType === 'NEW' ? customerEmail : undefined, custom_service_name: selectedServiceId === 'OTHER_SERVICE' ? customServiceName : undefined, }; const booking = await adminService.createServiceBooking(payload); await adminService.submitDeviceIntake(booking.job_id, { power_status: intakeDetails.power_status, screen_condition: intakeDetails.screen_condition, body_condition: intakeDetails.body_condition, back_condition: intakeDetails.back_condition, camera_condition: intakeDetails.camera_condition, accessories: intakeDetails.accessories, customer_notes: intakeDetails.customer_notes, technician_notes: intakeDetails.technician_notes, }); let baseTotal = 0; if (selectedServiceId === 'OTHER_SERVICE') { baseTotal = parseFloat(customServicePrice) || 0; } else { const serviceItem = catalog.find((s) => s.service_id === selectedServiceId); baseTotal = serviceItem ? serviceItem.base_price : 0; } await adminService.createOrReviseQuote(booking.job_id, { subtotal: baseTotal, tax: baseTotal * 0.18, additional_damage_amount: 0, total: baseTotal * 1.18, reason: 'Initial estimate quote generated for walk-in counter order.', }); const displayJobNo = booking.job_no || booking.job_id; toast.success(`Walk-in job ${displayJobNo} registered! Redirecting to technician workspace...`); router.push(`/technician?job_id=${booking.job_id}`); } catch (err: any) { const message = err.message || 'Failed to submit intake. Verify inputs.'; setErrorMsg(message); toast.error(message); } finally { setIsSubmitting(false); } }; const brandOptions = [ ...brands.map((b) => ({ value: b.brand_id as string, label: b.name as string })), { value: 'OTHER', label: 'Other brand' }, ]; const modelOptions = [ ...filteredModels.map((m) => ({ value: m.model_id as string, label: m.name as string })), { value: 'OTHER', label: 'Other model' }, ]; const serviceOptions = [ ...catalog.map((s) => ({ value: s.service_id, label: `${s.name} (${formatCurrency(s.base_price)})`, })), { value: 'OTHER_SERVICE', label: 'Other / custom service' }, ]; const dataCardShell = 'crm-radius-section border border-border bg-card shadow-[0_1px_3px_rgba(16,24,40,0.06)] overflow-visible min-w-0'; const inputClass = 'w-full h-9 px-3 bg-card border border-border crm-radius-control text-[13px] text-foreground focus:outline-none focus:border-primary disabled:opacity-50'; const labelClass = 'block text-[11px] font-semibold text-muted-foreground uppercase mb-1.5'; const primaryButton = 'inline-flex items-center justify-center gap-2 h-9 min-h-9 px-4 crm-radius-control bg-primary hover:bg-primary/90 text-white text-[13px] font-semibold cursor-pointer disabled:opacity-50 whitespace-nowrap'; const secondaryButton = 'inline-flex items-center justify-center gap-2 h-9 min-h-9 px-4 crm-radius-control border border-border bg-card text-foreground text-[13px] font-medium hover:bg-muted cursor-pointer disabled:opacity-50 whitespace-nowrap'; const sectionTitle = 'flex items-center gap-2 text-[14px] font-semibold text-foreground'; return (

Walk-In Device Intake

Register intake

Capture the customer, device and service details to open a walk-in job.

{errorMsg && (
{errorMsg}
)}

Customer

{customerType === 'EXISTING' ? (
Filter by name, phone, or email
setCustomerSearchQuery(e.target.value)} className={inputClass} />
{customerId && (
Selected Customer ID: {customerId}
)}
) : (
setCustomerName(e.target.value)} className={inputClass} />
setCustomerEmail(e.target.value)} className={inputClass} />
)}

Device

setDeviceDetails({ ...deviceDetails, brand: value })} options={brandOptions} placeholder="Select brand" aria-label="Brand" /> {deviceDetails.brand === 'OTHER' && ( setDeviceDetails({ ...deviceDetails, custom_brand: e.target.value })} className={`${inputClass} mt-2`} /> )}
setDeviceDetails({ ...deviceDetails, model: value })} options={modelOptions} placeholder="Select model" aria-label="Model" /> {deviceDetails.model === 'OTHER' && ( setDeviceDetails({ ...deviceDetails, custom_model: e.target.value })} className={`${inputClass} mt-2`} /> )}
setDeviceDetails({ ...deviceDetails, imei_primary: e.target.value })} className={`${inputClass} font-mono`} />
setDeviceDetails({ ...deviceDetails, color: e.target.value })} className={inputClass} />
setDeviceDetails({ ...deviceDetails, storage_capacity: e.target.value })} className={inputClass} />

Service

{selectedServiceId === 'OTHER_SERVICE' && (
setCustomServiceName(e.target.value)} className={inputClass} />
setCustomServicePrice(e.target.value)} className={inputClass} />
)}

Physical condition

setIntakeDetails({ ...intakeDetails, power_status: value })} options={POWER_OPTIONS} aria-label="Power status" />
setIntakeDetails({ ...intakeDetails, screen_condition: value })} options={SCREEN_OPTIONS} aria-label="Screen condition" />
setIntakeDetails({ ...intakeDetails, body_condition: value })} options={BODY_OPTIONS} aria-label="Body condition" />
setIntakeDetails({ ...intakeDetails, accessories: e.target.value })} className={inputClass} />
); }