@@ -27,7 +27,7 @@ export function AvailabilityChart({
2727 if ( isLoading ) {
2828 return (
2929 < Box
30- height = { height }
30+ height = '100%'
3131 display = 'flex'
3232 alignItems = 'center'
3333 justifyContent = 'center'
@@ -37,8 +37,7 @@ export function AvailabilityChart({
3737 _dark = { { bg : 'gray.800' } }
3838 >
3939 < VStack w = 'full' px = { 6 } gap = { 4 } >
40- < Skeleton height = '24px' width = '40%' />
41- < Skeleton height = { `${ height - 100 } px` } width = '100%' />
40+ < Skeleton height = '100%' width = '100%' />
4241 </ VStack >
4342 </ Box >
4443 ) ;
@@ -55,11 +54,7 @@ export function AvailabilityChart({
5554 minute : '2-digit' ,
5655 } ) ,
5756 uptime : check . status === 'up' ? 100 : 0 ,
58- responseTime : check . rttMs || null ,
59- status : check . status ,
60- error : check . error ,
6157 } ) ) ;
62-
6358 case '15m' :
6459 return data . rollup15m . map ( bucket => ( {
6560 timestamp : new Date ( bucket . bucketTs ) . getTime ( ) ,
@@ -68,10 +63,7 @@ export function AvailabilityChart({
6863 minute : '2-digit' ,
6964 } ) ,
7065 uptime : bucket . upPct ,
71- responseTime : bucket . avgRttMs || null ,
72- downEvents : bucket . downEvents ,
7366 } ) ) ;
74-
7567 case 'daily' :
7668 return data . rollupDaily . map ( bucket => ( {
7769 timestamp : new Date ( bucket . bucketDate ) . getTime ( ) ,
@@ -80,10 +72,7 @@ export function AvailabilityChart({
8072 day : 'numeric' ,
8173 } ) ,
8274 uptime : bucket . upPct ,
83- responseTime : bucket . avgRttMs || null ,
84- downEvents : bucket . downEvents ,
8575 } ) ) ;
86-
8776 default :
8877 return [ ] ;
8978 }
@@ -92,7 +81,7 @@ export function AvailabilityChart({
9281 if ( chartData ?. length === 0 ) {
9382 return (
9483 < Box
95- height = { height }
84+ height = '100%'
9685 display = 'flex'
9786 alignItems = 'center'
9887 justifyContent = 'center'
@@ -112,139 +101,82 @@ export function AvailabilityChart({
112101 ) ;
113102 }
114103
115- // Simple SVG chart implementation
116- const margin = { top : 20 , right : 30 , bottom : 40 , left : 50 } ;
117- const chartWidth = 800 - margin . left - margin . right ;
118- const chartHeight = height - margin . top - margin . bottom ;
119-
120- // Calculate scales
121- const minTime = Math . min ( ...( chartData ?. map ( d => d . timestamp ) ?? [ ] ) ) ;
122- const maxTime = Math . max ( ...( chartData ?. map ( d => d . timestamp ) ?? [ ] ) ) ;
123- const timeRange = maxTime - minTime || 1 ;
124-
125- const maxUptime = Math . max ( ...( chartData ?. map ( d => d . uptime ) ?? [ ] ) ) ;
126- const minUptime = Math . min ( ...( chartData ?. map ( d => d . uptime ) ?? [ ] ) ) ;
127- const uptimeRange = maxUptime - minUptime || 1 ;
128-
129- // Create path for area chart
130- const createPath = ( ) => {
131- let path = '' ;
132- chartData ?. forEach ( ( point , index ) => {
133- const x = ( ( point . timestamp - minTime ) / timeRange ) * chartWidth ;
134- const y = chartHeight - ( ( point . uptime - minUptime ) / uptimeRange ) * chartHeight ;
135-
136- if ( index === 0 ) {
137- path += `M ${ x } ${ y } ` ;
138- } else {
139- path += ` L ${ x } ${ y } ` ;
140- }
141- } ) ;
142- return path ;
143- } ;
144-
145- // Create area path (includes bottom line)
146- const createAreaPath = ( ) => {
147- let path = createPath ( ) ;
148- if ( ( chartData ?. length ?? 0 ) > 0 ) {
149- const lastX =
150- ( ( ( chartData ?. [ chartData . length - 1 ] ?. timestamp ?? minTime ) - minTime ) / timeRange ) *
151- chartWidth ;
152- const firstX = ( ( ( chartData ?. [ 0 ] ?. timestamp ?? minTime ) - minTime ) / timeRange ) * chartWidth ;
153- path += ` L ${ lastX } ${ chartHeight } L ${ firstX } ${ chartHeight } Z` ;
154- }
155- return path ;
156- } ;
104+ const margin = { top : 20 , right : 30 , bottom : 40 , left : 70 } ;
157105
158106 return (
159- < Box height = { '100%' } w = '100% ' position = 'relative' >
160- < svg width = '100%' height = '100%' >
107+ < Box w = 'full' h = 'full ' position = 'relative' >
108+ < svg width = '100%' height = '100%' viewBox = { `0 0 1000 ${ height } ` } preserveAspectRatio = 'none' >
161109 < g transform = { `translate(${ margin . left } , ${ margin . top } )` } >
162- { /* Grid lines */ }
163- < defs >
164- < pattern id = 'grid' width = '40' height = '40' patternUnits = 'userSpaceOnUse' >
165- < path
166- d = 'M 40 0 L 0 0 0 40'
167- fill = 'none'
168- stroke = '#e2e8f0'
169- strokeWidth = '1'
170- opacity = '0.3'
171- />
172- </ pattern >
173- </ defs >
174- < rect width = { chartWidth } height = { chartHeight } fill = 'url(#grid)' />
175-
176- { /* Y-axis labels */ }
177- { [ 0 , 25 , 50 , 75 , 100 ] . map ( value => (
110+ { /* Grid + Y-axis labels */ }
111+ { [ 0 , 10 , 20 , 30 , 40 , 50 , 60 , 70 , 80 , 90 , 100 ] . map ( value => (
178112 < g key = { value } >
179113 < line
180114 x1 = { 0 }
181- y1 = { chartHeight - ( value / 100 ) * chartHeight }
182- x2 = { chartWidth }
183- y2 = { chartHeight - ( value / 100 ) * chartHeight }
115+ y1 = {
116+ height -
117+ margin . top -
118+ margin . bottom -
119+ ( value / 100 ) * ( height - margin . top - margin . bottom )
120+ }
121+ x2 = { 1000 - margin . left - margin . right }
122+ y2 = {
123+ height -
124+ margin . top -
125+ margin . bottom -
126+ ( value / 100 ) * ( height - margin . top - margin . bottom )
127+ }
184128 stroke = '#e2e8f0'
185129 strokeWidth = '1'
186130 opacity = '0.5'
187131 />
188132 < text
189133 x = { - 10 }
190- y = { chartHeight - ( value / 100 ) * chartHeight + 4 }
191- fontSize = '12'
134+ y = {
135+ height -
136+ margin . top -
137+ margin . bottom -
138+ ( value / 100 ) * ( height - margin . top - margin . bottom ) +
139+ 4
140+ }
192141 fill = '#718096'
193142 textAnchor = 'end'
143+ style = { { fontSize : '10px' } }
194144 >
195145 { value } %
196146 </ text >
197147 </ g >
198148 ) ) }
199149
200- { /* X-axis */ }
201- < line
202- x1 = { 0 }
203- y1 = { chartHeight }
204- x2 = { chartWidth }
205- y2 = { chartHeight }
206- stroke = '#718096'
207- strokeWidth = '2'
208- />
209-
210- { /* Y-axis */ }
211- < line x1 = { 0 } y1 = { 0 } x2 = { 0 } y2 = { chartHeight } stroke = '#718096' strokeWidth = '2' />
212-
213- { /* Area chart */ }
214- < path
215- d = { createAreaPath ( ) }
216- fill = '#3182ce'
217- fillOpacity = '0.3'
218- stroke = '#3182ce'
219- strokeWidth = '2'
220- />
221-
222- { /* Data points */ }
150+ { /* Bars */ }
223151 { chartData ?. map ( ( point , index ) => {
224- const x = ( ( point . timestamp - minTime ) / timeRange ) * chartWidth ;
225- const y = chartHeight - ( ( point . uptime - minUptime ) / uptimeRange ) * chartHeight ;
152+ const chartWidth = 1000 - margin . left - margin . right ;
153+ const chartHeight = height - margin . top - margin . bottom ;
154+ const slotWidth = chartWidth / chartData . length ;
155+ const barWidth = slotWidth * 0.6 ;
156+ const x = index * slotWidth + ( slotWidth - barWidth ) / 2 ;
157+ const barHeight = ( point . uptime / 100 ) * chartHeight ;
158+ const y = chartHeight - barHeight ;
226159
227160 return (
228- < circle
161+ < rect
229162 key = { `${ point . timestamp } -${ index } ` }
230- cx = { x }
231- cy = { y }
232- r = '3'
163+ x = { x }
164+ y = { y }
165+ width = { barWidth }
166+ height = { barHeight }
233167 fill = '#3182ce'
234- stroke = 'white'
235- strokeWidth = '2'
236168 />
237169 ) ;
238170 } ) }
239171
240172 { /* Y-axis label */ }
241173 < text
242174 x = { - 35 }
243- y = { chartHeight / 2 }
244- fontSize = '12'
175+ y = { ( height - margin . top - margin . bottom ) / 2.3 }
176+ style = { { fontSize : '12' } }
245177 fill = '#718096'
246178 textAnchor = 'middle'
247- transform = { `rotate(-90, -35, ${ chartHeight / 2 } )` }
179+ transform = { `rotate(-90, -35, ${ ( height - margin . top - margin . bottom ) / 2 } )` }
248180 >
249181 Uptime %
250182 </ text >
0 commit comments