@@ -7,71 +7,95 @@ import useOrderStore from "@/app/zustand/order";
77import { AggregatedSlotData } from "@/model/timeslot" ;
88import { ORDER_AMOUNT_THRESHOLDS , TIME_SLOT_CONFIG } from "@/config" ;
99import { timeslotToLocalTime } from "@/lib/time" ;
10+ import { useShallow } from "zustand/react/shallow" ;
1011
11- const generateAllTimeslots = ( startDate : Date , stopDate : Date ) : AggregatedSlotData [ ] => {
12- const timeSlots : AggregatedSlotData [ ] = [ ] ;
13- const currentTime = new Date ( startDate ) ;
14-
15- while ( currentTime <= stopDate ) {
16- timeSlots . push ( {
17- time : currentTime . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) ,
18- height : 0 ,
19- } ) ;
20- currentTime . setMinutes ( currentTime . getMinutes ( ) + 5 ) ;
21- }
22-
23- return timeSlots ;
24- } ;
25-
26- const Timeline = ( ) => {
12+ const Timeline = ( {
13+ noClick
14+ } : { noClick : boolean } = { noClick : false } ) => {
2715 const [ timeslots , setTimeslots ] = useState < AggregatedSlotData [ ] > ( [ ] ) ;
2816 const [ isLoading , setIsLoading ] = useState ( true ) ;
2917 const [ error , setError ] = useState < string | null > ( null ) ;
3018 const t = useTranslations ( ) ;
3119
32- // Timeline date range calculation (can be done outside component if static)
33- const { startDate, stopDate } = useMemo ( ( ) => {
34- const startDate = new Date ( )
35- startDate . setHours ( startDate . getHours ( ) - 1 ) ;
36- startDate . setMinutes ( 0 , 0 , 0 ) ;
20+ const { setTimeslot, order, totalItems } = useOrderStore (
21+ useShallow ( ( state ) => ( {
22+ setTimeslot : state . setTimeslot ,
23+ order : state . currentOrder ,
24+ totalItems : state . getTotalItemCount ( )
25+ } ) )
26+ ) ;
3727
38- const stopDate = new Date ( ) ;
39- stopDate . setHours ( stopDate . getHours ( ) + 1 ) ;
40- stopDate . setMinutes ( 59 , 59 , 999 ) ;
41- return { startDate, stopDate } ;
28+ // Generate fallback timeslots
29+ const generateFallbackSlots = useCallback ( ( ) => {
30+ const slots : AggregatedSlotData [ ] = [ ] ;
31+ const start = new Date ( ) ;
32+ start . setHours ( start . getHours ( ) - 1 , 0 , 0 , 0 ) ;
33+
34+ const end = new Date ( ) ;
35+ end . setHours ( end . getHours ( ) + 1 , 59 , 59 , 999 ) ;
36+
37+ const current = new Date ( start ) ;
38+ while ( current <= end ) {
39+ slots . push ( {
40+ time : current . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) ,
41+ height : 0 ,
42+ } ) ;
43+ current . setMinutes ( current . getMinutes ( ) + 5 ) ;
44+ }
45+ return slots ;
4246 } , [ ] ) ;
4347
44- const { setTimeslot } = useOrderStore ( ) ;
4548 const fetchTimeline = useCallback ( async ( ) => {
4649 setError ( null ) ;
47- let apiSlots : AggregatedSlotData [ ] = [ ] ;
48- const response = await fetch ( '/api/timeline' ) ;
49- if ( ! response . ok ) {
50- setError ( 'Failed to fetch timeline data' ) ;
51- apiSlots = generateAllTimeslots ( startDate , stopDate ) ;
52- } else {
53- apiSlots = await response . json ( )
54- apiSlots = apiSlots . map ( ( item : AggregatedSlotData ) => ( {
55- ...item ,
56- time : timeslotToLocalTime ( item . time ) ,
57- } ) )
50+ try {
51+ const response = await fetch ( '/api/timeline' ) ;
52+ let apiSlots : AggregatedSlotData [ ] ;
53+
54+ if ( ! response . ok ) {
55+ setError ( 'Failed to fetch timeline data' ) ;
56+ apiSlots = generateFallbackSlots ( ) ;
57+ } else {
58+ apiSlots = await response . json ( ) ;
59+ apiSlots = apiSlots . map ( item => ( {
60+ ...item ,
61+ time : timeslotToLocalTime ( item . time ) ,
62+ } ) ) as AggregatedSlotData [ ] ;
63+ }
64+
65+ setTimeslots ( apiSlots ) ;
66+ } catch ( err ) {
67+ setError ( 'Network error' ) ;
68+ setTimeslots ( generateFallbackSlots ( ) ) ;
69+ } finally {
70+ setIsLoading ( false ) ;
5871 }
72+ } , [ generateFallbackSlots ] ) ;
73+
74+ // Enhanced timeslots with selected state highlighting
75+ const enhancedTimeslots = useMemo ( ( ) => {
76+ const selectedTime = timeslotToLocalTime ( order . timeslot ) ;
77+ return timeslots . map ( slot => ( {
78+ ...slot ,
79+ ...( slot . time === selectedTime && {
80+ border : TIME_SLOT_CONFIG . BORDER_STYLES . CURRENT_SLOT_COLOR ,
81+ borderwidth : TIME_SLOT_CONFIG . BORDER_STYLES . CURRENT_SLOT_WIDTH ,
82+ color : TIME_SLOT_CONFIG . STATUS_COLORS . SELECT ,
83+ ordersAmount : totalItems > 0 ? totalItems : 1
84+ } )
85+ } ) ) ;
86+ } , [ timeslots , order . timeslot , totalItems ] ) ;
5987
60- setTimeslots ( apiSlots ) ;
61- setIsLoading ( false ) ;
62- } , [ startDate , stopDate , TIME_SLOT_CONFIG . UPDATE_EVERY_SECONDS ] ) ;
88+ const handleBarClick = useCallback ( ( event : any ) => {
89+ if ( ! noClick && event ?. activeLabel ) {
90+ setTimeslot ( event . activeLabel ) ;
91+ }
92+ } , [ noClick , setTimeslot ] ) ;
6393
6494 useEffect ( ( ) => {
6595 fetchTimeline ( ) ;
6696 const interval = setInterval ( fetchTimeline , TIME_SLOT_CONFIG . UPDATE_EVERY_SECONDS * 1000 ) ;
6797 return ( ) => clearInterval ( interval ) ;
68- } , [ fetchTimeline , TIME_SLOT_CONFIG . UPDATE_EVERY_SECONDS ] ) ;
69-
70- const handleBarClick = useCallback ( ( event : any ) => {
71- if ( event ?. activeLabel ) {
72- setTimeslot ( event . activeLabel ) ;
73- }
74- } , [ ] ) ;
98+ } , [ fetchTimeline ] ) ;
7599
76100 if ( isLoading ) {
77101 return (
@@ -85,22 +109,21 @@ const Timeline = () => {
85109 < div className = "w-full" >
86110 { error && (
87111 < div className = "mb-2 p-2 bg-yellow-100 border border-yellow-400 text-yellow-700 rounded" >
88- { t ( 'components.timeline.warning' , { error : error } ) }
112+ { t ( 'components.timeline.warning' , { error } ) }
89113 </ div >
90114 ) }
91115
92116 < ResponsiveContainer height = { 300 } >
93117 < BarChart
94- data = { timeslots }
118+ data = { enhancedTimeslots }
95119 onClick = { handleBarClick }
96120 margin = { { top : 5 , right : 30 , left : - 30 , bottom : 5 } }
97121 >
98122 < XAxis dataKey = "time" />
99123 < YAxis domain = { [ 0 , ORDER_AMOUNT_THRESHOLDS . MAX ] } />
100124 < Tooltip />
101-
102125 < Bar dataKey = "ordersAmount" name = "Orders" fill = "#007bff" >
103- { timeslots . map ( ( entry , index ) => (
126+ { enhancedTimeslots . map ( ( entry , index ) => (
104127 < Cell
105128 key = { `cell-${ index } -${ entry . time } ` }
106129 fill = { entry . color ?? "#007bff" }
0 commit comments