- Express + EJS server-rendered app - Supabase PostgreSQL database - Auth: username/password with bcrypt - Dashboard with business stats - Load CRUD with filters - WhatsApp message parser - Payment tracking - Shipper & vehicle management - Reports (monthly, top shippers, routes) - Government-app aesthetic (tricolor theme) - Dark mode support - Docker + Coolify deployment ready - Seed data from existing business ledger (88 loads, 41 shippers, 70 vehicles)
92 lines
2.9 KiB
JavaScript
92 lines
2.9 KiB
JavaScript
// India-specific utilities for FreightDesk
|
|
|
|
const STATES = {
|
|
KL:'Kerala',TN:'Tamil Nadu',KA:'Karnataka',AP:'Andhra Pradesh',TS:'Telangana',
|
|
MH:'Maharashtra',GJ:'Gujarat',RJ:'Rajasthan',UP:'Uttar Pradesh',DL:'Delhi',
|
|
HR:'Haryana',PB:'Punjab',WB:'West Bengal',MP:'Madhya Pradesh',GA:'Goa',
|
|
};
|
|
|
|
function formatINR(n) {
|
|
if (n === null || n === undefined || isNaN(n)) return '—';
|
|
return '₹' + parseFloat(n).toLocaleString('en-IN');
|
|
}
|
|
|
|
function formatINRShort(n) {
|
|
if (n === null || n === undefined || isNaN(n)) return '—';
|
|
const num = parseFloat(n);
|
|
if (num >= 100000) return '₹' + (num / 100000).toFixed(1) + 'L';
|
|
if (num >= 1000) return '₹' + (num / 1000).toFixed(1) + 'K';
|
|
return '₹' + num.toLocaleString('en-IN');
|
|
}
|
|
|
|
function formatDate(dateStr) {
|
|
if (!dateStr) return '—';
|
|
const d = new Date(dateStr);
|
|
if (isNaN(d.getTime())) return dateStr;
|
|
return d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });
|
|
}
|
|
|
|
function formatDateShort(dateStr) {
|
|
if (!dateStr) return '—';
|
|
const d = new Date(dateStr);
|
|
if (isNaN(d.getTime())) return dateStr;
|
|
return d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short' });
|
|
}
|
|
|
|
function validateVehicleNumber(number) {
|
|
if (!number) return { valid: false };
|
|
const cleaned = number.replace(/[\s\-\.]/g, '').toUpperCase();
|
|
if (!/^[A-Z]{2}\d{1,2}[A-Z]{1,3}\d{4}$/.test(cleaned)) return { valid: false };
|
|
const sc = cleaned.substring(0, 2);
|
|
return {
|
|
valid: true,
|
|
state_code: sc,
|
|
state_name: STATES[sc] || 'Unknown',
|
|
formatted: `${cleaned.substring(0,2)} ${cleaned.substring(2,4)} ${cleaned.substring(4,cleaned.length-4)} ${cleaned.slice(-4)}`,
|
|
};
|
|
}
|
|
|
|
function getStatusColor(status) {
|
|
const colors = {
|
|
'settled': 'green',
|
|
'completed': 'green',
|
|
'commission received': 'green',
|
|
'reconciled': 'green',
|
|
'handled directly by shipper': 'green',
|
|
'loaded / in transit': 'blue',
|
|
'assigned': 'blue',
|
|
'assigned vehicle': 'blue',
|
|
'pending collection': 'orange',
|
|
'partially pending': 'orange',
|
|
'fully pending from shipper': 'orange',
|
|
'delivered / pending collection': 'orange',
|
|
'commission due': 'orange',
|
|
'pending lead': 'gray',
|
|
'partial': 'gray',
|
|
'available vehicle': 'gray',
|
|
'commission adjusted': 'purple',
|
|
'cancelled': 'red',
|
|
};
|
|
return colors[status] || 'gray';
|
|
}
|
|
|
|
function calcCommission(freight, paidToDriver) {
|
|
if (!freight || !paidToDriver) return null;
|
|
return freight - paidToDriver;
|
|
}
|
|
|
|
function calcPendingFromShipper(freight, advance) {
|
|
if (!freight) return null;
|
|
return freight - (advance || 0);
|
|
}
|
|
|
|
function calcPendingToDriver(driverFreight, paid) {
|
|
if (!driverFreight) return null;
|
|
return driverFreight - (paid || 0);
|
|
}
|
|
|
|
module.exports = {
|
|
STATES, formatINR, formatINRShort, formatDate, formatDateShort,
|
|
validateVehicleNumber, getStatusColor,
|
|
calcCommission, calcPendingFromShipper, calcPendingToDriver,
|
|
};
|