ZohnBorn/fleet-space
0
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8" />5 <meta name="viewport" content="width=device-width, initial-scale=1.0"/>6 <title>Fleet Management App</title>7 <script src="https://cdn.tailwindcss.com"></script>8</head>9<body class="bg-gray-100 font-sans text-gray-900">10 11<!-- App Container -->12<div id="app" class="flex flex-col h-screen">13 14 <!-- Header -->15 <header class="bg-blue-600 text-white p-4 text-center text-xl font-semibold">16 Fleet Management App17 </header>18 19 <!-- Main Content -->20 <main id="main" class="flex-1 p-4 overflow-auto"></main>21 22 <!-- Navigation -->23 <nav class="bg-blue-600 text-white flex justify-around items-center h-16">24 <button onclick="navigate('home')" class="flex flex-col items-center">25 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">26 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />27 </svg>28 <span>Home</span>29 </button>30 <button onclick="navigate('cars')" class="flex flex-col items-center">31 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">32 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />33 </svg>34 <span>Cars</span>35 </button>36 <button onclick="navigate('drivers')" class="flex flex-col items-center">37 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">38 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />39 </svg>40 <span>Drivers</span>41 </button>42 <button onclick="navigate('records')" class="flex flex-col items-center">43 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">44 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />45 </svg>46 <span>Records</span>47 </button>48 <button onclick="navigate('reports')" class="flex flex-col items-center">49 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">50 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />51 </svg>52 <span>Reports</span>53 </button>54 <button onclick="navigate('settings')" class="flex flex-col items-center">55 <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">56 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />57 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />58 </svg>59 <span>Settings</span>60 </button>61 </nav>62 63</div>64 65<!-- Scripts -->66<script type="module">67 // Mock data68 const cars = [69 { CarID: "CAR001", Make: "Toyota", Model: "Corolla", Year: 2023, LicensePlate: "ABC123GP", VIN: "VIN1234567890", MonthlyRentalCost: 500, MonthlyKms: 4000, TotalKms: 12000, AssignedDriver: "DRV001", Province: "Gauteng", Division: "Sales", Status: "Active" },70 { CarID: "CAR002", Make: "Ford", Model: "Ranger", Year: 2022, LicensePlate: "XYZ789EC", VIN: "VIN9876543210", MonthlyRentalCost: 700, MonthlyKms: 5500, TotalKms: 18000, AssignedDriver: "DRV002", Province: "Western Cape", Division: "Logistics", Status: "Maintenance" }71 ];72 const drivers = [73 { DriverID: "DRV001", FirstName: "John", LastName: "Smith", FullName: "John Smith", ContactNumber: "+27123456789", Position: "Field Manager", Province: "Gauteng", Division: "Sales", AssignedCar: "CAR001", Status: "Active" },74 { DriverID: "DRV002", FirstName: "Jane", LastName: "Doe", FullName: "Jane Doe", ContactNumber: "+27987654321", Position: "Logistics Lead", Province: "Western Cape", Division: "Logistics", AssignedCar: "CAR002", Status: "Active" }75 ];76 const records = [77 { RecordID: "REC001", CarID: "CAR001", MonthYear: "2025-09-01", MonthlyRentalCost: 500, MonthlyKms: 4000, Description: "September 2025 usage" },78 { RecordID: "REC002", CarID: "CAR002", MonthYear: "2025-09-01", MonthlyRentalCost: 700, MonthlyKms: 5500, Description: "September 2025 usage" }79 ];80 81 let currentMonth = new Date(new Date().getFullYear(), new Date().getMonth(), 1).toISOString().split('T')[0];82 let selectedCar = null;83 let selectedDriver = null;84 85 function navigate(screen) {86 const main = document.getElementById('main');87 main.innerHTML = '';88 switch (screen) {89 case 'home':90 showHome();91 break;92 case 'cars':93 showCars();94 break;95 case 'drivers':96 showDrivers();97 break;98 case 'records':99 showRecords();100 break;101 case 'reports':102 showReports();103 break;104 case 'settings':105 showSettings();106 break;107 }108 }109 110 function showHome() {111 const html = `112 <h2 class="text-2xl text-blue-600 text-center mb-4">Dashboard</h2>113 <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">114 <div class="bg-white p-4 border border-blue-600 rounded shadow">115 <h3 class="text-lg">Total Active Cars</h3>116 <p class="text-xl">${cars.filter(c => c.Status === 'Active').length}</p>117 </div>118 <div class="bg-white p-4 border border-blue-600 rounded shadow">119 <h3 class="text-lg">Total Active Drivers</h3>120 <p class="text-xl">${drivers.filter(d => d.Status === 'Active').length}</p>121 </div>122 <div class="bg-white p-4 border border-blue-600 rounded shadow">123 <h3 class="text-lg">Total Monthly Cost</h3>124 <p class="text-xl">$${(records.filter(r => r.MonthYear === currentMonth).reduce((a, b) => a + b.MonthlyRentalCost, 0)).toFixed(2)}</p>125 </div>126 <div class="bg-white p-4 border border-blue-600 rounded shadow">127 <h3 class="text-lg">Total Kms Driven</h3>128 <p class="text-xl">${records.filter(r => r.MonthYear === currentMonth).reduce((a, b) => a + b.MonthlyKms, 0)} km</p>129 </div>130 </div>131 <h3 class="text-lg text-blue-600 mb-2">Recent Records</h3>132 <ul class="bg-white p-4 border border-blue-600 rounded shadow max-h-64 overflow-y-auto">133 ${records.filter(r => r.MonthYear === currentMonth).map(r => `134 <li class="mb-2">${cars.find(c => c.CarID === r.CarID)?.LicensePlate} - ${r.MonthlyKms} km - $${r.MonthlyRentalCost}</li>135 `).join('')}136 </ul>137 `;138 document.getElementById('main').innerHTML = html;139 }140 141 function showCars() {142 document.getElementById('main').innerHTML = `143 <h2 class="text-2xl text-blue-600 text-center mb-4">Cars</h2>144 <input id="searchCar" class="w-full mb-4 p-2 border rounded" placeholder="Search by Make, Model or License Plate"/>145 <ul class="space-y-2">146 ${cars.map(car => `147 <li onclick="editCar('${car.CarID}')" class="bg-white p-3 border rounded shadow cursor-pointer">148 <div><strong>${car.Make} ${car.Model}</strong> - ${car.LicensePlate}</div>149 <div>Status: <span class="font-semibold ${car.Status === 'Active' ? 'text-green-500' : car.Status === 'Maintenance' ? 'text-yellow-500' : 'text-red-500'}">${car.Status}</span></div>150 <div>Driver: ${drivers.find(d => d.DriverID === car.AssignedDriver)?.FullName || 'None'}</div>151 </li>152 `).join('')}153 </ul>154 <button onclick="addCar()" class="mt-4 bg-green-500 text-white px-4 py-2 rounded">Add Car</button>155 `;156 }157 158 function showDrivers() {159 document.getElementById('main').innerHTML = `160 <h2 class="text-2xl text-blue-600 text-center mb-4">Drivers</h2>161 <input id="searchDriver" class="w-full mb-4 p-2 border rounded" placeholder="Search by Name or Contact"/>162 <ul class="space-y-2">163 ${drivers.map(driver => `164 <li onclick="editDriver('${driver.DriverID}')" class="bg-white p-3 border rounded shadow cursor-pointer">165 <div><strong>${driver.FirstName} ${driver.LastName}</strong></div>166 <div>Contact: ${driver.ContactNumber}</div>167 <div>Position: ${driver.Position}</div>168 <div>Status: <span class="font-semibold ${driver.Status === 'Active' ? 'text-green-500' : driver.Status === 'OnLeave' ? 'text-yellow-500' : 'text-red-500'}">${driver.Status}</span></div>169 </li>170 `).join('')}171 </ul>172 <button onclick="addDriver()" class="mt-4 bg-green-500 text-white px-4 py-2 rounded">Add Driver</button>173 `;174 }175 176 function showRecords() {177 document.getElementById('main').innerHTML = `178 <h2 class="text-2xl text-blue-600 text-center mb-4">Monthly Records</h2>179 <ul class="space-y-2">180 ${records.map(record => `181 <li class="bg-white p-3 border rounded shadow">182 <div>Car: ${cars.find(c => c.CarID === record.CarID)?.LicensePlate}</div>183 <div>Month: ${record.MonthYear}</div>184 <div>Kms: ${record.MonthlyKms} km</div>185 <div>Rental: $${record.MonthlyRentalCost}</div>186 </li>187 `).join('')}188 </ul>189 <button onclick="addRecord()" class="mt-4 bg-green-500 text-white px-4 py-2 rounded">Add Record</button>190 `;191 }192 193 function showReports() {194 const totalKms = records.reduce((a, b) => a + b.MonthlyKms, 0);195 const totalCost = records.reduce((a, b) => a + b.MonthlyRentalCost, 0);196 const avgCostPerKm = totalKms > 0 ? (totalCost / totalKms).toFixed(2) : 0;197 198 document.getElementById('main').innerHTML = `199 <h2 class="text-2xl text-blue-600 text-center mb-4">Reports</h2>200 <div class="bg-white p-4 border border-blue-600 rounded shadow mb-4">201 <p>Average Cost per Km: $${avgCostPerKm}/km</p>202 <p>Over-limit Cars: ${records.filter(r => r.MonthlyKms > 6000).length}</p>203 </div>204 <button class="bg-blue-600 text-white px-4 py-2 rounded">Export to Excel</button>205 `;206 }207 208 function showSettings() {209 document.getElementById('main').innerHTML = `210 <h2 class="text-2xl text-blue-600 text-center mb-4">Settings</h2>211 <input type="date" id="monthPicker" value="${currentMonth}" class="w-full mb-4 p-2 border rounded"/>212 <button onclick="refreshData()" class="bg-blue-600 text-white px-4 py-2 rounded mr-2">Refresh Data</button>213 <button onclick="resetMonthlyKms()" class="bg-blue-600 text-white px-4 py-2 rounded">Reset Monthly Kms</button>214 `;215 }216 217 function editCar(id) {218 selectedCar = cars.find(c => c.CarID === id);219 showCarForm();220 }221 222 function editDriver(id) {223 selectedDriver = drivers.find(d => d.DriverID === id);224 showDriverForm();225 }226 227 function addCar() {228 selectedCar = null;229 showCarForm();230 }231 232 function addDriver() {233 selectedDriver = null;234 showDriverForm();235 }236 237 function addRecord() {238 alert('Add Monthly Record Form (not implemented in this prototype)');239 }240 241 function refreshData() {242 alert('Data refreshed (mock)');243 }244 245 function resetMonthlyKms() {246 if (confirm('Are you sure you want to reset monthly kms?')) {247 cars.forEach(car => car.MonthlyKms = 0);248 alert('Monthly kms reset');249 }250 }251 252 function showCarForm() {253 const car = selectedCar || { CarID: '', Make: '', Model: '', LicensePlate: '' };254 document.getElementById('main').innerHTML = `255 <h2 class="text-2xl text-blue-600 text-center mb-4">${selectedCar ? 'Edit' : 'Add'} Car</h2>256 <form id="carForm" class="space-y-2">257 <input type="text" name="Make" value="${car.Make}" placeholder="Make" class="w-full p-2 border rounded" required />258 <input type="text" name="Model" value="${car.Model}" placeholder="Model" class="w-full p-2 border rounded" required />259 <input type="text" name="LicensePlate" value="${car.LicensePlate}" placeholder="License Plate" class="w-full p-2 border rounded" required />260 <button type="submit" class="bg-green-500 text-white px-4 py-2 rounded mr-2">Save</button>261 <button type="button" onclick="navigate('cars')" class="bg-red-500 text-white px-4 py-2 rounded">Cancel</button>262 </form>263 `;264 document.getElementById('carForm').onsubmit = function(e) {265 e.preventDefault();266 const formData = new FormData(e.target);267 const updatedCar = Object.fromEntries(formData);268 if (!selectedCar) {269 updatedCar.CarID = 'CAR' + (cars.length + 1).toString().padStart(3, '0');270 cars.push(updatedCar);271 } else {272 Object.assign(selectedCar, updatedCar);273 }274 navigate('cars');275 };276 }277 278 function showDriverForm() {279 const driver = selectedDriver || { FirstName: '', LastName: '', ContactNumber: '' };280 document.getElementById('main').innerHTML = `281 <h2 class="text-2xl text-blue-600 text-center mb-4">${selectedDriver ? 'Edit' : 'Add'} Driver</h2>282 <form id="driverForm" class="space-y-2">283 <input type="text" name="FirstName" value="${driver.FirstName}" placeholder="First Name" class="w-full p-2 border rounded" required />284 <input type="text" name="LastName" value="${driver.LastName}" placeholder="Last Name" class="w-full p-2 border rounded" required />285 <input type="text" name="ContactNumber" value="${driver.ContactNumber}" placeholder="Contact Number" class="w-full p-2 border rounded" required />286 <button type="submit" class="bg-green-500 text-white px-4 py-2 rounded mr-2">Save</button>287 <button type="button" onclick="navigate('drivers')" class="bg-red-500 text-white px-4 py-2 rounded">Cancel</button>288 </form>289 `;290 document.getElementById('driverForm').onsubmit = function(e) {291 e.preventDefault();292 const formData = new FormData(e.target);293 const updatedDriver = Object.fromEntries(formData);294 if (!selectedDriver) {295 updatedDriver.DriverID = 'DRV' + (drivers.length + 1).toString().padStart(3, '0');296 updatedDriver.FullName = `${updatedDriver.FirstName} ${updatedDriver.LastName}`;297 drivers.push(updatedDriver);298 } else {299 Object.assign(selectedDriver, updatedDriver);300 selectedDriver.FullName = `${updatedDriver.FirstName} ${updatedDriver.LastName}`;301 }302 navigate('drivers');303 };304 }305 306 // Initialize307 navigate('home');308</script>309 310<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-qwensite.hf.space/logo.svg" alt="qwensite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-qwensite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >QwenSite</a> - 🧬 <a href="https://enzostvs-qwensite.hf.space?remix=ZohnBorn/fleet-space" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>311</html>