SAMLING

Weighbridge Management System

Samling (M) Sdn. Bhd.

Default: admin / admin123

`); w.document.close(); } function cancelRecord(id) { const idx = records.findIndex(r => r.id === id); if (idx >= 0 && records[idx].status === 'first_weigh') { records[idx].status = 'cancelled'; showToast('Record cancelled'); navigateTo('records'); } } // ==================== REPORTS ==================== function renderReports() { const completed = records.filter(r => r.status === 'completed'); const cancelled = records.filter(r => r.status === 'cancelled'); const totalNet = completed.reduce((s, r) => s + (r.netWeight || 0), 0); // Weekly chart data const weekLabels = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; const weekNetData = [9800, 12400, 8700, 15200, totalNet || 10000, 6500, 11200]; const maxNet = Math.max(...weekNetData); let chartBars = weekLabels.map((d, i) => { const h = Math.max(4, (weekNetData[i] / maxNet) * 100); const isToday = i === 4; return `
${(weekNetData[i]/1000).toFixed(1)}k
${d}
`; }).join(''); // By product breakdown const byProduct = {}; completed.forEach(r => { byProduct[r.productName] = (byProduct[r.productName] || 0) + (r.netWeight || 0); }); const productRows = Object.entries(byProduct).map(([name, wt]) => `${name}${formatNumber(wt)}`).join(''); // By vehicle breakdown const byVehicle = {}; completed.forEach(r => { byVehicle[r.vehicle.plateNumber] = (byVehicle[r.vehicle.plateNumber] || 0) + (r.netWeight || 0); }); const vehicleRows = Object.entries(byVehicle).map(([plate, wt]) => `${plate}${formatNumber(wt)}`).join(''); return `

Total Records

${records.length}

Total Net Weight

${formatNumber(totalNet)} kg

Completed

${completed.length}

Cancelled

${cancelled.length}

Net Weight Trend (kg)

${chartBars}

By Product

${productRows}
ProductNet Weight (kg)

By Vehicle

${vehicleRows}
VehicleNet Weight (kg)
`; } function setReportQuick(period) { const d = new Date(); if (period === 'today') { reportDate = d.toISOString().split('T')[0]; reportType = 'daily'; } else if (period === 'yesterday') { d.setDate(d.getDate()-1); reportDate = d.toISOString().split('T')[0]; reportType = 'daily'; } else if (period === 'week') { reportDate = d.toISOString().split('T')[0]; reportType = 'weekly'; } else if (period === 'month') { reportDate = d.toISOString().split('T')[0]; reportType = 'monthly'; } navigateTo('reports'); } function printReport() { window.print(); } // ==================== VEHICLES ==================== function renderVehicles() { return `
${vehicles.map(v => ``).join('')}
Plate Number Driver Name Empty Weight Status Actions
${v.plateNumber} ${v.driverName} ${formatNumber(v.emptyWeight)} kg ${v.active ? 'Active' : 'Inactive'}
`; } function filterVehicles() { const q = document.getElementById('vehicle-search-input')?.value.toLowerCase() || ''; document.querySelectorAll('#vehicles-tbody tr').forEach(tr => { const plate = tr.dataset.plate || ''; const driver = tr.dataset.driver || ''; tr.style.display = (plate.includes(q) || driver.includes(q)) ? '' : 'none'; }); } function openVehicleModal(id) { const v = id ? vehicles.find(x => x.id === id) : null; openModal(`

${v ? 'Edit Vehicle' : 'Add Vehicle'}

`); } function saveVehicle(id) { const plate = document.getElementById('vm-plate').value.trim(); const driver = document.getElementById('vm-driver').value.trim(); const owner = document.getElementById('vm-owner').value.trim(); const empty = parseInt(document.getElementById('vm-empty').value) || 0; if (!plate || !driver) { showToast('Please fill required fields', 'error'); return; } if (id) { const idx = vehicles.findIndex(v => v.id === id); if (idx >= 0) { vehicles[idx] = { ...vehicles[idx], plateNumber: plate, driverName: driver, owner, emptyWeight: empty }; } showToast('Vehicle updated'); } else { vehicles.push({ id: Date.now(), plateNumber: plate, driverName: driver, owner, emptyWeight: empty, active: true }); showToast('Vehicle added'); } closeModal(); navigateTo('vehicles'); } function deleteVehicle(id) { if (confirm('Are you sure you want to delete this vehicle?')) { vehicles = vehicles.filter(v => v.id !== id); showToast('Vehicle deleted'); navigateTo('vehicles'); } } // ==================== USERS ==================== function renderUsers() { return `

Manage system users and their access roles.

${users.map(u => ``).join('')}
Username Name Role Status Actions
${u.username} ${u.name} ${roleBadge(u.role)}
`; } function toggleUserActive(id) { const u = users.find(x => x.id === id); if (u) { u.active = !u.active; navigateTo('users'); showToast(`User ${u.active ? 'activated' : 'deactivated'}`); } } function openUserModal(id) { const u = id ? users.find(x => x.id === id) : null; openModal(`

${u ? 'Edit User' : 'Add User'}

`); } function saveUser(id) { const username = document.getElementById('um-username').value.trim(); const name = document.getElementById('um-name').value.trim(); const role = document.getElementById('um-role').value; if (!username || !name) { showToast('Please fill required fields', 'error'); return; } if (id) { const idx = users.findIndex(u => u.id === id); if (idx >= 0) { users[idx] = { ...users[idx], name, role }; } showToast('User updated'); } else { const pw = document.getElementById('um-password').value; if (!pw) { showToast('Password is required for new users', 'error'); return; } users.push({ id: Date.now(), username, name, role, active: true }); showToast('User added'); } closeModal(); navigateTo('users'); } // ==================== SETTINGS ==================== function renderSettings() { return `

Company Settings

Weighbridge Location

Scale & Hardware Settings

Scale readings are currently simulated for demonstration purposes. Configure actual hardware connection for production use.

Display Settings

`; } function saveSettings() { const fields = ['company_name','company_address','company_phone','weighbridge_location','weighbridge_name','scale_port','scale_baud_rate','weight_unit','ticket_prefix','receipt_footer']; fields.forEach(f => { const el = document.getElementById('s-' + f); if (el) settings[f] = el.value; }); showToast('Settings saved successfully'); } // ==================== INIT ==================== document.addEventListener('DOMContentLoaded', function() { lucide.createIcons(); }); // Close dropdown on outside click document.addEventListener('click', function(e) { const dd = document.getElementById('vehicle-dropdown'); const sc = document.getElementById('vehicle-search-container'); if (dd && sc && !sc.contains(e.target)) dd.classList.add('hidden'); });