@@ -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