diff --git a/src/app/admin/bikers/[id]/page.tsx b/src/app/admin/bikers/[id]/page.tsx index d6ee01f..441258e 100644 --- a/src/app/admin/bikers/[id]/page.tsx +++ b/src/app/admin/bikers/[id]/page.tsx @@ -862,7 +862,7 @@ export default function BikerDetailPage() { Back to Bikers -
+
{biker.profileImage ? ( diff --git a/src/app/admin/maintenance/[id]/page.tsx b/src/app/admin/maintenance/[id]/page.tsx index fec82f4..31f0045 100644 --- a/src/app/admin/maintenance/[id]/page.tsx +++ b/src/app/admin/maintenance/[id]/page.tsx @@ -7,7 +7,7 @@ import { MapPin, FileText, Image, DollarSign, Wrench, Battery, Key, CheckCircle, XCircle, ChevronLeft, Save, Printer, Send, QrCode, Wallet, Building, Edit, MessageSquare, Calendar, ArrowLeft, Trash2, - Package, Settings + Package, Settings, History, ArrowRight } from 'lucide-react'; import Link from 'next/link'; @@ -284,7 +284,6 @@ export default function MaintenanceDetailPage() { const [showInvoiceModal, setShowInvoiceModal] = useState(false); const [showAddNoteModal, setShowAddNoteModal] = useState(false); const [showAddPartModal, setShowAddPartModal] = useState(false); - const [showAddServiceCostModal, setShowAddServiceCostModal] = useState(false); const [partSearch, setPartSearch] = useState(''); const [invoiceData, setInvoiceData] = useState({ tips: 0, discount: 0 }); const [invoiceCreated, setInvoiceCreated] = useState(false); @@ -295,7 +294,6 @@ export default function MaintenanceDetailPage() { const [actualCost, setActualCost] = useState(''); const [selectedPart, setSelectedPart] = useState(null); const [partQuantity, setPartQuantity] = useState(1); - const [serviceCostInput, setServiceCostInput] = useState(''); useEffect(() => { const found = mockMaintenance.find(r => r.id === id); @@ -457,12 +455,12 @@ export default function MaintenanceDetailPage() { Back to Maintenance -
+
-
-
-

{record.id}

+
+
+

{record.id}

{record.severity} @@ -475,7 +473,7 @@ export default function MaintenanceDetailPage() {

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

-
+
{editMode ? ( <>
+
+

+ Issue History +

+
+ {record.batteryId && ( + +
+ + Battery History +
+ + + )} + {record.bikeId && ( + +
+ + Fleet History +
+ + + )} + +
+ + All Maintenance +
+ + +
+
+

Reporter @@ -790,44 +830,68 @@ export default function MaintenanceDetailPage() { )}

-
- {(record.partsUsed || []).length > 0 ? ( - - - - - - - - - - - - {record.partsUsed?.map((part) => ( - - - - - - - + +
+
+ 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()} +
+ ))} - - - - - - - - -
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 && 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} -
Parts Total: - ৳{record.partsUsed?.reduce((sum, p) => sum + p.totalPrice, 0).toLocaleString()} -
+
+ ) : (

No parts added

)} + {record.partsUsed && record.partsUsed.length > 0 && ( +
+ Parts Total: + + ৳{record.partsUsed.reduce((sum, p) => sum + p.totalPrice, 0).toLocaleString()} + +
+ )}
@@ -922,17 +1012,22 @@ export default function MaintenanceDetailPage() {
-
- Estimated Cost: - ৳{record.estimatedCost.toLocaleString()} -
Parts Total: ৳{record.partsUsed?.reduce((s, p) => s + p.totalPrice, 0).toLocaleString()}
-
- Service Cost (Labor): - ৳{(record.serviceCost || 0).toLocaleString()} +
+ +
+ setRecord(prev => prev ? { ...prev, serviceCost: parseInt(e.target.value) || 0 } : null)} + className="flex-1 px-3 py-2 border border-blue-200 rounded-lg text-sm" + placeholder="Enter labor cost" + /> +
@@ -1256,59 +1351,6 @@ export default function MaintenanceDetailPage() {
)} -
- -
- - {showAddServiceCostModal && ( -
-
-
-

- Add Service Cost -

- -
-
-
- - setServiceCostInput(e.target.value)} - className="w-full px-3 py-2 border border-slate-200 rounded-lg text-lg" - placeholder="Enter service cost" - /> -
-
-

Current Service Cost: ৳{record?.serviceCost || 0}

-
-
-
- - -
-
-
- )} - {showPaymentSuccess && (
diff --git a/src/app/admin/maintenance/page.tsx b/src/app/admin/maintenance/page.tsx index 0c8f108..af13835 100644 --- a/src/app/admin/maintenance/page.tsx +++ b/src/app/admin/maintenance/page.tsx @@ -1,13 +1,14 @@ 'use client'; import Link from 'next/link'; +import { useRouter } from 'next/navigation'; import { useState } from 'react'; import { AlertTriangle, Search, Plus, X, Check, Clock, Bike, User, Phone, - MapPin, FileText, Image, DollarSign, Wrench, Battery, Key, + MapPin, FileText, Image as ImageIcon, DollarSign, Wrench, Battery, Key, CheckCircle, XCircle, ChevronDown, ChevronUp, Download, Eye, Edit, - MessageSquare, Filter, Calendar, Save, Printer, Send + MessageSquare, Filter, Calendar, Save, Printer, Send, Activity } from 'lucide-react'; type TransactionType = 'deposit' | 'rent_income' | 'investor_funding' | 'investor_withdraw' | 'salary' | 'rent_expense' | 'utility' | 'maintenance' | 'bike_purchase' | 'bike_sale' | 'other_income' | 'other_expense'; @@ -245,6 +246,7 @@ const typeIcons: Record = { }; export default function MaintenancePage() { + const router = useRouter(); const [mainCategory, setMainCategory] = useState<'damage' | 'maintenance'>('damage'); const [targetType, setTargetType] = useState<'all' | 'battery' | 'fleet'>('all'); const [records, setRecords] = useState(mockMaintenance); @@ -261,6 +263,7 @@ export default function MaintenancePage() { const [newNoteText, setNewNoteText] = useState(''); const [editForm, setEditForm] = useState>({}); const [reportType, setReportType] = useState<'damage' | 'maintenance'>('damage'); + const [showSuccessModal, setShowSuccessModal] = useState(false); const filteredRecords = records.filter(r => { const isDamage = r.type === 'damage'; @@ -281,13 +284,21 @@ export default function MaintenancePage() { const damageRecords = records.filter(r => r.type === 'damage'); const maintenanceRecords = records.filter(r => r.type !== 'damage'); + const currentMonth = new Date().toISOString().slice(0, 7); const stats = { damageCount: damageRecords.length, maintenanceCount: maintenanceRecords.length, + damageThisMonth: damageRecords.filter(r => r.date?.slice(0, 7) === currentMonth).length, + maintenanceThisMonth: maintenanceRecords.filter(r => r.date?.slice(0, 7) === currentMonth).length, + completedThisMonth: records.filter(r => r.status === 'completed' && r.resolvedAt?.slice(0, 7) === currentMonth).length, batteryDamage: damageRecords.filter(r => r.batteryId).length, fleetDamage: damageRecords.filter(r => r.bikeId && !r.batteryId).length, batteryMaintenance: maintenanceRecords.filter(r => r.batteryId).length, fleetMaintenance: maintenanceRecords.filter(r => r.bikeId && !r.batteryId).length, + upcomingBattery: maintenanceRecords.filter(r => r.batteryId && r.status === 'reported').length, + upcomingFleet: maintenanceRecords.filter(r => r.bikeId && !r.batteryId && r.status === 'reported').length, + ongoingBattery: maintenanceRecords.filter(r => r.batteryId && r.status === 'in_progress').length, + ongoingFleet: maintenanceRecords.filter(r => r.bikeId && !r.batteryId && r.status === 'in_progress').length, pendingMaintenance: maintenanceRecords.filter(r => r.status === 'reported' || r.status === 'in_progress').length, completedMaintenance: maintenanceRecords.filter(r => r.status === 'completed').length, critical: records.filter(r => r.severity === 'critical' && r.status !== 'completed').length, @@ -414,7 +425,7 @@ export default function MaintenancePage() { }; return ( -
+

Damage & Maintenance

@@ -436,8 +447,11 @@ export default function MaintenancePage() {
-
-

{stats.damageCount}

+
+
+

{stats.damageCount}

+ {stats.damageThisMonth} this month +

Total Damage

@@ -447,19 +461,25 @@ export default function MaintenancePage() {
-
-

{stats.maintenanceCount}

+
+
+

{stats.maintenanceCount}

+ {stats.maintenanceThisMonth} this month +

Total Maintenance

-
+
-
-

{stats.completed}

+
+
+

{stats.completed}

+ {stats.completedThisMonth} this month +

Completed

@@ -550,36 +570,36 @@ export default function MaintenancePage() {
-
-
-
- -
-
-

{stats.batteryMaintenance}

-

Battery Maintenance

-
-
-
-

{stats.fleetMaintenance}

-

Fleet Maintenance

+

{stats.upcomingFleet}

+

Upcoming Fleet

+
+
+
+
+
+
+ +
+
+

{stats.upcomingBattery}

+

Upcoming Battery

- +
-

{stats.pendingMaintenance}

-

Pending Maintenance

+

{stats.ongoingBattery + stats.ongoingFleet}

+

Ongoing Maintenance

@@ -656,57 +676,59 @@ export default function MaintenancePage() { {filteredRecords.map(record => { const TypeIcon = typeIcons[record.type]; return ( - -
-
-
- + +
+
+
+
-
-
-

{record.id}

+
+
+

{record.id}

{record.batteryId && ( - + Battery )} {!record.batteryId && record.bikeId && ( - + Fleet )} - + {record.severity}
-

- {record.bikeModel} ({record.bikePlate}) - | - {record.reporterName} +

+ {record.bikeModel} + | + {record.bikePlate} + | + {record.reporterName}

-
-

{record.description}

-
+
+

{record.description}

+

{record.date}

-

- {record.location} +

+ {record.location}

{record.images.length > 0 && (

- {record.images.length} photos + {record.images.length}

)} {record.notes.length > 0 && ( )} @@ -721,24 +743,24 @@ export default function MaintenancePage() { )}
-
-
+
+

৳{record.actualCost || record.estimatedCost}

{record.paymentStatus === 'paid' ? 'Paid' : record.paymentStatus === 'approved' ? 'Approved' : 'Payment ' + record.paymentStatus}

- - {record.status === 'reported' && } - {record.status === 'in_progress' && } - {record.status === 'parts_ordered' && } - {record.status === 'completed' && } - {record.status === 'cancelled' && } - {record.status.replace('_', ' ')} - +
+ + {record.status === 'reported' && } + {record.status === 'in_progress' && } + {record.status === 'parts_ordered' && } + {record.status === 'completed' && } + {record.status === 'cancelled' && } + {record.status.replace('_', ' ')} + -
+
+ +
+ + +
+
@@ -949,23 +990,45 @@ export default function MaintenancePage() { -
-
- -
+ {reportType === 'damage' ? ( +
+ + +
+ ) : ( +
+ + +
+ )}
@@ -976,14 +1039,29 @@ export default function MaintenancePage() {
-
- - -
-
- - -
+ {reportType === 'damage' ? ( + <> +
+ + +
+
+ + +
+ + ) : ( + <> +
+ + +
+
+ + +
+ + )}
@@ -995,14 +1073,16 @@ export default function MaintenancePage() {
- +
- +

Click to upload images

JPG, PNG up to 5MB

@@ -1012,7 +1092,7 @@ export default function MaintenancePage() { -
@@ -1117,6 +1197,24 @@ export default function MaintenancePage() {
)} + + {showSuccessModal && ( +
+
+
+ +
+

Issue Reported Successfully!

+

Your issue has been submitted and will be reviewed shortly.

+ +
+
+ )}
); } \ No newline at end of file