updates on the orderdetails page and implemented the download option
This commit is contained in:
197
src/lib/ordersPdfGenerator.js
Normal file
197
src/lib/ordersPdfGenerator.js
Normal file
@@ -0,0 +1,197 @@
|
||||
import { jsPDF } from 'jspdf';
|
||||
import autoTable from 'jspdf-autotable';
|
||||
import dayjs from 'dayjs';
|
||||
import { formatDoormileTimestamp } from '@/lib/doormileTimestamp';
|
||||
|
||||
/**
|
||||
* Generates an executive-grade operational PDF report for Doormile Orders.
|
||||
*/
|
||||
export async function generateOrdersPdfReport({
|
||||
title = 'Orders Report',
|
||||
subtitle = 'Operational Delivery Breakdown',
|
||||
dateRangeLabel = 'Last 7 Days',
|
||||
fromDate,
|
||||
toDate,
|
||||
rows = [],
|
||||
}) {
|
||||
const doc = new jsPDF({
|
||||
orientation: 'landscape',
|
||||
unit: 'mm',
|
||||
format: 'a4',
|
||||
});
|
||||
|
||||
const pageWidth = doc.internal.pageSize.getWidth();
|
||||
const pageHeight = doc.internal.pageSize.getHeight();
|
||||
|
||||
// 1. Top Brand Crimson Header Bar
|
||||
doc.setFillColor(200, 16, 46); // Doormile Brand Crimson #C8102E
|
||||
doc.rect(0, 0, pageWidth, 24, 'F');
|
||||
|
||||
// Company Name
|
||||
doc.setTextColor(255, 255, 255);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.setFontSize(15);
|
||||
doc.text('DOORMILE EXPRESS', 14, 10.5);
|
||||
|
||||
// Subtitle
|
||||
doc.setFont('helvetica', 'normal');
|
||||
doc.setFontSize(8.5);
|
||||
doc.text(`${title.toUpperCase()} · ${subtitle}`, 14, 17.5);
|
||||
|
||||
// Date and Metadata on Right
|
||||
doc.setFontSize(8);
|
||||
doc.text(
|
||||
`Period: ${dateRangeLabel} (${fromDate} to ${toDate})`,
|
||||
pageWidth - 14,
|
||||
10.5,
|
||||
{ align: 'right' }
|
||||
);
|
||||
doc.text(
|
||||
`Generated: ${dayjs().format('DD MMM YYYY, hh:mm A')}`,
|
||||
pageWidth - 14,
|
||||
17.5,
|
||||
{ align: 'right' }
|
||||
);
|
||||
|
||||
// 2. Metric KPI Summary Ribbon
|
||||
const totalOrders = rows.length;
|
||||
const completedOrders = rows.filter((r) =>
|
||||
['delivered', 'completed'].includes((r.orderstatus || '').toLowerCase())
|
||||
).length;
|
||||
const totalRevenue = rows.reduce(
|
||||
(sum, r) => sum + (Number(r.deliverycharges) || 0),
|
||||
0
|
||||
);
|
||||
const totalKm = rows.reduce((sum, r) => sum + (Number(r.kms) || 0), 0);
|
||||
|
||||
const kpis = [
|
||||
{ label: 'TOTAL ORDERS', val: String(totalOrders) },
|
||||
{
|
||||
label: 'DELIVERED SUCESSFULLY',
|
||||
val: `${completedOrders} (${totalOrders ? Math.round((completedOrders / totalOrders) * 100) : 0}%)`,
|
||||
},
|
||||
{
|
||||
label: 'DELIVERY CHARGES',
|
||||
val: `Rs. ${totalRevenue.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`,
|
||||
},
|
||||
{ label: 'TOTAL DISTANCE', val: `${totalKm.toFixed(1)} km` },
|
||||
];
|
||||
|
||||
doc.setFillColor(248, 250, 252);
|
||||
doc.rect(14, 28, pageWidth - 28, 13, 'F');
|
||||
doc.setDrawColor(226, 232, 240);
|
||||
doc.rect(14, 28, pageWidth - 28, 13, 'S');
|
||||
|
||||
const colWidth = (pageWidth - 28) / kpis.length;
|
||||
kpis.forEach((kpi, idx) => {
|
||||
const x = 14 + idx * colWidth + 5;
|
||||
doc.setTextColor(100, 116, 139);
|
||||
doc.setFontSize(6.5);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(kpi.label, x, 32.5);
|
||||
|
||||
doc.setTextColor(16, 24, 32);
|
||||
doc.setFontSize(9.5);
|
||||
doc.setFont('helvetica', 'bold');
|
||||
doc.text(kpi.val, x, 38);
|
||||
});
|
||||
|
||||
// 3. Tabular Orders List
|
||||
const tableData = rows.map((r, i) => [
|
||||
i + 1,
|
||||
r.orderid || '—',
|
||||
formatDoormileTimestamp(r.orderdate, 'DD/MM/YY hh:mm A') || '—',
|
||||
r.tenantname || '—',
|
||||
(r.pickupaddress || '—').substring(0, 42),
|
||||
`${r.deliverycustomer ? r.deliverycustomer + ' - ' : ''}${(r.deliveryaddress || '—').substring(0, 38)}`,
|
||||
r.ridername || 'Unassigned',
|
||||
r.kms ? `${Number(r.kms).toFixed(1)}` : '0.0',
|
||||
r.deliverycharges != null ? `Rs. ${Number(r.deliverycharges).toFixed(2)}` : '—',
|
||||
(r.orderstatus || 'pending').toUpperCase(),
|
||||
]);
|
||||
|
||||
autoTable(doc, {
|
||||
startY: 45,
|
||||
head: [
|
||||
[
|
||||
'#',
|
||||
'Order ID',
|
||||
'Date & Time',
|
||||
'Client',
|
||||
'Pickup Location',
|
||||
'Drop Customer & Location',
|
||||
'Miler',
|
||||
'Km',
|
||||
'Amount',
|
||||
'Status',
|
||||
],
|
||||
],
|
||||
body:
|
||||
tableData.length > 0
|
||||
? tableData
|
||||
: [
|
||||
[
|
||||
'—',
|
||||
'No orders recorded in this date range',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
],
|
||||
],
|
||||
theme: 'grid',
|
||||
headStyles: {
|
||||
fillColor: [200, 16, 46],
|
||||
textColor: [255, 255, 255],
|
||||
fontSize: 7.5,
|
||||
fontStyle: 'bold',
|
||||
halign: 'left',
|
||||
cellPadding: 2,
|
||||
},
|
||||
styles: {
|
||||
fontSize: 7,
|
||||
textColor: [30, 41, 59],
|
||||
cellPadding: 1.8,
|
||||
overflow: 'linebreak',
|
||||
},
|
||||
alternateRowStyles: {
|
||||
fillColor: [248, 250, 252],
|
||||
},
|
||||
columnStyles: {
|
||||
0: { cellWidth: 8, halign: 'center' },
|
||||
1: { cellWidth: 24, fontStyle: 'bold' },
|
||||
2: { cellWidth: 26 },
|
||||
3: { cellWidth: 26 },
|
||||
4: { cellWidth: 50 },
|
||||
5: { cellWidth: 54 },
|
||||
6: { cellWidth: 26 },
|
||||
7: { cellWidth: 14, halign: 'right' },
|
||||
8: { cellWidth: 22, halign: 'right', fontStyle: 'bold' },
|
||||
9: { cellWidth: 20, halign: 'center' },
|
||||
},
|
||||
margin: { left: 14, right: 14, top: 45, bottom: 12 },
|
||||
didDrawPage: (data) => {
|
||||
// Footer
|
||||
doc.setFontSize(7);
|
||||
doc.setTextColor(148, 163, 184);
|
||||
doc.text(
|
||||
'Doormile Express Operations Report · Confidential · System Generated',
|
||||
14,
|
||||
pageHeight - 6
|
||||
);
|
||||
doc.text(
|
||||
`Page ${doc.internal.getNumberOfPages()}`,
|
||||
pageWidth - 14,
|
||||
pageHeight - 6,
|
||||
{ align: 'right' }
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const cleanTitle = title.replace(/\s+/g, '_');
|
||||
doc.save(`Doormile_${cleanTitle}_${fromDate}_to_${toDate}.pdf`);
|
||||
}
|
||||
Reference in New Issue
Block a user