'use client'; import { useState, useEffect } from 'react'; import { useParams, useRouter } from 'next/navigation'; import { ArrowLeft, Building2, Star, Phone, Mail, MapPin, Activity, Wrench, Battery, Bike, DollarSign, CheckCircle2, Clock, AlertTriangle, Search, SlidersHorizontal, ArrowUpDown, User, Calendar, Shield, Tag, Plus, Eye, BarChart3, Percent, ChevronRight, ExternalLink } from 'lucide-react'; import Link from 'next/link'; import { ServiceCenter } from '../page'; // Interface for Maintenance History Record interface HistoryRecord { id: string; date: string; assetId: string; assetType: 'EV Bike' | 'Battery'; serviceType: 'Damage' | 'Repair' | 'Service' | 'Battery Swap' | 'Inspection'; description: string; severity: 'critical' | 'major' | 'minor' | 'cosmetic'; status: 'completed' | 'in_progress' | 'parts_ordered'; estimatedCost: number; actualCost: number; partsUsed: { name: string; qty: number; price: number }[]; laborCost: number; technician: string; } // Generate realistic mock history data based on Center ID/Name const getMockHistoryData = (centerName: string): HistoryRecord[] => { const baseHistory: HistoryRecord[] = [ { id: 'MNT-101', date: '2024-03-21', assetId: 'EV-004', assetType: 'EV Bike', serviceType: 'Damage', description: 'Front fender shattered in traffic collision. Replaced brackets and front wheel.', severity: 'major', status: 'in_progress', estimatedCost: 3500, actualCost: 3200, partsUsed: [ { name: 'Front fender', qty: 1, price: 1500 }, { name: 'Mounting brackets', qty: 2, price: 800 }, { name: 'Brake pads', qty: 1, price: 600 } ], laborCost: 1200, technician: 'Sabbir Ahmed' }, { id: 'MNT-102', date: '2024-03-18', assetId: 'BAT-044', assetType: 'Battery', serviceType: 'Battery Swap', description: 'Internal diagnostic showing rapid voltage degradation. Replaced cells and recalibrated BMS.', severity: 'critical', status: 'completed', estimatedCost: 12000, actualCost: 11500, partsUsed: [ { name: 'Battery 60V cell pack', qty: 1, price: 9500 }, { name: 'BMS Controller Board', qty: 1, price: 2000 } ], laborCost: 2500, technician: 'Kamrul Hasan' }, { id: 'MNT-103', date: '2024-03-15', assetId: 'EV-012', assetType: 'EV Bike', serviceType: 'Service', description: 'Routine 5,000km periodic maintenance. Calibrated drum brakes and greased chassis bearings.', severity: 'minor', status: 'completed', estimatedCost: 1500, actualCost: 1450, partsUsed: [ { name: 'Brake Cable', qty: 1, price: 250 }, { name: 'Sprocket kit', qty: 1, price: 450 } ], laborCost: 750, technician: 'Sabbir Ahmed' }, { id: 'MNT-104', date: '2024-03-10', assetId: 'BAT-021', assetType: 'Battery', serviceType: 'Inspection', description: 'Thermal warning flag during hyper-charging cycle. Terminals cleaned and thermal gel reapplied.', severity: 'cosmetic', status: 'completed', estimatedCost: 500, actualCost: 400, partsUsed: [ { name: 'Thermal paste', qty: 1, price: 150 } ], laborCost: 250, technician: 'Kamrul Hasan' }, { id: 'MNT-105', date: '2024-03-05', assetId: 'EV-009', assetType: 'EV Bike', serviceType: 'Repair', description: 'Throttle failure reported by delivery driver. Replaced magnetic sensor assembly.', severity: 'major', status: 'completed', estimatedCost: 1800, actualCost: 2100, partsUsed: [ { name: 'Throttle control assembly', qty: 1, price: 800 }, { name: 'Wiring loom adapter', qty: 1, price: 450 } ], laborCost: 850, technician: 'Rafiqul Islam' }, { id: 'MNT-106', date: '2024-02-28', assetId: 'EV-017', assetType: 'EV Bike', serviceType: 'Damage', description: 'Rear tire blowout due to road debris. Replacement and alignment completed.', severity: 'minor', status: 'completed', estimatedCost: 2800, actualCost: 2750, partsUsed: [ { name: 'Rear Tire tubeless', qty: 1, price: 2200 }, { name: 'Chain replacement', qty: 1, price: 400 } ], laborCost: 500, technician: 'Sabbir Ahmed' }, { id: 'MNT-107', date: '2024-02-20', assetId: 'BAT-089', assetType: 'Battery', serviceType: 'Battery Swap', description: 'Dead module replacement under premium warranty. Replaced sub-assemblies.', severity: 'critical', status: 'parts_ordered', estimatedCost: 15000, actualCost: 0, partsUsed: [ { name: 'Battery 48V cell pack', qty: 1, price: 8000 } ], laborCost: 1500, technician: 'Kamrul Hasan' } ]; // Variations in records based on Center's specialty & size to make data dynamic if (centerName.includes('Gulshan') || centerName.includes('Center A')) { return baseHistory; } else if (centerName.includes('Banani') || centerName.includes('Center B')) { return baseHistory.filter(h => h.serviceType === 'Battery Swap' || h.serviceType === 'Service' || h.serviceType === 'Inspection').map(h => ({ ...h, id: h.id.replace('10', '20'), technician: 'Tanvir Rahman' })); } else { // Uttara / Authorized return baseHistory.filter(h => h.serviceType === 'Inspection' || h.serviceType === 'Repair').map(h => ({ ...h, id: h.id.replace('10', '30'), technician: 'Arif Chowdhury' })); } }; export default function ServiceCenterDetailsPage() { const params = useParams(); const router = useRouter(); const id = params.id as string; const [isMounted, setIsMounted] = useState(false); const [center, setCenter] = useState(null); const [history, setHistory] = useState([]); // Filtering / Sorting / Search states for history const [searchQuery, setSearchQuery] = useState(''); const [assetFilter, setAssetFilter] = useState('all'); const [typeFilter, setTypeFilter] = useState('all'); const [severityFilter, setSeverityFilter] = useState('all'); const [statusFilter, setStatusFilter] = useState('all'); const [sortBy, setSortBy] = useState<'date-desc' | 'date-asc' | 'cost-desc' | 'cost-asc' | 'urgency-desc' | 'urgency-asc'>('date-desc'); // Map Interactive detail popup state const [mapPopup, setMapPopup] = useState(null); useEffect(() => { setIsMounted(true); // Load Service Centers from localStorage const stored = localStorage.getItem('jaiben_service_centers'); let foundCenter: ServiceCenter | null = null; if (stored) { try { const centers: ServiceCenter[] = JSON.parse(stored); foundCenter = centers.find(c => c.id === id) || null; } catch (e) {} } if (foundCenter) { setCenter(foundCenter); setHistory(getMockHistoryData(foundCenter.name)); } else { router.push('/admin/service-centers'); } }, [id, router]); if (!isMounted || !center) return null; // Filter History records const filteredHistory = history.filter(h => { const matchesSearch = h.id.toLowerCase().includes(searchQuery.toLowerCase()) || h.assetId.toLowerCase().includes(searchQuery.toLowerCase()) || h.description.toLowerCase().includes(searchQuery.toLowerCase()) || h.technician.toLowerCase().includes(searchQuery.toLowerCase()); const matchesAsset = assetFilter === 'all' || h.assetType === assetFilter; const matchesType = typeFilter === 'all' || h.serviceType === typeFilter; const matchesSeverity = severityFilter === 'all' || h.severity === severityFilter; const matchesStatus = statusFilter === 'all' || h.status === statusFilter; return matchesSearch && matchesAsset && matchesType && matchesSeverity && matchesStatus; }); // Sort History records const severityWeights = { cosmetic: 1, minor: 2, major: 3, critical: 4 }; const sortedHistory = [...filteredHistory].sort((a, b) => { switch (sortBy) { case 'date-desc': return new Date(b.date).getTime() - new Date(a.date).getTime(); case 'date-asc': return new Date(a.date).getTime() - new Date(b.date).getTime(); case 'cost-desc': return (b.actualCost || b.estimatedCost) - (a.actualCost || a.estimatedCost); case 'cost-asc': return (a.actualCost || a.estimatedCost) - (b.actualCost || b.estimatedCost); case 'urgency-desc': return severityWeights[b.severity] - severityWeights[a.severity]; case 'urgency-asc': return severityWeights[a.severity] - severityWeights[b.severity]; default: return 0; } }); // Financial & Aggregate calculations const totalRepairs = history.length; const completedRepairs = history.filter(h => h.status === 'completed'); const totalEstimatedCost = completedRepairs.reduce((sum, h) => sum + h.estimatedCost, 0); const totalActualCost = completedRepairs.reduce((sum, h) => sum + h.actualCost, 0); const costVariance = totalActualCost - totalEstimatedCost; const totalPartsCost = completedRepairs.reduce((sum, h) => sum + h.partsUsed.reduce((s, p) => s + (p.price * p.qty), 0), 0); const totalLaborCost = completedRepairs.reduce((sum, h) => sum + h.laborCost, 0); const totalSpend = totalPartsCost + totalLaborCost; // Aggregate Parts Utilized Log const partsAggregated: { name: string; totalQty: number; totalCost: number }[] = []; history.forEach(h => { h.partsUsed.forEach(part => { const existing = partsAggregated.find(p => p.name === part.name); if (existing) { existing.totalQty += part.qty; existing.totalCost += part.price * part.qty; } else { partsAggregated.push({ name: part.name, totalQty: part.qty, totalCost: part.price * part.qty }); } }); }); const topParts = partsAggregated.sort((a, b) => b.totalQty - a.totalQty).slice(0, 5); const statusColors = { active: 'bg-emerald-100 text-emerald-700', busy: 'bg-amber-100 text-amber-700', inactive: 'bg-slate-100 text-slate-700' }; const severityColors = { critical: 'bg-red-100 text-red-700', major: 'bg-orange-100 text-orange-700', minor: 'bg-amber-100 text-amber-700', cosmetic: 'bg-slate-100 text-slate-700' }; const statusHistoryColors = { completed: 'bg-emerald-100 text-emerald-700', in_progress: 'bg-blue-100 text-blue-700', parts_ordered: 'bg-purple-100 text-purple-700' }; return (
{/* Navigation Top - standard layout of other detail profiles */}
Node Registry: {center.id}
{/* Main Profile Info Header - rounded-xl alignment matching maintenance page */}
{/* Glow effect decorative */}

