1- import { useState } from 'react' ;
1+ import { useEffect , useRef , useState } from 'react' ;
22import { useQuery } from '@tanstack/react-query' ;
3- import {
4- BarChart ,
5- Bar ,
6- LineChart ,
7- Line ,
8- XAxis ,
9- YAxis ,
10- Tooltip ,
11- ResponsiveContainer ,
12- } from 'recharts' ;
3+ import { BarChart , Bar , LineChart , Line , XAxis , YAxis , Tooltip } from 'recharts' ;
134import { api } from '../../api/client' ;
145import type { HistoryStatsTimeRange } from '../../types' ;
156
@@ -34,6 +25,8 @@ const COLORS = {
3425 cancelled : '#71717a' , // zinc-500
3526} ;
3627
28+ const CHART_HEIGHT = 192 ; // h-48 = 12rem = 192px
29+
3730function formatTimestamp ( timestamp : string , range : HistoryStatsTimeRange ) : string {
3831 const date = new Date ( timestamp ) ;
3932
@@ -82,9 +75,33 @@ function CustomTooltip({ active, payload, label }: CustomTooltipProps) {
8275}
8376
8477export function HistoryChart ( { groupId } : HistoryChartProps ) {
78+ const containerRef = useRef < HTMLDivElement > ( null ) ;
79+ const [ containerWidth , setContainerWidth ] = useState ( 0 ) ;
8580 const [ timeRange , setTimeRange ] = useState < HistoryStatsTimeRange > ( 'auto' ) ;
8681 const [ chartType , setChartType ] = useState < ChartType > ( 'bar' ) ;
8782
83+ // Track container width with ResizeObserver.
84+ useEffect ( ( ) => {
85+ const container = containerRef . current ;
86+ if ( ! container ) return ;
87+
88+ const updateWidth = ( ) => {
89+ const { width } = container . getBoundingClientRect ( ) ;
90+ if ( width > 0 ) {
91+ setContainerWidth ( width ) ;
92+ }
93+ } ;
94+
95+ // Check immediately.
96+ updateWidth ( ) ;
97+
98+ // Observe for size changes.
99+ const observer = new ResizeObserver ( updateWidth ) ;
100+ observer . observe ( container ) ;
101+
102+ return ( ) => observer . disconnect ( ) ;
103+ } , [ ] ) ;
104+
88105 const { data, isLoading, error } = useQuery ( {
89106 queryKey : [ 'historyStats' , groupId , timeRange ] ,
90107 queryFn : ( ) => api . getHistoryStats ( groupId , timeRange ) ,
@@ -152,7 +169,7 @@ export function HistoryChart({ groupId }: HistoryChartProps) {
152169 </ div >
153170
154171 { /* Chart */ }
155- < div className = "h-48" >
172+ < div ref = { containerRef } className = "h-48" >
156173 { isLoading ? (
157174 < div className = "flex h-full items-center justify-center" >
158175 < div className = "text-sm text-zinc-500" > Loading...</ div >
@@ -165,72 +182,80 @@ export function HistoryChart({ groupId }: HistoryChartProps) {
165182 < div className = "flex h-full items-center justify-center" >
166183 < div className = "text-sm text-zinc-500" > No historical data available</ div >
167184 </ div >
168- ) : (
169- < ResponsiveContainer width = "100%" height = "100%" >
170- { chartType === 'bar' ? (
171- < BarChart data = { chartData } margin = { { top : 5 , right : 5 , left : - 20 , bottom : 5 } } >
172- < XAxis
173- dataKey = "timestamp"
174- tick = { { fontSize : 10 , fill : '#71717a' } }
175- tickLine = { false }
176- axisLine = { { stroke : '#3f3f46' } }
177- interval = "preserveStartEnd"
178- />
179- < YAxis
180- tick = { { fontSize : 10 , fill : '#71717a' } }
181- tickLine = { false }
182- axisLine = { false }
183- allowDecimals = { false }
184- />
185- < Tooltip content = { < CustomTooltip /> } />
186- < Bar dataKey = "completed" stackId = "a" fill = { COLORS . completed } name = "Completed" />
187- < Bar dataKey = "failed" stackId = "a" fill = { COLORS . failed } name = "Failed" />
188- < Bar dataKey = "cancelled" stackId = "a" fill = { COLORS . cancelled } name = "Cancelled" />
189- </ BarChart >
190- ) : (
191- < LineChart data = { chartData } margin = { { top : 5 , right : 5 , left : - 20 , bottom : 5 } } >
192- < XAxis
193- dataKey = "timestamp"
194- tick = { { fontSize : 10 , fill : '#71717a' } }
195- tickLine = { false }
196- axisLine = { { stroke : '#3f3f46' } }
197- interval = "preserveStartEnd"
198- />
199- < YAxis
200- tick = { { fontSize : 10 , fill : '#71717a' } }
201- tickLine = { false }
202- axisLine = { false }
203- allowDecimals = { false }
204- />
205- < Tooltip content = { < CustomTooltip /> } />
206- < Line
207- type = "monotone"
208- dataKey = "completed"
209- stroke = { COLORS . completed }
210- strokeWidth = { 2 }
211- dot = { false }
212- name = "Completed"
213- />
214- < Line
215- type = "monotone"
216- dataKey = "failed"
217- stroke = { COLORS . failed }
218- strokeWidth = { 2 }
219- dot = { false }
220- name = "Failed"
221- />
222- < Line
223- type = "monotone"
224- dataKey = "cancelled"
225- stroke = { COLORS . cancelled }
226- strokeWidth = { 2 }
227- dot = { false }
228- name = "Cancelled"
229- />
230- </ LineChart >
231- ) }
232- </ ResponsiveContainer >
233- ) }
185+ ) : containerWidth > 0 ? (
186+ chartType === 'bar' ? (
187+ < BarChart
188+ width = { containerWidth }
189+ height = { CHART_HEIGHT }
190+ data = { chartData }
191+ margin = { { top : 5 , right : 5 , left : - 20 , bottom : 5 } }
192+ >
193+ < XAxis
194+ dataKey = "timestamp"
195+ tick = { { fontSize : 10 , fill : '#71717a' } }
196+ tickLine = { false }
197+ axisLine = { { stroke : '#3f3f46' } }
198+ interval = "preserveStartEnd"
199+ />
200+ < YAxis
201+ tick = { { fontSize : 10 , fill : '#71717a' } }
202+ tickLine = { false }
203+ axisLine = { false }
204+ allowDecimals = { false }
205+ />
206+ < Tooltip content = { < CustomTooltip /> } />
207+ < Bar dataKey = "completed" stackId = "a" fill = { COLORS . completed } name = "Completed" />
208+ < Bar dataKey = "failed" stackId = "a" fill = { COLORS . failed } name = "Failed" />
209+ < Bar dataKey = "cancelled" stackId = "a" fill = { COLORS . cancelled } name = "Cancelled" />
210+ </ BarChart >
211+ ) : (
212+ < LineChart
213+ width = { containerWidth }
214+ height = { CHART_HEIGHT }
215+ data = { chartData }
216+ margin = { { top : 5 , right : 5 , left : - 20 , bottom : 5 } }
217+ >
218+ < XAxis
219+ dataKey = "timestamp"
220+ tick = { { fontSize : 10 , fill : '#71717a' } }
221+ tickLine = { false }
222+ axisLine = { { stroke : '#3f3f46' } }
223+ interval = "preserveStartEnd"
224+ />
225+ < YAxis
226+ tick = { { fontSize : 10 , fill : '#71717a' } }
227+ tickLine = { false }
228+ axisLine = { false }
229+ allowDecimals = { false }
230+ />
231+ < Tooltip content = { < CustomTooltip /> } />
232+ < Line
233+ type = "monotone"
234+ dataKey = "completed"
235+ stroke = { COLORS . completed }
236+ strokeWidth = { 2 }
237+ dot = { false }
238+ name = "Completed"
239+ />
240+ < Line
241+ type = "monotone"
242+ dataKey = "failed"
243+ stroke = { COLORS . failed }
244+ strokeWidth = { 2 }
245+ dot = { false }
246+ name = "Failed"
247+ />
248+ < Line
249+ type = "monotone"
250+ dataKey = "cancelled"
251+ stroke = { COLORS . cancelled }
252+ strokeWidth = { 2 }
253+ dot = { false }
254+ name = "Cancelled"
255+ />
256+ </ LineChart >
257+ )
258+ ) : null }
234259 </ div >
235260
236261 { /* Legend with totals */ }
0 commit comments