fix: replace 3 operations sections with exact prototype code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-03-28 15:09:19 +08:00
parent df1ab64c97
commit 752208da02

View File

@@ -33,6 +33,8 @@ export default function App() {
customer?: string;
isColdChain?: boolean;
isTrailer?: boolean;
type?: string;
source?: string;
} | null>(null);
// Data state
@@ -128,8 +130,30 @@ export default function App() {
if (cat === 'Operating') params.category = 'Operating';
if (showPlateNumbers.manager) params.manager = showPlateNumbers.manager;
if (showPlateNumbers.customer) params.customer = showPlateNumbers.customer;
if (!showPlateNumbers.type) {
if (showPlateNumbers.isColdChain !== undefined) params.isColdChain = String(showPlateNumbers.isColdChain);
if (showPlateNumbers.isTrailer !== undefined) params.isTrailer = String(showPlateNumbers.isTrailer);
}
// Map prototype's type field to backend vehicleType
if (showPlateNumbers.type) {
if (showPlateNumbers.type === '4.5T') {
if (showPlateNumbers.isColdChain === true) {
params.vehicleType = '4.5T冷链';
} else if (showPlateNumbers.isColdChain === false) {
params.vehicleType = '4.5T普货';
}
} else if (showPlateNumbers.type === '18T') {
params.vehicleType = '18T';
} else if (showPlateNumbers.type === '49T') {
params.vehicleType = '49T';
} else if (showPlateNumbers.type === '其他车型') {
if (showPlateNumbers.isTrailer === true) {
params.isTrailer = 'true';
} else if (showPlateNumbers.isTrailer === false) {
params.vehicleType = '其他';
}
}
}
fetchVehicleList(params)
.then(setModalVehicles)
.catch(() => setModalVehicles([]))
@@ -860,8 +884,9 @@ export default function App() {
</div>
</div>
{/* Department Operations Statistics */}
<section className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden mb-6">
<section className="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden mb-6">
<div className="p-3 sm:p-4 border-b border-gray-50 flex items-center justify-between gap-4">
<div className="flex items-center gap-3">
<div className="w-1.5 h-6 bg-blue-600 rounded-full"></div>
@@ -873,7 +898,7 @@ export default function App() {
</div>
<div className="p-0 sm:p-2 bg-gray-50/30">
{/* Overall Total Summary (Compact) */}
{/* Overall Total Summary (Compact) - Moved to Top */}
<div className="m-2 bg-slate-800 rounded-xl p-3 text-white shadow-lg">
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
<div className="flex flex-col">
@@ -894,7 +919,9 @@ export default function App() {
</div>
<div className="flex flex-col">
<span className="text-[9px] opacity-50 uppercase font-bold tracking-widest mb-0.5 text-blue-400"></span>
<span className="text-xl font-black text-blue-400"></span>
<span className="text-xl font-black text-blue-400">
{'—'}
</span>
</div>
</div>
</div>
@@ -980,11 +1007,20 @@ export default function App() {
<td className="p-2 border-r border-gray-100 font-bold text-gray-800">
{dept.department}
</td>
<td className="p-2 border-r border-gray-100 text-center"><span className="bg-blue-50 text-blue-600 text-[10px] font-bold px-2 py-0.5 rounded-full"></span></td>
<td className="p-2 border-r border-gray-100 text-center">
<span className="bg-blue-50 text-blue-600 text-[10px] font-bold px-2 py-0.5 rounded-full">
{'—'}
</span>
</td>
<td className="p-2 border-r border-gray-100 text-center font-black text-gray-800 text-sm">
{dept.totalAssets}
</td>
<td className="p-2 border-r border-gray-100 text-center"><div className="flex items-baseline justify-center gap-1"><span className="font-black text-gray-400 text-sm"></span></div></td>
<td className="p-2 border-r border-gray-100 text-center">
<div className="flex items-baseline justify-center gap-1">
<span className="font-black text-gray-800 text-sm">{'—'}</span>
<span className="text-[9px] text-gray-400 font-bold">km</span>
</div>
</td>
<td className="p-2 border-r border-gray-100 text-center font-black text-green-500 text-sm">
{dept.operatingCount}
</td>
@@ -1014,7 +1050,7 @@ export default function App() {
<button
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating', source: 'department' });
}}
className="text-[10px] font-bold text-blue-600 bg-blue-50 px-2 py-0.5 rounded hover:bg-blue-100 transition-colors"
>
@@ -1027,42 +1063,42 @@ export default function App() {
<div className="grid grid-cols-3 gap-1 mt-2">
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T普货' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">4.5T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5}</div>
</div>
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T冷链' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5c}</div>
</div>
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '18T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">18T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t18}</div>
</div>
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '49T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">49T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t49}</div>
</div>
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, isTrailer: true })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.trailer}</div>
</div>
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '其他' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.other}</div>
@@ -1098,7 +1134,7 @@ export default function App() {
className="p-2 border-r border-gray-100 text-center font-black text-blue-600 text-sm cursor-pointer hover:bg-blue-50"
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, source: 'department' });
}}
>
{m.total}
@@ -1113,7 +1149,7 @@ export default function App() {
<button
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating', source: 'department' });
}}
className="text-blue-500 hover:text-blue-700 transition-colors"
>
@@ -1125,27 +1161,27 @@ export default function App() {
<tr className="bg-gray-50/50 border-b border-gray-100">
<td colSpan={10} className="p-0">
<div className="grid grid-cols-6 text-[10px] bg-white/50">
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T普货' })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, source: 'department' })}>
<span className="text-gray-400 uppercase mb-1">4.5T</span>
<span className="font-bold text-gray-600">{m.t4_5}</span>
</div>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T冷链' })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, source: 'department' })}>
<span className="text-gray-400 uppercase mb-1"></span>
<span className="font-bold text-gray-600">{m.t4_5c}</span>
</div>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '18T' })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', source: 'department' })}>
<span className="text-gray-400 uppercase mb-1">18T</span>
<span className="font-bold text-gray-600">{m.t18}</span>
</div>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '49T' })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', source: 'department' })}>
<span className="text-gray-400 uppercase mb-1">49T</span>
<span className="font-bold text-gray-600">{m.t49}</span>
</div>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, isTrailer: true })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, source: 'department' })}>
<span className="text-gray-400 uppercase mb-1"></span>
<span className="font-bold text-gray-600">{m.trailer}</span>
</div>
<div className="p-2 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '其他' })}>
<div className="p-2 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, source: 'department' })}>
<span className="text-gray-400 uppercase mb-1"></span>
<span className="font-bold text-gray-600">{m.other}</span>
</div>
@@ -1185,7 +1221,7 @@ export default function App() {
</div>
<div className="text-center">
<div className="text-[8px] text-gray-400 uppercase font-bold mb-0.5"></div>
<div className="text-xs font-black text-gray-400"></div>
<div className="text-xs font-black text-gray-800">{'—'}</div>
</div>
<div className="text-center">
<div className="text-[8px] text-green-500 uppercase font-bold mb-0.5"></div>
@@ -1217,7 +1253,7 @@ export default function App() {
<button
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, source: 'department' });
}}
className="text-[10px] font-bold text-blue-600 bg-blue-50 px-2 py-0.5 rounded"
>
@@ -1229,42 +1265,42 @@ export default function App() {
<div className="p-2 border-t border-gray-50 bg-gray-50/30 grid grid-cols-3 gap-1">
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T普货' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">4.5T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T冷链' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5c}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '18T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">18T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t18}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '49T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">49T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t49}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, isTrailer: true })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.trailer}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '其他' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.other}</div>
@@ -1297,7 +1333,7 @@ export default function App() {
className="text-[11px] font-bold text-blue-600 whitespace-nowrap"
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, source: 'department' });
}}
>
: {m.total}
@@ -1308,7 +1344,7 @@ export default function App() {
<button
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating', source: 'department' });
}}
className="text-blue-500 p-1 hover:bg-blue-50 rounded transition-colors flex-shrink-0"
>
@@ -1320,42 +1356,42 @@ export default function App() {
<div className="p-2 border-t border-gray-50 bg-gray-50/30 grid grid-cols-3 gap-1">
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T普货' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">4.5T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T冷链' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5c}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '18T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">18T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t18}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '49T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">49T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t49}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, isTrailer: true })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.trailer}</div>
</div>
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '其他' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.other}</div>
@@ -1486,70 +1522,88 @@ export default function App() {
</tr>
</thead>
<tbody className="text-xs">
{filteredRegionData.map((r) => {
if (r.totalAssets === 0) return null;
const isExpanded = expandedRegions.has(r.region);
{uniqueRegions.filter(r => !regionFilters.region || r === regionFilters.region).map((region) => {
const regionStats = customerData.filter(s => {
const matchRegion = s.region === region;
const matchCity = !regionFilters.city || s.city === regionFilters.city;
const matchCustomer = !regionFilters.customer || s.customer.toLowerCase().includes(regionFilters.customer.toLowerCase());
return matchRegion && matchCity && matchCustomer;
});
const totalAssets = regionStats.reduce((acc, s) => acc + s.total, 0);
if (totalAssets === 0) return null;
const isExpanded = expandedRegions.has(region);
return (
<React.Fragment key={r.region}>
<React.Fragment key={region}>
<tr
className={`border-b border-slate-100 cursor-pointer transition-colors ${isExpanded ? 'bg-slate-50' : 'bg-white hover:bg-slate-50/50'}`}
onClick={() => toggleRegion(r.region)}
onClick={() => toggleRegion(region)}
>
<td className="p-2 font-bold text-slate-700 flex items-center gap-2">
{isExpanded ? <ChevronDown size={14} className="text-slate-400" /> : <ChevronRight size={14} className="text-slate-400" />}
<Truck size={14} className="text-slate-400" />
{r.region}
{region}
</td>
<td className="p-2 text-center font-bold text-slate-600">{r.totalAssets}</td>
<td className="p-2 text-center font-bold text-slate-600">{totalAssets}</td>
<td
className="p-2 text-center text-green-600 font-bold cursor-pointer hover:bg-green-50"
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating', source: 'asset' });
}}
>
{r.operatingCount}
{Math.floor(totalAssets * 0.8)}
</td>
<td
className="p-2 text-center text-orange-600 font-bold cursor-pointer hover:bg-orange-50"
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending', source: 'asset' });
}}
>
{r.inventoryCount}
{Math.floor(totalAssets * 0.05)}
</td>
<td className="p-2 text-center text-slate-500 font-medium">
{r.customers.slice(0, 2).join(', ')}
{regionStats.slice(0, 2).map(s => s.customer).join(', ')}
</td>
</tr>
{isExpanded && r.typeBreakdown.map(tb => {
if (tb.total === 0) return null;
const vehicleType = tb.type === '4.5T' ? '4.5T普货' : tb.type;
{isExpanded && ['4.5T', '18T', '49T'].map(type => {
const typeTotal = regionStats.reduce((acc, s) => {
if (type === '4.5T') return acc + s.t4_5 + s.t4_5c;
if (type === '18T') return acc + s.t18;
if (type === '49T') return acc + s.t49;
return acc;
}, 0);
if (typeTotal === 0) return null;
return (
<React.Fragment key={tb.type}>
<React.Fragment key={type}>
<tr className="border-b border-gray-50 hover:bg-gray-50">
<td className="p-2 pl-8 text-gray-500 flex items-center gap-2">
<div className="w-1 h-1 bg-slate-300 rounded-full"></div>
{tb.type}
{type}
</td>
<td className="p-2 text-center text-gray-600">{tb.total}</td>
<td className="p-2 text-center text-gray-600">{typeTotal}</td>
<td
className="p-2 text-center text-green-600 cursor-pointer hover:bg-green-50"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', vehicleType, category: 'Operating' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', type, category: 'Operating', source: 'asset' })}
>
{tb.operating}
{Math.floor(typeTotal * 0.8)}
</td>
<td
className="p-2 text-center text-orange-600 cursor-pointer hover:bg-orange-50"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', vehicleType, category: 'Pending' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', type, category: 'Pending', source: 'asset' })}
>
{tb.inventory}
{Math.floor(typeTotal * 0.05)}
</td>
<td className="p-2 text-center text-gray-400 italic">
{tb.customers.join(', ')}
{regionStats.filter(s => {
if (type === '4.5T') return (s.t4_5 + s.t4_5c) > 0;
if (type === '18T') return s.t18 > 0;
if (type === '49T') return s.t49 > 0;
return false;
}).map(s => s.customer).join(', ')}
</td>
</tr>
</React.Fragment>
@@ -1564,60 +1618,73 @@ export default function App() {
{/* Mobile View (Region) */}
<div className="lg:hidden p-2 space-y-3">
{filteredRegionData.map((r) => {
if (r.totalAssets === 0) return null;
const isExpanded = expandedRegions.has(r.region);
{uniqueRegions.filter(r => !regionFilters.region || r === regionFilters.region).map((region) => {
const regionStats = customerData.filter(s => {
const matchRegion = s.region === region;
const matchCity = !regionFilters.city || s.city === regionFilters.city;
const matchCustomer = !regionFilters.customer || s.customer.toLowerCase().includes(regionFilters.customer.toLowerCase());
return matchRegion && matchCity && matchCustomer;
});
const totalAssets = regionStats.reduce((acc, s) => acc + s.total, 0);
if (totalAssets === 0) return null;
const isExpanded = expandedRegions.has(region);
return (
<div key={r.region} className="bg-slate-50/50 rounded-xl border border-slate-100 overflow-hidden">
<div key={region} className="bg-slate-50/50 rounded-xl border border-slate-100 overflow-hidden">
<div
className="bg-white p-3 flex justify-between items-center cursor-pointer"
onClick={() => toggleRegion(r.region)}
onClick={() => toggleRegion(region)}
>
<div className="flex items-center gap-2 font-bold text-slate-700">
{isExpanded ? <ChevronDown size={14} className="text-slate-400" /> : <ChevronRight size={14} className="text-slate-400" />}
<Truck size={14} className="text-slate-400" />
{r.region}
{region}
</div>
<div className="text-xs font-bold text-slate-500">: {r.totalAssets}</div>
<div className="text-xs font-bold text-slate-500">: {totalAssets}</div>
</div>
{isExpanded && (
<>
<div className="p-2 grid grid-cols-2 gap-2 text-center border-t border-slate-100">
<div
className="bg-white p-2 rounded border border-slate-100 cursor-pointer active:bg-green-50"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating', source: 'asset' })}
>
<div className="text-[9px] text-gray-400 uppercase"></div>
<div className="text-xs font-bold text-green-600">{r.operatingCount}</div>
<div className="text-xs font-bold text-green-600">{Math.floor(totalAssets * 0.8)}</div>
</div>
<div
className="bg-white p-2 rounded border border-slate-100 cursor-pointer active:bg-orange-50"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending', source: 'asset' })}
>
<div className="text-[9px] text-gray-400 uppercase"></div>
<div className="text-xs font-bold text-orange-600">{r.inventoryCount}</div>
<div className="text-xs font-bold text-orange-600">{Math.floor(totalAssets * 0.05)}</div>
</div>
</div>
<div className="px-2 pb-2 space-y-1">
{r.typeBreakdown.map(tb => {
if (tb.total === 0) return null;
const vehicleType = tb.type === '4.5T' ? '4.5T普货' : tb.type;
{['4.5T', '18T', '49T'].map(type => {
const typeTotal = regionStats.reduce((acc, s) => {
if (type === '4.5T') return acc + s.t4_5 + s.t4_5c;
if (type === '18T') return acc + s.t18;
if (type === '49T') return acc + s.t49;
return acc;
}, 0);
if (typeTotal === 0) return null;
return (
<div key={tb.type} className="flex justify-between items-center text-[10px] bg-white/80 px-2 py-1.5 rounded border border-slate-50">
<span className="text-gray-500">{tb.type} </span>
<div key={type} className="flex justify-between items-center text-[10px] bg-white/80 px-2 py-1.5 rounded border border-slate-50">
<span className="text-gray-500">{type} </span>
<div className="flex gap-3">
<span
className="font-bold text-green-600 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', vehicleType, category: 'Operating' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', type, category: 'Operating', source: 'asset' })}
>
:{tb.operating}
:{Math.floor(typeTotal * 0.8)}
</span>
<span
className="font-bold text-orange-600 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', vehicleType, category: 'Pending' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', type, category: 'Pending', source: 'asset' })}
>
:{tb.inventory}
:{Math.floor(typeTotal * 0.05)}
</span>
</div>
</div>
@@ -1804,17 +1871,17 @@ export default function App() {
<span className="bg-gray-100 px-2 py-0.5 rounded text-[10px] font-medium">{cust.region}</span>
</td>
<td className="p-2 border-r border-gray-100 text-gray-600 text-center">{cust.manager}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '4.5T普货' }); }}>{cust.t4_5}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '4.5T冷链' }); }}>{cust.t4_5c}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '18T' }); }}>{cust.t18}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '49T' }); }}>{cust.t49}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, isTrailer: true }); }}>{cust.trailer}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '其他' }); }}>{cust.other}</td>
<td className="p-2 text-center font-bold bg-emerald-50 text-emerald-800 cursor-pointer hover:bg-emerald-100 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer }); }}>{cust.total}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', source: 'customer' }); }}>{cust.t4_5}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', source: 'customer' }); }}>{cust.t4_5c}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '18T', source: 'customer' }); }}>{cust.t18}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '49T', source: 'customer' }); }}>{cust.t49}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer' }); }}>{cust.trailer}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer' }); }}>{cust.other}</td>
<td className="p-2 text-center font-bold bg-emerald-50 text-emerald-800 cursor-pointer hover:bg-emerald-100 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, source: 'customer' }); }}>{cust.total}</td>
</tr>
{isExpanded && (
<tr className="bg-gray-50/30">
<td colSpan={10} className="p-2">
<td colSpan={9} className="p-2">
<div className="grid grid-cols-4 gap-2">
<div className="bg-white p-2 rounded border border-gray-100 shadow-sm">
<div className="text-[10px] text-gray-400 uppercase mb-1"></div>
@@ -1834,7 +1901,7 @@ export default function App() {
<div className="bg-white p-2 rounded border border-gray-100 shadow-sm">
<div className="text-[10px] text-gray-400 uppercase mb-1"></div>
<div className="text-sm font-bold text-gray-700">
{((cust.total / (customerData.reduce((s, c) => s + c.total, 0) || 1)) * 100).toFixed(1)}%
{((cust.total / deptData.reduce((s, d) => s + d.totalAssets, 0)) * 100).toFixed(1)}%
</div>
</div>
</div>
@@ -1894,7 +1961,7 @@ export default function App() {
<div className="bg-gray-50 p-2 rounded border border-gray-100">
<div className="text-[8px] text-gray-400 uppercase mb-1"></div>
<div className="text-xs font-bold text-gray-700">
{((cust.total / (customerData.reduce((s, c) => s + c.total, 0) || 1)) * 100).toFixed(1)}%
{((cust.total / deptData.reduce((s, d) => s + d.totalAssets, 0)) * 100).toFixed(1)}%
</div>
</div>
</div>
@@ -1904,42 +1971,42 @@ export default function App() {
<div className="grid grid-cols-3 gap-2">
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '4.5T普货' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase">4.5T</div>
<div className="text-[10px] font-bold text-gray-600">{cust.t4_5}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '4.5T冷链' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{cust.t4_5c}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '18T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '18T', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase">18T</div>
<div className="text-[10px] font-bold text-gray-600">{cust.t18}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '49T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '49T', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase">49T</div>
<div className="text-[10px] font-bold text-gray-600">{cust.t49}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, isTrailer: true })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{cust.trailer}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '其他' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{cust.other}</div>
@@ -1955,6 +2022,7 @@ export default function App() {
</div>
</section>
{/* Plate Number Modal */}
<AnimatePresence>
{showPlateNumbers && (