Skip to content

Commit 05e790b

Browse files
committed
Add drivers reports
1 parent 1a65ed7 commit 05e790b

File tree

2 files changed

+148
-7
lines changed

2 files changed

+148
-7
lines changed

client/hash.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const bcryptjs = require('bcryptjs')
22

3-
const plainPassword = 'Francis03'
3+
const plainPassword = 'hanni18'
44

55
bcryptjs.hash(plainPassword, 10, (err, hashedPassword) => {
66
if (err) {

client/src/views/pages/admin/Reports.js

Lines changed: 147 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,26 @@ const Reports = () => {
2626
const [shipments, setShipments] = useState([])
2727
const [loading, setLoading] = useState(true)
2828
const [error, setError] = useState(null)
29+
const [drivers, setDrivers] = useState([])
2930

3031
useEffect(() => {
31-
const fetchShipments = async () => {
32+
const fetchData = async () => {
3233
try {
33-
const res = await axios.get('/api/shipments')
34-
setShipments(Array.isArray(res.data) ? res.data : [])
34+
const [shipmentsRes, driversRes] = await Promise.all([
35+
axios.get('/api/shipments'),
36+
axios.get('/api/drivers'),
37+
])
38+
39+
setShipments(Array.isArray(shipmentsRes.data) ? shipmentsRes.data : [])
40+
setDrivers(Array.isArray(driversRes.data) ? driversRes.data : [])
3541
} catch (err) {
36-
setError('Failed to load shipment reports.')
42+
setError('Failed to load shipment or driver data.')
3743
} finally {
3844
setLoading(false)
3945
}
4046
}
4147

42-
fetchShipments()
48+
fetchData()
4349
}, [])
4450

4551
const summarize = (days) => {
@@ -65,10 +71,51 @@ const Reports = () => {
6571
range: `${start.format('MMM D')} - ${today.format('MMM D')}`,
6672
}
6773
}
74+
const summarizeDrivers = (days) => {
75+
const today = dayjs()
76+
const start = today.subtract(days, 'day')
77+
78+
const filtered = shipments.filter((s) => dayjs(s.updated_at).isAfter(start))
79+
80+
const driverMap = {}
81+
drivers.forEach((d) => {
82+
driverMap[d.username] = d.name
83+
})
84+
85+
const result = {}
86+
Object.values(driverMap).forEach((name) => {
87+
result[name] = {
88+
count: 0,
89+
shipments: [],
90+
}
91+
})
92+
93+
filtered.forEach((s) => {
94+
const driverName = driverMap[s.driverUsername] || 'Unknown'
95+
if (!result[driverName]) {
96+
result[driverName] = {
97+
count: 0,
98+
shipments: [],
99+
}
100+
}
101+
result[driverName].count += 1
102+
result[driverName].shipments.push({
103+
trackingNumber: s.trackingNumber,
104+
date: dayjs(s.updated_at).format('YYYY-MM-DD'),
105+
})
106+
})
107+
108+
return result
109+
}
110+
68111
const daily = summarize(1)
69112
const weekly = summarize(7)
70113
const monthly = summarize(30)
71114

115+
const driverDaily = summarizeDrivers(1)
116+
const driverWeekly = summarizeDrivers(7)
117+
const driverMonthly = summarizeDrivers(30)
118+
72119
const exportAsPDF = () => {
73120
const doc = new jsPDF()
74121

@@ -95,6 +142,44 @@ const Reports = () => {
95142

96143
doc.save('shipment-analytics.pdf')
97144
}
145+
const exportDriverReportsPDF = () => {
146+
const doc = new jsPDF()
147+
let y = 10
148+
149+
const addDriverSection = (title, data, range) => {
150+
doc.text(`${title} (${range})`, 14, y)
151+
y += 6
152+
153+
const body = Object.entries(data).map(([driver, info]) => {
154+
const shipmentsStr = info.shipments
155+
.map((s) => `#${s.trackingNumber} - (${s.date})`)
156+
.join('\n')
157+
158+
return [driver, info.count.toString(), shipmentsStr]
159+
})
160+
161+
autoTable(doc, {
162+
startY: y,
163+
head: [['Driver', 'Total Shipments', 'Shipments']],
164+
body: body,
165+
margin: { top: 10, left: 14 },
166+
styles: { fontSize: 10, cellPadding: 2, overflow: 'linebreak' },
167+
columnStyles: {
168+
0: { cellWidth: 40 },
169+
1: { cellWidth: 30, halign: 'center' },
170+
2: { cellWidth: 110 },
171+
},
172+
})
173+
174+
y = doc.lastAutoTable.finalY + 8
175+
}
176+
177+
addDriverSection('Driver Daily Report', driverDaily, daily.range)
178+
addDriverSection('Driver Weekly Report', driverWeekly, weekly.range)
179+
addDriverSection('Driver Monthly Report', driverMonthly, monthly.range)
180+
181+
doc.save('driver-delivery-report.pdf')
182+
}
98183

99184
const renderTable = (summary) => (
100185
<CTable small responsive>
@@ -125,6 +210,33 @@ const Reports = () => {
125210
</CTable>
126211
)
127212

213+
const renderDriverTable = (data) => (
214+
<CTable small responsive>
215+
<CTableHead>
216+
<CTableRow>
217+
<CTableHeaderCell>Driver</CTableHeaderCell>
218+
<CTableHeaderCell>Total Shipments</CTableHeaderCell>
219+
<CTableHeaderCell>Shipments</CTableHeaderCell>
220+
</CTableRow>
221+
</CTableHead>
222+
<CTableBody>
223+
{Object.entries(data).map(([driver, info], index) => (
224+
<CTableRow key={index}>
225+
<CTableDataCell>{driver}</CTableDataCell>
226+
<CTableDataCell>{info.count}</CTableDataCell>
227+
<CTableDataCell>
228+
{info.shipments.map((s) => (
229+
<div key={s.trackingNumber}>
230+
#{s.trackingNumber} -({s.date})
231+
</div>
232+
))}
233+
</CTableDataCell>
234+
</CTableRow>
235+
))}
236+
</CTableBody>
237+
</CTable>
238+
)
239+
128240
return (
129241
<>
130242
{loading && <CSpinner color="primary" />}
@@ -133,9 +245,18 @@ const Reports = () => {
133245
<>
134246
<div className="d-flex justify-content-end mb-3">
135247
<CButton color="primary" variant="outline" onClick={exportAsPDF}>
136-
Export as PDF
248+
Export Shipment Report
249+
</CButton>
250+
<CButton
251+
color="success"
252+
variant="outline"
253+
className="ms-2"
254+
onClick={exportDriverReportsPDF}
255+
>
256+
Export Drivers Report
137257
</CButton>
138258
</div>
259+
139260
<CCard>
140261
<CCardHeader>Shipment Analytics Report</CCardHeader>
141262
<CCardBody>
@@ -155,6 +276,26 @@ const Reports = () => {
155276
</CAccordion>
156277
</CCardBody>
157278
</CCard>
279+
280+
<CCard className="mt-4">
281+
<CCardHeader>Driver Delivery Reports</CCardHeader>
282+
<CCardBody>
283+
<CAccordion alwaysOpen>
284+
<CAccordionItem itemKey={4}>
285+
<CAccordionHeader>📅 Driver Daily Report ({daily.range})</CAccordionHeader>
286+
<CAccordionBody>{renderDriverTable(driverDaily)}</CAccordionBody>
287+
</CAccordionItem>
288+
<CAccordionItem itemKey={5}>
289+
<CAccordionHeader>📈 Driver Weekly Report ({weekly.range})</CAccordionHeader>
290+
<CAccordionBody>{renderDriverTable(driverWeekly)}</CAccordionBody>
291+
</CAccordionItem>
292+
<CAccordionItem itemKey={6}>
293+
<CAccordionHeader>📆 Driver Monthly Report ({monthly.range})</CAccordionHeader>
294+
<CAccordionBody>{renderDriverTable(driverMonthly)}</CAccordionBody>
295+
</CAccordionItem>
296+
</CAccordion>
297+
</CCardBody>
298+
</CCard>
158299
</>
159300
)}
160301
</>

0 commit comments

Comments
 (0)