@@ -5,13 +5,16 @@ import { useState, useEffect, useCallback } from 'react'
55import { pipe } from '@/lib/tinybird'
66import { Card , CardContent , CardHeader , CardTitle } from '@/components/ui/card'
77import { formatDuration , formatNumber , formatPercentage } from '@/lib/utils'
8- import Overview from '@/components/overview-chart'
8+ import Overview from '@/components/tools/pagerduty/ overview-chart'
99import BarList from '@/components/bar-list'
1010import { DateRangePicker , DateRange } from '@/components/ui/date-range-picker'
1111import { startOfDay , endOfDay , format } from 'date-fns'
1212import MetricCard from '@/components/metric-card'
13- import IncidentCategoriesTable from "@/components/incident-categories-table"
13+ import IncidentCategoriesTable from "@/components/tools/pagerduty/ incident-categories-table"
1414import { Filter } from './filters'
15+ import InterruptionsChart from "@/components/tools/pagerduty/interruptions-chart"
16+ import ResponseEffortChart from "@/components/tools/pagerduty/response-effort-chart"
17+ import SleepInterruptions from "@/components/tools/pagerduty/sleep-interruptions"
1518
1619interface IncidentType {
1720 title : string
@@ -22,6 +25,20 @@ interface IncidentType {
2225 example_title : string
2326}
2427
28+ interface Responder {
29+ html_url : string ;
30+ id : string ;
31+ self : string ;
32+ summary : string ;
33+ type : string ;
34+ }
35+
36+ interface InterruptionData {
37+ day : string
38+ interruptions : number
39+ day_type : 'business' | 'off'
40+ }
41+
2542export default function PagerDutyDashboard ( ) {
2643 const [ token ] = useQueryState ( 'token' )
2744 const [ dateRange , setDateRange ] = useState < DateRange > ( {
@@ -52,9 +69,10 @@ export default function PagerDutyDashboard() {
5269 high_urgency_rate : number
5370 } > > ( [ ] )
5471 const [ responders , setResponders ] = useState < Array < {
55- responder : string
56- responses : number
57- avg_response_time : number
72+ responder : Responder [ ] ;
73+ responses : number ;
74+ acknowledgements : number ;
75+ avg_response_time : number ;
5876 } > > ( [ ] )
5977 const [ statusDistribution , setStatusDistribution ] = useState < Array < {
6078 event_type : string
@@ -65,6 +83,28 @@ export default function PagerDutyDashboard() {
6583 const pageSize = 10
6684 const [ incidentTypes , setIncidentTypes ] = useState < IncidentType [ ] > ( [ ] )
6785 const [ selectedService , setSelectedService ] = useState < string > ( )
86+ const [ interruptions , setInterruptions ] = useState < InterruptionData [ ] > ( [ ] )
87+ const [ responseEffort , setResponseEffort ] = useState < Array < {
88+ responder : Array < {
89+ html_url : string
90+ id : string
91+ self : string
92+ summary : string
93+ type : string
94+ } >
95+ hours : number
96+ } > > ( [ ] )
97+ const [ sleepInterruptions , setSleepInterruptions ] = useState < Array < {
98+ responder : Array < {
99+ html_url : string
100+ id : string
101+ self : string
102+ summary : string
103+ type : string
104+ } >
105+ interruptions : number
106+ avg_response_time : number
107+ } > > ( [ ] )
68108
69109 const fetchIncidentTypes = useCallback ( async ( newPage : number ) => {
70110 if ( ! token ) return
@@ -116,15 +156,21 @@ export default function PagerDutyDashboard() {
116156 statusDistributionData ,
117157 resolutionTimesData ,
118158 serviceDistributionData ,
119- incidentTypesData
159+ incidentTypesData ,
160+ interruptionsData ,
161+ responseEffortData ,
162+ sleepInterruptionsData
120163 ] = await Promise . all ( [
121164 pipe ( token , 'pagerduty_incident_metrics' , baseParams ) ,
122165 pipe ( token , 'pagerduty_incidents_over_time' , baseParams ) ,
123166 pipe ( token , 'pagerduty_responders' , baseParams ) ,
124167 pipe ( token , 'pagerduty_status_distribution' , baseParams ) ,
125168 pipe ( token , 'pagerduty_resolution_times' , baseParams ) ,
126169 pipe ( token , 'pagerduty_service_distribution' , baseParams ) ,
127- pipe ( token , 'pagerduty_incident_types' , baseParams )
170+ pipe ( token , 'pagerduty_incident_types' , baseParams ) ,
171+ pipe ( token , 'pagerduty_interruptions' , baseParams ) ,
172+ pipe ( token , 'pagerduty_response_effort' , baseParams ) ,
173+ pipe ( token , 'pagerduty_sleep_interruptions' , baseParams )
128174 ] )
129175
130176 setMetrics ( ( incidentMetrics ?. data ?. [ 0 ] || {
@@ -143,9 +189,10 @@ export default function PagerDutyDashboard() {
143189 escalated : number
144190 } > )
145191 setResponders ( respondersData . data as Array < {
146- responder : string
147- responses : number
148- avg_response_time : number
192+ responder : Responder [ ] ;
193+ responses : number ;
194+ acknowledgements : number ;
195+ avg_response_time : number ;
149196 } > )
150197 setStatusDistribution ( statusDistributionData . data as Array < {
151198 event_type : string
@@ -166,6 +213,28 @@ export default function PagerDutyDashboard() {
166213 high_urgency_rate : number
167214 } > )
168215 setIncidentTypes ( incidentTypesData . data as IncidentType [ ] )
216+ setInterruptions ( interruptionsData . data as InterruptionData [ ] )
217+ setResponseEffort ( responseEffortData . data as Array < {
218+ responder : Array < {
219+ html_url : string
220+ id : string
221+ self : string
222+ summary : string
223+ type : string
224+ } >
225+ hours : number
226+ } > )
227+ setSleepInterruptions ( sleepInterruptionsData . data as Array < {
228+ responder : Array < {
229+ html_url : string
230+ id : string
231+ self : string
232+ summary : string
233+ type : string
234+ } >
235+ interruptions : number
236+ avg_response_time : number
237+ } > )
169238
170239 } catch ( error ) {
171240 console . error ( 'Failed to fetch metrics:' , error )
@@ -270,9 +339,9 @@ export default function PagerDutyDashboard() {
270339 data = { incidentsOverTime }
271340 categories = { [ 'triggered' , 'resolved' , 'escalated' ] }
272341 colors = { {
273- 'triggered' : '#dc2626 ' , // Red-600
274- 'resolved' : '#16a34a ' , // Green-600
275- 'escalated' : '#d97706 ' // Amber-600
342+ 'triggered' : 'hsl(var(--primary)) ' , // Red-600
343+ 'resolved' : '#93c5fd ' , // Green-600
344+ 'escalated' : '#6b7280 ' // Amber-600
276345 } }
277346 index = "hour"
278347 valueKey = "count"
@@ -282,31 +351,65 @@ export default function PagerDutyDashboard() {
282351 </ Card >
283352
284353 { /* Response Analysis */ }
285- < div className = "grid gap-4 grid-cols-3 " >
354+ < div className = "grid gap-4 grid-cols-2 " >
286355 < Card >
287356 < CardHeader >
288- < CardTitle > Response Team Activity</ CardTitle >
357+ < CardTitle > Oncall Team Activity</ CardTitle >
289358 </ CardHeader >
290359 < CardContent className = "max-h-[400px] overflow-auto" >
291360 < BarList
292- data = { ( responders || [ ] ) . map ( ( item ) => ( {
293- name : item . responder ,
294- value : item . responses ,
295- extra : `${ formatDuration ( item . avg_response_time ) } avg response time` ,
296- } ) ) }
361+ data = { responders
362+ . reduce ( ( acc , item ) => {
363+ if ( item . responder . length > 1 ) return acc ;
364+
365+ const name = item . responder [ 0 ] ?. summary || 'Unknown'
366+ const existing = acc . find ( x => x . name === name )
367+ if ( existing ) {
368+ existing . value += item . responses
369+ existing . acknowledgements += item . acknowledgements
370+ existing . avgTime = ( existing . avgTime * existing . count + item . avg_response_time ) / ( existing . count + 1 )
371+ existing . count ++
372+ } else {
373+ acc . push ( {
374+ name,
375+ value : item . responses ,
376+ acknowledgements : item . acknowledgements ,
377+ avgTime : item . avg_response_time ,
378+ count : 1
379+ } )
380+ }
381+ return acc
382+ } , [ ] as Array < {
383+ name : string
384+ value : number
385+ acknowledgements : number
386+ avgTime : number
387+ count : number
388+ } > )
389+ . map ( item => ( {
390+ name : item . name ,
391+ value : item . value ,
392+ extra : `${ item . acknowledgements } acks • ${ formatDuration ( item . avgTime ) } avg response time`
393+ } ) )
394+ }
297395 />
298396 </ CardContent >
299397 </ Card >
300- < div className = "col-span-2" >
301- < IncidentCategoriesTable
302- data = { incidentTypes || [ ] }
303- page = { page }
304- onPageChange = { handlePageChange }
305- pageSize = { pageSize }
306- isLoading = { isTableLoading }
307- />
308- </ div >
398+ < ResponseEffortChart data = { responseEffort } />
309399 </ div >
400+
401+ < div className = "grid gap-4 grid-cols-2" >
402+ < InterruptionsChart data = { interruptions } />
403+ < SleepInterruptions data = { sleepInterruptions } />
404+ </ div >
405+
406+ < IncidentCategoriesTable
407+ data = { incidentTypes || [ ] }
408+ page = { page }
409+ onPageChange = { handlePageChange }
410+ pageSize = { pageSize }
411+ isLoading = { isTableLoading }
412+ />
310413 </ div >
311414 )
312415}
0 commit comments