{center.name}

{center.status}
{center.rating.toFixed(1)}
{/* Profile items - clean, consistent spacing */}
{center.address} {center.googleMapLink && ( Map Link )}
{center.phone}
{center.email}
Staff: {center.staffCount} technicians
Capacity: {center.capacity} total slots
{/* Specialization List Header */}
Node Specializations
{center.specialization.map(spec => ( {spec} ))}
{/* Quick Stats Header Summary - simplified matching maintenance specs, occupancy not needed */}

Repairs Logs

{totalRepairs}

{completedRepairs.length} completed

Capacity

{center.capacity}

Service slots registered

{/* Analytics: Map Mockup & Cost Breakdown Grid */}
{/* INTERACTIVE STYLIZED MAP CONTAINER - aligned clean white rounded-xl styles */}

Node Location Map

Dhaka city arterial coverage grid mockup

{/* Map canvas container */}
{/* Pulsating target coordinate representing the Center */}
setMapPopup(center.name)} >
{/* Stylized Dhaka grids & landmarks using SVGs */} {/* Arterial Highways */} {/* Waterway (Gulshan Lake) */} {/* Other hubs mockup dots */} {/* Scale watermark */}
GPS: {center.latitude.toFixed(4)}°N, {center.longitude.toFixed(4)}°E
{/* Stylized popup when clicked */} {mapPopup && (
{center.name}

{center.address}

Capacity: {center.capacity} slots Rating: {center.rating.toFixed(1)}
)} {/* Custom street labels */}
Gulshan Lake Road
Tejgaon-Gulshan Link Road
📍 Click GPS coordinate node for telemetry details
{/* FINANCIAL PERFORMANCE & EXPENSE TRACKING */}

