Shift Performance Analysis

Units Produced (Today)

0

Overall Defect Rate

0%

Machine Downtime

0m

Avg. Productivity

0

Shift Comparison

Production Trend (Last 7 Days)

Recent Production Log

TimestampShiftBatch IDUnits ProducedDefects

Log Production Batch

Full Production Log

TimestampShiftBatch IDUnitsDefectsAction

Error: A required library is missing.

'; return; } // --- DATA MANAGEMENT --- let productionLog = JSON.parse(localStorage.getItem('spa_prodLog')) || []; const getSampleData = () => { const data = []; const now = new Date('2025-07-09T22:00:00Z'); for (let i = 0; i < 50; i++) { const timestamp = new Date(now.getTime() - i * 3 * 3600 * 1000); const units = Math.floor(Math.random() * 200) + 800; data.push({ id: 'B' + (1000 + i), timestamp: timestamp.toISOString(), shift: timestamp.getUTCHours() >= 7 && timestamp.getUTCHours() < 19 ? 'Day Shift' : 'Night Shift', units: units, defects: Math.floor(units * (Math.random() * 0.05)), // 0-5% defect rate downtime: Math.floor(Math.random() * 15) // 0-15 mins }); } productionLog = data; }; if (productionLog.length === 0) getSampleData(); const saveState = () => localStorage.setItem('spa_prodLog', JSON.stringify(productionLog)); // --- CHART INSTANCES & UTILITIES --- let comparisonChart, trendChart; const tabButtons = document.querySelectorAll('.spa-tab-button'); const tabContents = document.querySelectorAll('.spa-tab-content'); const nextBtn = document.getElementById('spa-next-btn'); const prevBtn = document.getElementById('spa-prev-btn'); // --- RENDER FUNCTIONS --- const renderAll = () => { try { const todayStr = new Date('2025-07-09T22:00:00Z').toISOString().split('T')[0]; const todaysLog = productionLog.filter(l => l.timestamp.startsWith(todayStr)); renderKPIs(todaysLog); renderComparisonChart(todaysLog); renderTrendChart(); renderLogTables(); updateNavButtons(); } catch(error) { console.error("Dashboard rendering failed:", error); } }; const getShiftMetrics = (log) => { const metrics = { units: 0, defects: 0, downtime: 0, hours: 0 }; log.forEach(l => { metrics.units += l.units; metrics.defects += l.defects; metrics.downtime += l.downtime || 0; }); // Assuming each shift is 12 hours for productivity calculation metrics.hours = 12; return metrics; }; const renderKPIs = (todaysLog) => { const totalUnits = todaysLog.reduce((sum, l) => sum + l.units, 0); const totalDefects = todaysLog.reduce((sum, l) => sum + l.defects, 0); const totalDowntime = todaysLog.reduce((sum, l) => sum + (l.downtime || 0), 0); const defectRate = totalUnits > 0 ? (totalDefects / totalUnits) * 100 : 0; const productivity = totalUnits / 24; // 2 shifts document.getElementById('spa-units-today-kpi').textContent = totalUnits.toLocaleString('en-US'); document.getElementById('spa-defect-rate-kpi').textContent = `${defectRate.toFixed(2)}%`; document.getElementById('spa-downtime-kpi').textContent = `${totalDowntime}m`; document.getElementById('spa-productivity-kpi').textContent = `${productivity.toFixed(1)} u/hr`; }; const renderComparisonChart = (todaysLog) => { const dayShiftMetrics = getShiftMetrics(todaysLog.filter(l => l.shift === 'Day Shift')); const nightShiftMetrics = getShiftMetrics(todaysLog.filter(l => l.shift === 'Night Shift')); const dayProductivity = dayShiftMetrics.hours > 0 ? dayShiftMetrics.units / dayShiftMetrics.hours : 0; const nightProductivity = nightShiftMetrics.hours > 0 ? nightShiftMetrics.units / nightShiftMetrics.hours : 0; const dayDefectRate = dayShiftMetrics.units > 0 ? (dayShiftMetrics.defects / dayShiftMetrics.units) * 100 : 0; const nightDefectRate = nightShiftMetrics.units > 0 ? (nightShiftMetrics.defects / nightShiftMetrics.units) * 100 : 0; const options = { chart: { type: 'bar', height: 350 }, series: [ { name: 'Day Shift', data: [dayProductivity, dayDefectRate, dayShiftMetrics.downtime] }, { name: 'Night Shift', data: [nightProductivity, nightDefectRate, nightShiftMetrics.downtime] } ], xaxis: { categories: ['Productivity (u/hr)', 'Defect Rate (%)', 'Downtime (min)'] }, plotOptions: { bar: { horizontal: false, columnWidth: '50%'} }, colors: ['var(--spa-day-shift-color)', 'var(--spa-night-shift-color)'], dataLabels: { enabled: false } }; if(comparisonChart) comparisonChart.destroy(); document.querySelector("#spa-comparison-chart").innerHTML = ''; comparisonChart = new ApexCharts(document.querySelector("#spa-comparison-chart"), options); comparisonChart.render(); }; const renderTrendChart = () => { const last7Days = [...new Set(productionLog.map(l => l.timestamp.split('T')[0]))].sort().slice(-7); const dayData = last7Days.map(day => productionLog.filter(l => l.timestamp.startsWith(day) && l.shift === 'Day Shift') .reduce((sum, l) => sum + l.units, 0) ); const nightData = last7Days.map(day => productionLog.filter(l => l.timestamp.startsWith(day) && l.shift === 'Night Shift') .reduce((sum, l) => sum + l.units, 0) ); const options = { chart: { type: 'line', height: 350, toolbar: { show: false } }, series: [{ name: 'Day Shift', data: dayData }, { name: 'Night Shift', data: nightData }], xaxis: { categories: last7Days.map(d => new Date(d+'T00:00:00').toLocaleDateString('en-US', {month:'short', day:'numeric'})) }, stroke: { curve: 'smooth', width: 3 }, colors: ['var(--spa-day-shift-color)', 'var(--spa-night-shift-color)'] }; if(trendChart) trendChart.destroy(); document.querySelector("#spa-trend-chart").innerHTML = ''; trendChart = new ApexCharts(document.querySelector("#spa-trend-chart"), options); trendChart.render(); }; const renderLogTables = () => { const recentTbody = document.getElementById('spa-log-tbody'); recentTbody.innerHTML = ''; productionLog.sort((a,b) => new Date(b.timestamp) - new Date(a.timestamp)).slice(0,5).forEach(l => { recentTbody.innerHTML += `${new Date(l.timestamp).toLocaleString('en-US')}${l.shift}${l.id}${l.units}${l.defects}`; }); const manageTbody = document.getElementById('spa-manage-tbody'); manageTbody.innerHTML = ''; productionLog.forEach(l => { manageTbody.innerHTML += `${new Date(l.timestamp).toLocaleString('en-US')}${l.shift}${l.id}${l.units}${l.defects}`; }); }; // --- 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(`.spa-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('spa-log-form').addEventListener('submit', e => { e.preventDefault(); productionLog.unshift({ id: 'B' + Date.now().toString().slice(-4), timestamp: new Date(document.getElementById('spa-log-datetime').value).toISOString(), shift: document.getElementById('spa-log-shift').value, units: parseInt(document.getElementById('spa-log-units').value), defects: parseInt(document.getElementById('spa-log-defects').value), downtime: 0 }); saveState(); renderAll(); e.target.reset(); }); document.getElementById('spa-manage-tbody').addEventListener('click', e => { if(e.target.tagName === 'BUTTON') { if(confirm('Are you sure you want to delete this log entry?')) { productionLog = productionLog.filter(l => l.id !== e.target.dataset.id); saveState(); renderAll(); } } }); // --- PDF EXPORT --- document.getElementById('spa-download-pdf-btn').addEventListener('click', function() { const btn = this; btn.textContent = 'Generating...'; btn.disabled = true; const content = document.getElementById('spa-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'); doc.setFontSize(18); doc.text('Shift Performance Report', 14, 22); doc.addImage(imgData, 'PNG', 10, 30, 277, 150); // A4 landscape is 297x210 doc.addPage(); doc.text('Full Production Log', 14, 22); doc.autoTable({ html: '.spa-log-table', startY: 30, theme: 'striped', headStyles: { fillColor: [52, 73, 94] } }); doc.save('Shift_Performance_Report.pdf'); }).finally(() => { btn.textContent = 'Download Report'; btn.disabled = false; }); }); // --- INITIALIZATION --- renderAll(); });
Scroll to Top