diff --git a/src/app/admin/maintenance/[id]/page.tsx b/src/app/admin/maintenance/[id]/page.tsx index e47e12e..a29a792 100644 --- a/src/app/admin/maintenance/[id]/page.tsx +++ b/src/app/admin/maintenance/[id]/page.tsx @@ -282,6 +282,12 @@ export default function MaintenanceDetailPage() { const [showCompleteModal, setShowCompleteModal] = useState(false); const [showPaymentModal, setShowPaymentModal] = useState(false); const [showInvoiceModal, setShowInvoiceModal] = useState(false); + + // AI OCR Simulation States + const [isOcrProcessing, setIsOcrProcessing] = useState(false); + const [ocrComplete, setOcrComplete] = useState(false); + const [ocrFileName, setOcrFileName] = useState(''); + const [ocrStep, setOcrStep] = useState(''); const [showAddNoteModal, setShowAddNoteModal] = useState(false); const [showAddPartModal, setShowAddPartModal] = useState(false); const [partSearch, setPartSearch] = useState(''); @@ -473,77 +479,84 @@ export default function MaintenanceDetailPage() {

{typeLabels[record.type]} • {record.date}

-
- {editMode ? ( - <> - - - - ) : ( - <> - {!invoiceCreated && ( - - )} - {!invoiceCreated && ( - - )} - {record.status !== 'completed' && !invoiceCreated && ( - - )} - {record.status !== 'completed' && invoiceCreated && ( - <> + + ) : ( + <> + {!invoiceCreated && ( + + )} + {!invoiceCreated && ( + + )} + {record.status !== 'completed' && !invoiceCreated && ( + + )} + {record.status !== 'completed' && invoiceCreated && ( + <> + + + + + )} + {record.status === 'completed' && record.paymentStatus !== 'paid' && ( + + )} + {record.paymentStatus === 'paid' && ( - - - - )} - {record.status === 'completed' && record.paymentStatus !== 'paid' && ( - - )} - {record.paymentStatus === 'paid' && ( - - )} - - )} + )} + + )} +
@@ -617,36 +630,44 @@ export default function MaintenanceDetailPage() { )} -
-

- Issue History -

-
- {record.batteryId && ( - -
- - Battery History -
- - - )} - {record.bikeId && ( - -
- - Fleet History -
- - - )} - +
+ {ocrComplete && ( +
+ + Populated via AI OCR + +
+ )} +
+

+ Issue History +

+
+ {record.batteryId && ( + +
+ + Battery History +
+ + + )} + {record.bikeId && ( + +
+ + Fleet History +
+ + + )} +
@@ -724,224 +745,6 @@ export default function MaintenanceDetailPage() { )}
- - -
- -
- - -
-

- Cost Details -

-
-
-

- Cost Breakdown -

- {editMode && ( - setEditForm({ ...editForm, estimatedCost: parseInt(e.target.value) })} - className="px-2 py-1 border border-purple-200 rounded text-sm w-24" - /> - )} -
- -
-
- Estimated Cost: - ৳{record.estimatedCost.toLocaleString()} -
- -
-
- - Parts Total: - - ৳{record.partsUsed?.reduce((sum, p) => sum + p.totalPrice, 0).toLocaleString() || 0} -
- -
- - Service Cost (Labor): - - ৳{(record.serviceCost || 0).toLocaleString()} -
-
- -
-
- Actual Total Cost: - - ৳{((record.partsUsed?.reduce((sum, p) => sum + p.totalPrice, 0) || 0) + (record.serviceCost || 0)).toLocaleString()} - -
-
-
-
-
- -
-

- Assigned To -

- {editMode ? ( - - ) : ( -

{record.assignedTo || 'Not assigned'}

- )} -
- -
-
-

- Parts Used -

- {!editMode && ( - - )} -
-
- {record.partsUsed && record.partsUsed.length > 0 ? ( - <> -
- - - - - - - - - - - - {record.partsUsed?.map((part) => ( - - - - - - - - ))} - -
PartQtyUnit PriceTotalAction
{part.partName} - { - const newQty = Math.max(1, parseInt(e.target.value) || 1); - setRecord(prev => prev ? { - ...prev, - partsUsed: prev.partsUsed?.map(p => - p.id === part.id - ? { ...p, quantity: newQty, totalPrice: newQty * p.unitPrice } - : p - ) - } : null); - }} - className="w-16 px-2 py-1 border border-orange-200 rounded text-center text-sm" - /> - ৳{part.unitPrice.toLocaleString()}৳{part.totalPrice.toLocaleString()} - -
-
-
- {record.partsUsed.map((part) => ( -
-
- {part.partName} - -
-
-
- Qty: - { - const newQty = Math.max(1, parseInt(e.target.value) || 1); - setRecord(prev => prev ? { - ...prev, - partsUsed: prev.partsUsed?.map(p => - p.id === part.id - ? { ...p, quantity: newQty, totalPrice: newQty * p.unitPrice } - : p - ) - } : null); - }} - className="w-16 px-2 py-1 border border-orange-200 rounded text-center text-sm" - /> -
- ৳{part.unitPrice.toLocaleString()}/unit -
-
- Total - ৳{part.totalPrice.toLocaleString()} -
-
- ))} -
- - ) : ( -

No parts added

- )} - {record.partsUsed && record.partsUsed.length > 0 && ( -
- Parts Total: - - ৳{record.partsUsed.reduce((sum, p) => sum + p.totalPrice, 0).toLocaleString()} - -
- )} -
- - -
-

Notes ({(editMode ? editForm.notes : record.notes)?.length}) @@ -988,6 +791,348 @@ export default function MaintenanceDetailPage() { )}