Financial Performance & Cost Margins

Aggregated historical metrics from completed maintenance invoices

{/* Financial details panel */}
Estimated Spend

৳{totalEstimatedCost.toLocaleString()}

Budgeted repairs cost

Actual Invoice Spend

৳{totalActualCost.toLocaleString()}

Billed repair totals

0 ? 'bg-rose-50 border-rose-100' : 'bg-emerald-50 border-emerald-100'}`}> Cost Variance

0 ? 'text-rose-700' : 'text-emerald-700'}`}> {costVariance > 0 ? `+৳${costVariance.toLocaleString()}` : `-৳${Math.abs(costVariance).toLocaleString()}`}

{costVariance > 0 ? 'Over budget invoices' : 'Under budget savings!'}

{/* Parts Used Aggregates & Labor Breakdown */}
{/* Margins */}

Expense Margin Distribution

{/* Parts Spend Bar */}
Spare Parts Cost ৳{totalPartsCost.toLocaleString()} ({totalSpend > 0 ? Math.round((totalPartsCost/totalSpend)*100) : 0}%)
0 ? (totalPartsCost/totalSpend)*100 : 0}%` }} />
{/* Labor Spend Bar */}
Labor Costs ৳{totalLaborCost.toLocaleString()} ({totalSpend > 0 ? Math.round((totalLaborCost/totalSpend)*100) : 0}%)
0 ? (totalLaborCost/totalSpend)*100 : 0}%` }} />
{/* General Health Tip */}
Cost Ratio Notice: This node maintains a healthy parts-to-labor ratio of {totalSpend > 0 ? Math.round((totalPartsCost/totalSpend)*100) : 0}:{totalSpend > 0 ? Math.round((totalLaborCost/totalSpend)*100) : 0}. Lower labor ratios reflect technician efficiency.
{/* Parts utilized list */}

