// AddRepair.jsx — Operator-facing "Add Repair" form. // Lets an Operator (or Rigger) report a fault on the machine they run, so the // workshop sees it without a phone call. Deliberately simpler than the // Admin/Mechanic R&M ticket form (no cost / mechanic / supplier fields) — an // operator just says WHICH machine, WHAT is wrong, HOW bad, and adds a photo. // Problem areas an operator picks from (plain language, not workshop jargon). const REPAIR_AREAS = [ 'Engine', 'Hydraulics', 'Boom & winch', 'Slew / rotation', 'Outriggers', 'Brakes', 'Tyres / wheels', 'Electrical / lights', 'Cabin / controls', 'Other (describe below)', ]; // Severity in operator language — how much it stops the job. const SEVERITY = [ { id: 'Low', label: 'Low', note: 'Can still work', status: 'gray' }, { id: 'Medium', label: 'Medium', note: 'Affecting work', status: 'yellow' }, { id: 'High', label: 'High', note: 'Cannot operate', status: 'red' }, ]; function severityStatus(sev) { return (SEVERITY.find(s => s.id === sev) || {}).status || 'gray'; } // Map a machine id (or freeform machine string) to a readable label. function repairMachineLabel(id) { const m = MACHINES.find(x => x.id === id); if (!m) return id; const plate = m.plate ? m.plate + ' · ' : ''; return plate + (typeof machineLabel === 'function' ? machineLabel(m) : m.name); } function AddRepairPage({ role = 'Operator', operatorName = 'Razak Hassan' }) { const isViewer = role !== 'Operator' && role !== 'Rigger' && role !== 'Admin'; const todayISO = (typeof TODAY !== 'undefined' ? TODAY : new Date().toISOString().slice(0, 10)); // Seed list — what this operator has reported recently, with workshop status. const [requests, setRequests] = React.useState([ { id: 'RR-0231', machine: 'PLC-001', area: 'Hydraulics', severity: 'High', desc: 'Hydraulic oil leaking from boom cylinder when extending. Pressure drops.', date: (typeof addDaysISO === 'function' ? addDaysISO(todayISO, -1) : todayISO), status: 'In Workshop', photo: true, by: operatorName }, { id: 'RR-0228', machine: 'PLC-001', area: 'Cabin / controls', severity: 'Low', desc: 'Aircon in cab not cold. Still drivable but hot in afternoon.', date: (typeof addDaysISO === 'function' ? addDaysISO(todayISO, -4) : todayISO), status: 'Reviewing', photo: false, by: operatorName }, ]); // ---- Form state ---- const [machine, setMachine] = React.useState(''); const [area, setArea] = React.useState(''); const [severity, setSeverity] = React.useState('Medium'); const [desc, setDesc] = React.useState(''); const [date, setDate] = React.useState(todayISO); const [photoName, setPhotoName] = React.useState(''); const [photoUrl, setPhotoUrl] = React.useState(''); const [showErrors, setShowErrors] = React.useState(false); const [toast, setToast] = React.useState(null); const errors = { machine: !machine, area: !area, desc: !desc.trim() }; const hasErrors = Object.values(errors).some(Boolean); const resetForm = () => { setMachine(''); setArea(''); setSeverity('Medium'); setDesc(''); setDate(todayISO); setPhotoName(''); setPhotoUrl(''); setShowErrors(false); }; const onPickPhoto = (e) => { const f = e.target.files && e.target.files[0]; if (!f) return; setPhotoName(f.name); try { setPhotoUrl(URL.createObjectURL(f)); } catch (_) {} }; const submit = () => { if (hasErrors) { setShowErrors(true); return; } const seq = 232 + requests.filter(r => r.id.startsWith('RR-')).length; const newReq = { id: 'RR-0' + seq, machine, area, severity, desc: desc.trim(), date, status: 'Submitted', photo: !!photoName, by: operatorName, }; setRequests([newReq, ...requests]); setToast('Repair reported · workshop notified'); window.setTimeout(() => setToast(null), 2600); resetForm(); }; // ---- shared field primitives (mirrors the R&M form chrome) ---- const labelStyle = { display: 'block', fontSize: 13, color: 'var(--color-grey-700)', marginBottom: 6, fontWeight: 600 }; const reqStar = *; const inputBase = { width: '100%', height: 46, padding: '0 14px', border: '1px solid var(--color-grey-300)', borderRadius: 10, fontSize: 15, color: 'var(--color-grey-900, #111827)', background: 'white', boxSizing: 'border-box', fontFamily: 'inherit', outline: 'none', }; const inputErr = { borderColor: '#dc2626', background: '#fef2f2' }; const Field = ({ label, required, hint, error, children }) => (