} const load = (k) => localStorage.getItem('otis-' + k); const save = (k, v) => localStorage.setItem('otis-' + k, v); // LOGO const lInput = document.getElementById('logoInput'); const uLogo = document.getElementById('userLogo'); const uploadDiv = document.getElementById('uploadPrompt'); function applyLogo(d) { if (!d) return; uLogo.src = d; uLogo.style.display = 'block'; if (uploadDiv) uploadDiv.style.display = 'none'; } const savedLogo = load('user-logo'); if (savedLogo) applyLogo(savedLogo); document.getElementById('logoClickZone')?.addEventListener('click', () => lInput.click()); if (lInput) { lInput.onchange = (e) => { const f = e.target.files[0]; if (!f) return; if (f.size > 2 * 1024 * 1024) { alert("File too large. Use < 2MB."); return; } const r = new FileReader(); r.onload = (ev) => { const d = ev.target.result; save('user-logo', d); applyLogo(d); }; r.readAsDataURL(f); }; } // DILUTION const dTot = document.getElementById('dil-total'); const dRat = document.getElementById('dil-ratio'); const dRes = document.getElementById('dil-result'); function calcDil() { const t = parseFloat(dTot.value) || 0; const r = parseFloat(dRat.value) || 0; const c = t / (r + 1); const w = t - c; dRes.innerHTML = `CHEMICAL: ${c.toFixed(1)} oz
WATER: ${w.toFixed(1)} oz`; } if (dTot && dRat) { dTot.oninput = calcDil; dRat.oninput = calcDil; } // DETAILED LOGGING FUNCTIONS (14-day auto-clean) function getLog(key) { let raw = localStorage.getItem('otis-log-' + key); if (!raw) return []; try { let logs = JSON.parse(raw); let now = Date.now(); let cutoff = now - 14 * 86400000; let filtered = logs.filter(l => l.t > cutoff); if (filtered.length !== logs.length) { localStorage.setItem('otis-log-' + key, JSON.stringify(filtered)); } return filtered; } catch(e) { return []; } } function addToLog(key, data) { let logs = getLog(key); logs.push({ t: Date.now(), d: new Date().toLocaleDateString(), ...data }); localStorage.setItem('otis-log-' + key, JSON.stringify(logs)); } // DISPLAY function updateDisplay() { const p = parseFloat(load('daily-profit')) || 0; const h = parseFloat(load('daily-hours')) || 0; const mi = parseInt(load('daily-miles')) || 0; const e = parseFloat(load('daily-expenses')) || 0; document.getElementById('m-profit').innerText = "$" + p.toFixed(2); document.getElementById('m-hours').innerText = h.toFixed(1) + "h"; document.getElementById('m-miles').innerText = mi + " mi"; document.getElementById('m-exp').innerText = "$" + e.toFixed(2); document.getElementById('m-rate').innerText = h > 0 ? "$" + (p / h).toFixed(2) + "/hr" : "$0.00/hr"; const v = load('j-vehicle'); const pr = load('j-price'); document.getElementById('job-detail-val').innerText = v ? v + " | $" + pr : "SET JOB"; const sT = load('start-raw'); if (sT) document.getElementById('start-val').innerText = "START: " + new Date(sT).toLocaleTimeString([], {hour:'2-digit', minute:'2-digit'}); renderFullHistory(); } window.toggleModal = (id) => { const m = document.getElementById(id); if (m && id !== 'license-modal') m.style.display = m.style.display === 'block' ? 'none' : 'block'; }; window.logDetailedJob = () => { let v = prompt("Vehicle:"); let p = prompt("Price:"); if (v && p) { save('j-vehicle', v); save('j-price', p); updateDisplay(); } }; window.logTime = (type) => { const n = new Date(); save(type + '-raw', n.toISOString()); if (type === 'end') { setTimeout(() => { if (confirm("Finalize Job?")) finalize(); }, 300); } updateDisplay(); }; window.handleMileage = function() { let s = load('m-start'); const lb = document.getElementById('miles-val'); if (!s) { let v = prompt("Start Odo:"); if (v) { save('m-start', v); lb.innerText = "START: " + v; } } else { let v = prompt("End Odo:"); if (v) { let t = parseInt(v) - parseInt(s); save('daily-miles', (parseInt(load('daily-miles')) || 0) + t); localStorage.removeItem('otis-m-start'); lb.innerText = "TRIP: " + t; updateDisplay(); } } }; // Save expense (daily total + 14-day log) window.saveExpense = () => { const a = document.getElementById('exp-amt'); const d = document.getElementById('exp-desc'); const amt = parseFloat(a.value) || 0; const desc = d ? d.value : 'Expense'; if (amt > 0) { // Daily total (original) save('daily-expenses', (parseFloat(load('daily-expenses')) || 0) + amt); // 14-day log addToLog('expense', { description: desc, amount: amt }); if (d) d.value = ''; a.value = ''; toggleModal('expense-modal'); updateDisplay(); } }; function finalize() { const p = parseFloat(load('j-price')) || 0; const v = load('j-vehicle') || "Service"; const s = load('start-raw'); const e = load('end-raw'); if (!s || !e) return; const diff = (new Date(e) - new Date(s)) / 3600000; save('daily-profit', (parseFloat(load('daily-profit')) || 0) + p); save('daily-hours', (parseFloat(load('daily-hours')) || 0) + diff); let history = JSON.parse(load('history-data') || "[]"); history.unshift({ date: new Date().toLocaleDateString(), vehicle: v, earned: p, hours: diff.toFixed(1) }); save('history-data', JSON.stringify(history.slice(0, 50))); ['j-vehicle', 'j-price', 'start-raw', 'end-raw'].forEach(k => localStorage.removeItem('otis-' + k)); document.getElementById('start-val').innerText = "START: --:--"; document.getElementById('end-val').innerText = "END: --:--"; document.getElementById('job-detail-val').innerText = "SET JOB"; updateDisplay(); } function renderFullHistory() { const container = document.getElementById('history-log-container'); if (!container) return; let jobs = JSON.parse(load('history-data') || "[]"); let inspections = getLog('inspection'); let expenses = getLog('expense'); let html = '
📋 JOBS
'; jobs.forEach(job => { html += `
${job.date}
${job.vehicle} — $${job.earned} (${job.hours}h)
`; }); if (inspections.length) { html += '
🔍 PRE-EXISTING DAMAGE
'; inspections.forEach(ins => { let items = ins.checked || []; html += `
${ins.d}
${items.join(', ') || 'None recorded'}
`; }); } if (expenses.length) { html += '
💰 EXPENSES (14d)
'; expenses.forEach(exp => { html += `
${exp.d}
${exp.description || 'Expense'} — -$${exp.amount || exp.a}
`; }); } if (!jobs.length && !inspections.length && !expenses.length) { html = '
No logs yet
'; } container.innerHTML = html; } window.resetDay = function() { if (confirm("Reset Totals?")) { ['daily-profit', 'daily-hours', 'daily-miles', 'daily-expenses', 'history-data'].forEach(k => localStorage.removeItem('otis-' + k)); location.reload(); } }; const inspCont = document.getElementById('check-list-container'); if (inspCont) { const labs = ["#1 Driver Front", "#2 Driver Cockpit", "#3 Driver Backseat", "#4 Roof", "#5 Driver Rear", "#6 Pass Rear", "#7 Cargo", "#8 Trunk", "#9 Pass Backseat", "#10 Pass Roof", "#11 Pass Cockpit", "#12 Pass Front", "#13 Hood"]; inspCont.innerHTML = ''; labs.forEach(l => { inspCont.innerHTML += `
${l}
`; }); } // Hook inspection save on modal close let origToggle = window.toggleModal; window.toggleModal = function(id) { if (id === 'inspect-modal' && document.getElementById('inspect-modal').style.display === 'block') { let checkboxes = document.querySelectorAll('#check-list-container input[type="checkbox"]'); let labels = []; checkboxes.forEach((cb, idx) => { if (cb.checked) { let divs = document.querySelectorAll('#check-list-container div'); let labelText = divs[idx] ? divs[idx].innerText.split('\n')[0] : 'Area ' + (idx + 1); labels.push(labelText); } }); if (labels.length) { addToLog('inspection', { checked: labels, total: checkboxes.length }); } } origToggle(id); }; updateDisplay(); })();