Service Dashboard

Open Work Orders

0

Jobs Completed Today

0

SLA Compliance

0%

CSAT

0%

Work Orders by Status

Work Orders by Priority

Technician Leaderboard (Jobs Completed Today)

Active Work Orders

WO#PriorityService TypeTechnicianStatus

Create New Work Order

All Work Orders

WO#Service TypeStatusAction

Error: A required library is missing.

'; return; } // --- DATA MANAGEMENT --- let workOrders = JSON.parse(localStorage.getItem('sd_workOrders')) || []; const getSampleData = () => { const todayStr = new Date('2025-07-09').toISOString().split('T')[0]; return [ { id: 'WO-7501', type: 'HVAC Maintenance', status: 'Dispatched', priority: 'Normal', tech: 'John Smith', created: todayStr, completed: null }, { id: 'WO-7502', type: 'Network Outage', status: 'In-Progress', priority: 'Critical', tech: 'Jane Doe', created: todayStr, completed: null }, { id: 'WO-7503', type: 'Plumbing Repair', status: 'New', priority: 'High', tech: 'Unassigned', created: todayStr, completed: null }, { id: 'WO-7504', type: 'IT Hardware Install', status: 'In-Progress', priority: 'Normal', tech: 'Emily White', created: todayStr, completed: null }, { id: 'WO-7499', type: 'HVAC Maintenance', status: 'Completed', priority: 'Normal', tech: 'John Smith', created: '2025-07-08', completed: todayStr }, { id: 'WO-7498', type: 'Security System Check', status: 'Completed', priority: 'Low', tech: 'David Green', created: '2025-07-08', completed: todayStr }, ]; }; if (workOrders.length === 0) workOrders = getSampleData(); const saveState = () => localStorage.setItem('sd_workOrders', JSON.stringify(workOrders)); // --- CHART INSTANCES & UTILITIES --- let statusChart, priorityChart; const tabButtons = document.querySelectorAll('.sd-tab-button'); const tabContents = document.querySelectorAll('.sd-tab-content'); const nextBtn = document.getElementById('sd-next-btn'); const prevBtn = document.getElementById('sd-prev-btn'); // --- RENDER FUNCTIONS --- const renderAll = () => { try { const openOrders = workOrders.filter(o => o.status !== 'Completed'); renderKPIs(openOrders); renderStatusChart(openOrders); renderPriorityChart(openOrders); renderLeaderboard(); renderOrdersTable(); renderManageUI(); updateNavButtons(); } catch(error) { console.error("Dashboard rendering failed:", error); } }; const renderKPIs = (openOrders) => { const todayStr = new Date('2025-07-09').toISOString().split('T')[0]; const completedToday = workOrders.filter(o => o.completed === todayStr).length; document.getElementById('sd-open-orders-kpi').textContent = openOrders.length; document.getElementById('sd-completed-today-kpi').textContent = completedToday; document.getElementById('sd-sla-kpi').textContent = '98.5%'; // Static sample data document.getElementById('sd-csat-kpi').textContent = '94%'; // Static sample data }; const renderStatusChart = (openOrders) => { const byStatus = openOrders.reduce((acc, o) => { acc[o.status] = (acc[o.status] || 0) + 1; return acc; }, {}); const options = { chart: { type: 'donut', height: 350 }, series: Object.values(byStatus), labels: Object.keys(byStatus), legend: { position: 'bottom' } }; if(statusChart) statusChart.destroy(); document.querySelector("#sd-status-chart").innerHTML = ''; statusChart = new ApexCharts(document.querySelector("#sd-status-chart"), options); statusChart.render(); }; const renderPriorityChart = (openOrders) => { const byPriority = openOrders.reduce((acc, o) => { acc[o.priority] = (acc[o.priority] || 0) + 1; return acc; }, {}); const order = { 'Critical':1, 'High':2, 'Normal':3, 'Low':4 }; const sorted = Object.entries(byPriority).sort((a,b) => order[a[0]] - order[b[0]]); const options = { chart: { type: 'bar', height: 350, toolbar: { show: false } }, series: [{ name: 'Open Orders', data: sorted.map(s => s[1]) }], xaxis: { categories: sorted.map(s => s[0]) }, plotOptions: { bar: { distributed: true, horizontal: false } }, colors: ['var(--sd-danger-color)', 'var(--sd-warning-color)', 'var(--sd-primary-color)'], legend: { show: false } }; if(priorityChart) priorityChart.destroy(); document.querySelector("#sd-priority-chart").innerHTML = ''; priorityChart = new ApexCharts(document.querySelector("#sd-priority-chart"), options); priorityChart.render(); }; const renderLeaderboard = () => { const todayStr = new Date('2025-07-09').toISOString().split('T')[0]; const techScores = workOrders .filter(o => o.completed === todayStr) .reduce((acc, o) => { if (o.tech !== 'Unassigned') acc[o.tech] = (acc[o.tech] || 0) + 1; return acc; }, {}); const sortedTechs = Object.entries(techScores).sort((a,b) => b[1] - a[1]); const list = document.getElementById('sd-leaderboard'); list.innerHTML = '
    '; if (sortedTechs.length === 0) { list.querySelector('ol').innerHTML = '
  1. No jobs completed today.
  2. '; return; } sortedTechs.forEach(([name, count]) => { list.querySelector('ol').innerHTML += `
  3. ${name}${count} Jobs
  4. `; }); }; const renderOrdersTable = () => { const tbody = document.getElementById('sd-orders-tbody'); const searchVal = document.getElementById('sd-search-filter').value.toLowerCase(); const openOrders = workOrders.filter(o => o.status !== 'Completed'); const filtered = openOrders.filter(o => o.type.toLowerCase().includes(searchVal) || o.tech.toLowerCase().includes(searchVal) ); tbody.innerHTML = ''; filtered.forEach(o => { tbody.innerHTML += ` ${o.id} ${o.priority} ${o.type} ${o.tech} ${o.status} `; }); }; const renderManageUI = () => { const tbody = document.getElementById('sd-manage-tbody'); tbody.innerHTML = ''; workOrders.forEach(o => { tbody.innerHTML += `${o.id}${o.type}${o.status}`; }); const techs = [...new Set(workOrders.map(o => o.tech).filter(t => t !== 'Unassigned'))]; document.getElementById('sd-tech-list').innerHTML = techs.map(t => ``).join(''); const services = [...new Set(workOrders.map(o => o.type))]; document.getElementById('sd-service-list').innerHTML = services.map(s => ``).join(''); }; // --- EVENT HANDLING --- const switchTab = (tabId) => { tabContents.forEach(c => c.style.display = 'none'); tabButtons.forEach(b => b.classList.remove('active')); const activeContent = document.getElementById(tabId); const activeButton = document.querySelector(`.sd-tab-button[data-tab="${tabId}"]`); if (activeContent && activeButton) { activeContent.style.display = 'block'; activeButton.classList.add('active'); } updateNavButtons(); }; const updateNavButtons = () => { const i = [...tabButtons].findIndex(b => b.classList.contains('active')); prevBtn.disabled = i === 0; nextBtn.disabled = i === tabButtons.length - 1; }; tabButtons.forEach(b => b.addEventListener('click', () => switchTab(b.dataset.tab))); nextBtn.addEventListener('click', () => { const i = [...tabButtons].findIndex(b=>b.classList.contains('active')); if (i < tabButtons.length - 1) switchTab(tabButtons[i+1].dataset.tab); }); prevBtn.addEventListener('click', () => { const i = [...tabButtons].findIndex(b=>b.classList.contains('active')); if (i > 0) switchTab(tabButtons[i-1].dataset.tab); }); document.getElementById('sd-order-form').addEventListener('submit', e => { e.preventDefault(); const status = document.getElementById('sd-status').value; const todayStr = new Date('2025-07-09').toISOString().split('T')[0]; workOrders.unshift({ id: 'WO-' + (Math.floor(Math.random()*900)+7500), type: document.getElementById('sd-service-type').value, status: status, priority: document.getElementById('sd-priority').value, tech: document.getElementById('sd-technician').value, created: todayStr, completed: status === 'Completed' ? todayStr : null }); saveState(); renderAll(); e.target.reset(); }); document.getElementById('sd-manage-tbody').addEventListener('click', e => { if(e.target.tagName === 'BUTTON') { const id = e.target.dataset.id; if(confirm('Are you sure you want to delete this work order?')) { workOrders = workOrders.filter(o => o.id !== id); saveState(); renderAll(); } } }); document.getElementById('sd-search-filter').addEventListener('input', renderOrdersTable); // --- PDF EXPORT --- document.getElementById('sd-download-pdf-btn').addEventListener('click', function() { const btn = this; btn.textContent = 'Generating...'; btn.disabled = true; const content = document.getElementById('sd-pdf-capture-area'); html2canvas(content, { scale: 2 }).then(canvas => { const doc = new jsPDF({ orientation: 'l', unit: 'mm', format: 'a4' }); const imgData = canvas.toDataURL('image/png'); const pdfWidth = doc.internal.pageSize.getWidth(); const imgHeight = (canvas.height * pdfWidth) / canvas.width; doc.setFontSize(18); doc.text('Daily Service Report', 14, 22); doc.addImage(imgData, 'PNG', 0, 25, pdfWidth, imgHeight); doc.addPage(); doc.text('Active Work Orders Log', 14, 22); doc.autoTable({ html: '.sd-work-orders-table', startY: 30, theme: 'striped', headStyles: { fillColor: [44, 62, 80] } }); doc.save('Service_Dashboard_Report.pdf'); }).finally(() => { btn.textContent = 'Download Report'; btn.disabled = false; }); }); // --- INITIALIZATION --- renderAll(); });
    Scroll to Top