Top Spare Parts Log

{topParts.length === 0 ? (

No spare parts recorded yet

) : topParts.map(part => (
{part.name}
Qty: {part.totalQty} ৳{part.totalCost.toLocaleString()}
))}
{/* Interactive History Log - aligned standard filters and table headers */}
{/* Section title */}

Serviced Fleet History Log

Integrated audit list for EV bikes and Battery Swap maintenance nodes

{/* Quick Counter */} {sortedHistory.length} Matches Found
{/* Filter Controls Panel */}
{/* Search bar */}
setSearchQuery(e.target.value)} className="w-full pl-9 pr-3 py-2 border border-slate-200 rounded-lg text-xs focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent transition-all bg-white" />
{/* Filter 1: Asset Type */} {/* Filter 2: Service Type */} {/* Sorting Dropdown */}
{/* Table / List representation */} {sortedHistory.length === 0 ? (

No matching service records

Try clearing filters or search variables

) : (
{sortedHistory.map(record => ( ))}
Record ID Asset Code Service Type Description Severity Status Invoice cost Details
{record.id}
{record.assetType === 'EV Bike' ? ( ) : ( )}
{record.assetId} {record.assetType}
{record.serviceType}

{record.description}

Tech: {record.technician} {record.date}
{record.severity} {record.status.replace('_', ' ')} {record.actualCost > 0 ? ( ৳{record.actualCost.toLocaleString()} ) : ( Pending invoice )} {/* Deep link details */}
)}
); }