+ + + +
+ + + {/* Manual Invoice OCR Card */} +
+
+

+ Manual Invoice OCR Upload +

+ {ocrComplete && ( + + OCR Synced + + )} +
+ + {!isOcrProcessing && !ocrComplete && ( +
+ { + const file = e.target.files?.[0]; + if (!file) return; + setOcrFileName(file.name); + setIsOcrProcessing(true); + + setOcrStep('1/3: Parsing document layout & digital signature...'); + setTimeout(() => { + setOcrStep('2/3: Running AI layout analysis & line item extraction...'); + setTimeout(() => { + setOcrStep('3/3: Auto-populating ledger items and actual costs...'); + setTimeout(() => { + setIsOcrProcessing(false); + setOcrComplete(true); + // Populate parts dynamically with mock OCR data! + setRecord(prev => { + if (!prev) return null; + return { + ...prev, + partsUsed: [ + { id: 'PU-OCR-1', partId: 'PRT-001', partName: 'Front fender (OCR Extracted)', quantity: 1, unitPrice: 1500, totalPrice: 1500, addedAt: new Date().toISOString().split('T')[0] }, + { id: 'PU-OCR-2', partId: 'PRT-003', partName: 'Mounting brackets (OCR Extracted)', quantity: 2, unitPrice: 800, totalPrice: 1600, addedAt: new Date().toISOString().split('T')[0] }, + { id: 'PU-OCR-3', partId: 'PRT-004', partName: 'Brake pads (OCR Extracted)', quantity: 2, unitPrice: 600, totalPrice: 1200, addedAt: new Date().toISOString().split('T')[0] }, + ], + serviceCost: 1800, + actualCost: 6100, // 1500 + 1600 + 1200 + 1800 + notes: [...prev.notes, `OCR Scan Success: Extracted items from manual invoice "${file.name}".`] + }; + }); + }, 1000); + }, 1200); + }, 1000); + }} + /> +
+
+ +
+
+

Upload manual invoice receipt

+

No file chosen

+

Supports PDF, PNG, JPG (e.g. MNT-001-invoice.pdf)

+
+
+
+ )} + + {isOcrProcessing && ( +
+
+ Running JAIBEN AI OCR Engine... + {ocrStep.split(':')[0]} +
+
+
+
+

{ocrStep}

+
+ )} + + {ocrComplete && ( +
+
+ + OCR PARSING COMPLETE +
+
+

📄 Document: {ocrFileName}

+

🔧 Extracted Parts: 3 items

+

⚡ Labor Service Costs: ৳1,800

+

💰 Auto Total Synced: ৳6,100

+
+ +
+ )} +
+ +
+ {ocrComplete && ( +
+ + Populated via AI OCR + +
+ )} +
+

+ Cost Details +

+
+
+

+ Cost Breakdown +

+ {editMode && ( + setEditForm({ ...editForm, estimatedCost: parseInt(e.target.value) })} + className="px-2 py-1 border border-purple-200 rounded text-sm w-24" + /> + )} +
+ +
+
+ Estimated Cost: + ৳{record.estimatedCost.toLocaleString()} +
+ +
+
+ + Parts Total: + + ৳{record.partsUsed?.reduce((sum, p) => sum + p.totalPrice, 0).toLocaleString() || 0} +
+ +
+ + Service Cost (Labor): + + ৳{(record.serviceCost || 0).toLocaleString()} +
+
+ +
+
+ Actual Total Cost: + + ৳{((record.partsUsed?.reduce((sum, p) => sum + p.totalPrice, 0) || 0) + (record.serviceCost || 0)).toLocaleString()} + +
+
+
+
+
+
+ +
+

+ Assigned To +

+ {editMode ? ( + + ) : ( +

{record.assignedTo || 'Not assigned'}

+ )} +
+ +
+ {ocrComplete && ( +
+ + Populated via AI OCR + +
+ )} +
+
+

+ Parts Used +

+ {!editMode && ( + + )} +
+
+ {record.partsUsed && record.partsUsed.length > 0 ? ( + <> +
+ + + + + + + + + + + + {record.partsUsed?.map((part) => ( + + + + + + + + ))} + +
PartQtyUnit PriceTotalAction
{part.partName} + { + const newQty = Math.max(1, parseInt(e.target.value) || 1); + setRecord(prev => prev ? { + ...prev, + partsUsed: prev.partsUsed?.map(p => + p.id === part.id + ? { ...p, quantity: newQty, totalPrice: newQty * p.unitPrice } + : p + ) + } : null); + }} + className="w-16 px-2 py-1 border border-orange-200 rounded text-center text-sm" + /> + ৳{part.unitPrice.toLocaleString()}৳{part.totalPrice.toLocaleString()} + +
+
+
+ {record.partsUsed.map((part) => ( +
+
+ {part.partName} + +
+
+
+ Qty: + { + const newQty = Math.max(1, parseInt(e.target.value) || 1); + setRecord(prev => prev ? { + ...prev, + partsUsed: prev.partsUsed?.map(p => + p.id === part.id + ? { ...p, quantity: newQty, totalPrice: newQty * p.unitPrice } + : p + ) + } : null); + }} + className="w-16 px-2 py-1 border border-orange-200 rounded text-center text-sm" + /> +
+ ৳{part.unitPrice.toLocaleString()}/unit +
+
+ Total + ৳{part.totalPrice.toLocaleString()} +
+
+ ))} +
+ + ) : ( +

No parts added

+ )} + {record.partsUsed && record.partsUsed.length > 0 && ( +
+ Parts Total: + + ৳{record.partsUsed.reduce((sum, p) => sum + p.totalPrice, 0).toLocaleString()} + +
+ )} +
+
+
+ +