// GarageManager - SPA Frontend const API = '/modules/garage/api.php'; const UPLOADS = '/modules/garage/api.php?action=photo&file='; function $(s, ctx=document){return ctx.querySelector(s);} function $$(s, ctx=document){return [...ctx.querySelectorAll(s)];} function fmt(n){return n!=null?Number(n).toLocaleString('fr-FR'):'--';} function fmtPrice(n){return n!=null?Number(n).toFixed(2)+' \u20ac':'--';} function fmtDate(d){if(!d)return '--';return new Date(d).toLocaleDateString('fr-FR');} function ago(d){if(!d)return '';const diff=Math.floor((Date.now()-new Date(d))/86400000);if(diff===0)return "aujourd'hui";if(diff===1)return 'hier';return 'il y a '+diff+'j';} function toast(msg,type='success'){ const c=document.getElementById('toasts');const t=document.createElement('div'); t.className='toast '+type;t.textContent=msg;c.appendChild(t); setTimeout(()=>t.remove(),3000); } // NAV let currentPage='dashboard'; function navigate(page,params={}){ currentPage=page; $$('.page').forEach(p=>p.classList.remove('active')); $$('.nav-link').forEach(n=>n.classList.remove('active')); document.getElementById('page-'+page)?.classList.add('active'); document.querySelector('.nav-link[data-page="'+page+'"]')?.classList.add('active'); if(page==='dashboard')loadDashboard(); else if(page==='vehicles')loadVehicles(); else if(page==='vehicle')loadVehicleDetail(params.id); else if(page==='parts')loadParts(); else if(page==='maintenances-all')loadAllMaintenances(); } // API async function api(action,method='GET',data=null,extra=''){ const opts={method,headers:{}}; if(data&&!(data instanceof FormData)){opts.headers['Content-Type']='application/json';opts.body=JSON.stringify(data);} else if(data instanceof FormData){opts.body=data;} const r=await fetch(API+'?action='+action+extra,opts); const j=await r.json(); if(!j.ok)throw new Error(j.error||'Erreur API'); return j.data; } // DASHBOARD async function loadDashboard(){ try{ const[stats,vehicles,reminders]=await Promise.all([api('stats'),api('vehicles'),api('maintenances')]); $('#stat-vehicles').textContent=stats.vehicles; $('#stat-maintenances').textContent=stats.maintenances; $('#stat-parts').textContent=stats.parts; $('#stat-cost').textContent=fmtPrice(parseFloat(stats.total_cost)+parseFloat(stats.total_parts_cost)); renderDashboardVehicles(vehicles); renderReminders(reminders); }catch(e){toast(e.message,'error');} } function renderDashboardVehicles(list){ const el=$('#dashboard-vehicles'); if(!list.length){el.innerHTML='
Aucun v\u00e9hicule
Aucun rappel configur\u00e9
| V\u00e9hicule | Type | Prochaine date | Prochain km | \u00c9cart km |
|---|---|---|---|---|
| '+r.vehicle_name+''+(r.license_plate?' '+r.license_plate+'':'')+' | '+ ''+r.type+' | '+ ''+(r.next_date?''+fmtDate(r.next_date)+'':'--')+' | '+ ''+(r.next_km?fmt(r.next_km)+' km':'--')+' | '+ ''+(diff!=null?''+(diff>=0?'+':'')+fmt(diff)+' km':'--')+' |
Aucun v\u00e9hicule. Ajoutez-en un !
Aucun entretien enregistr\u00e9
| Date | Type | Description | Kilom\u00e9trage | Co\u00fbt MO | Pi\u00e8ces | Prochain | |
|---|---|---|---|---|---|---|---|
| '+fmtDate(m.date)+' '+ago(m.date)+' | '+
''+m.type+' | '+ ''+(m.description||'--')+' | '+ ''+(m.km?fmt(m.km)+' km':'--')+' | '+ ''+fmtPrice(m.cost)+' | '+ ''+(m.parts_count>0?'\ud83d\udd29 '+m.parts_count+' ('+fmtPrice(m.parts_cost)+')':'--')+' | '+ ''+(m.next_date?'\ud83d\udcc5 '+fmtDate(m.next_date):'')+' '+(m.next_km?' \ud83d\udee3\ufe0f '+fmt(m.next_km)+' km':'')+' | '+
''+ ' |
Aucune pi\u00e8ce enregistr\u00e9e
| Photo | Nom | Marque | R\u00e9f\u00e9rence | Cat\u00e9gorie | Prix unit. | Qt\u00e9 | Total | Fournisseur | |
|---|---|---|---|---|---|---|---|---|---|
| '+(p.photo?' | '+
''+p.name+' | '+ ''+(p.brand||'--')+' | '+ ''+(p.reference||'--')+' | '+
''+p.category+' | '+ ''+fmtPrice(p.price)+' | '+ ''+p.quantity+' '+p.unit+' | '+ ''+fmtPrice(parseFloat(p.price||0)*parseInt(p.quantity||1))+' | '+ ''+(p.supplier||'--')+' | '+ ''+ ' |
Aucune pi\u00e8ce
| Photo | Nom | Marque | R\u00e9f\u00e9rence | Cat\u00e9gorie | Prix unit. | Qt\u00e9 | Total | Entretien | |
|---|---|---|---|---|---|---|---|---|---|
| '+(p.photo?' | '+
''+escHtml(p.name)+' | '+ ''+(p.brand||'--')+' | '+ ''+(p.reference||'--')+' | '+
''+p.category+' | '+ ''+fmtPrice(p.price)+' | '+ ''+p.quantity+' '+p.unit+' | '+ ''+fmtPrice(parseFloat(p.price||0)*parseInt(p.quantity||1))+' | '+ ''+(p.maintenance_type?p.maintenance_type+' ('+fmtDate(p.maintenance_date)+')':'--')+' | '+ ''+ ' |
Aucun véhicule
| Date | Type | Description | Km | Coût M.O. | Pièces | Total | Mécanicien | |
|---|---|---|---|---|---|---|---|---|
| '+fmtDate(m.date)+' | '+ ''+m.type+' | '+ ''+escHtml(m.description||'--')+' | '+ ''+(m.km?fmt(m.km)+' km':'--')+' | '+ ''+fmtPrice(m.cost)+' | '+ ''+(m.parts_count>0?m.parts_count+' ('+fmtPrice(m.parts_cost)+')':'--')+' | '+ ''+fmtPrice(parseFloat(m.cost||0)+parseFloat(m.parts_cost||0))+' | '+ ''+(m.mechanic||'--')+' | '+ ''+ ' |
Aucun entretien