Skip to content

Commit 30da877

Browse files
authored
Merge pull request #48 from tinybirdco/pagerduty01
pagerduty
2 parents cdc2fa2 + 461cdf7 commit 30da877

16 files changed

+1262
-1809
lines changed

apps/web/package-lock.json

Lines changed: 434 additions & 1779 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"use client"
2+
3+
interface BarListProps {
4+
data: Array<{
5+
name: string
6+
value: number
7+
extra?: string
8+
}>
9+
}
10+
11+
export default function BarList({ data }: BarListProps) {
12+
const max = Math.max(...data.map((item) => item.value))
13+
14+
return (
15+
<div className="space-y-2">
16+
{data.map((item) => (
17+
<div key={item.name} className="flex items-center">
18+
<div className="flex-1 space-y-1">
19+
<p className="text-sm font-medium leading-none">{item.name}</p>
20+
{item.extra && (
21+
<p className="text-sm text-muted-foreground">{item.extra}</p>
22+
)}
23+
</div>
24+
<div className="ml-4 w-24 text-sm text-right">{item.value}</div>
25+
<div className="ml-2 w-48 h-2 rounded-full bg-muted overflow-hidden">
26+
<div
27+
className="h-full bg-primary"
28+
style={{ width: `${(item.value / max) * 100}%` }}
29+
/>
30+
</div>
31+
</div>
32+
))}
33+
</div>
34+
)
35+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
2+
import { Card, CardContent, CardHeader, CardTitle, CardFooter } from "@/components/ui/card"
3+
import { formatPercentage } from "@/lib/utils"
4+
import { useState } from "react"
5+
import { ArrowUpDown, ChevronLeft, ChevronRight } from "lucide-react"
6+
import { Button } from "@/components/ui/button"
7+
8+
interface IncidentType {
9+
title: string
10+
cloud: string
11+
cluster: string
12+
total_incidents: number
13+
high_urgency_incidents: number
14+
example_title: string
15+
}
16+
17+
interface IncidentCategoriesTableProps {
18+
data: IncidentType[]
19+
page: number
20+
onPageChange: (page: number) => void
21+
pageSize: number
22+
isLoading: boolean
23+
}
24+
25+
export default function IncidentCategoriesTable({ data, onPageChange, page, pageSize, isLoading }: IncidentCategoriesTableProps) {
26+
const [sortConfig, setSortConfig] = useState<{
27+
key: keyof IncidentType
28+
direction: 'asc' | 'desc'
29+
}>({ key: 'total_incidents', direction: 'desc' })
30+
31+
const sortedData = [...data].sort((a, b) => {
32+
if (sortConfig.direction === 'asc') {
33+
return a[sortConfig.key] > b[sortConfig.key] ? 1 : -1
34+
}
35+
return a[sortConfig.key] < b[sortConfig.key] ? 1 : -1
36+
})
37+
38+
const requestSort = (key: keyof IncidentType) => {
39+
setSortConfig({
40+
key,
41+
direction: sortConfig.key === key && sortConfig.direction === 'asc' ? 'desc' : 'asc',
42+
})
43+
}
44+
45+
return (
46+
<Card>
47+
<CardHeader>
48+
<CardTitle>Incident Categories</CardTitle>
49+
</CardHeader>
50+
<CardContent>
51+
<Table>
52+
<TableHeader>
53+
<TableRow>
54+
<TableHead className="max-w-[300px]">
55+
<Button variant="ghost" onClick={() => requestSort('title')}>
56+
Title <ArrowUpDown className="ml-2 h-4 w-4" />
57+
</Button>
58+
</TableHead>
59+
<TableHead className="max-w-[100px]">
60+
<Button variant="ghost" onClick={() => requestSort('cloud')}>
61+
Cloud <ArrowUpDown className="ml-2 h-4 w-4" />
62+
</Button>
63+
</TableHead>
64+
<TableHead className="max-w-[150px]">
65+
<Button variant="ghost" onClick={() => requestSort('cluster')}>
66+
Cluster <ArrowUpDown className="ml-2 h-4 w-4" />
67+
</Button>
68+
</TableHead>
69+
<TableHead className="text-right">
70+
<Button variant="ghost" onClick={() => requestSort('total_incidents')}>
71+
Total <ArrowUpDown className="ml-2 h-4 w-4" />
72+
</Button>
73+
</TableHead>
74+
<TableHead className="text-right">
75+
<Button variant="ghost" onClick={() => requestSort('high_urgency_incidents')}>
76+
High Urgency <ArrowUpDown className="ml-2 h-4 w-4" />
77+
</Button>
78+
</TableHead>
79+
</TableRow>
80+
</TableHeader>
81+
<TableBody>
82+
{isLoading ? (
83+
<TableRow>
84+
<TableCell colSpan={5} className="text-center">
85+
Loading...
86+
</TableCell>
87+
</TableRow>
88+
) : (
89+
sortedData.map((item) => (
90+
<TableRow key={`${item.title}-${item.cloud}-${item.cluster}`}>
91+
<TableCell className="truncate max-w-[300px]">{item.title}</TableCell>
92+
<TableCell className="truncate max-w-[100px]">{item.cloud}</TableCell>
93+
<TableCell className="truncate max-w-[150px]">{item.cluster}</TableCell>
94+
<TableCell className="text-right">{item.total_incidents}</TableCell>
95+
<TableCell className="text-right">{parseInt(formatPercentage(item.high_urgency_incidents / item.total_incidents * 100))}%</TableCell>
96+
</TableRow>
97+
))
98+
)}
99+
</TableBody>
100+
</Table>
101+
</CardContent>
102+
<CardFooter className="flex items-center justify-end space-x-2">
103+
<Button
104+
variant="outline"
105+
size="sm"
106+
onClick={() => onPageChange(Math.max(0, page - 1))}
107+
disabled={page === 0}
108+
>
109+
<ChevronLeft className="h-4 w-4" />
110+
Previous
111+
</Button>
112+
<div className="flex items-center gap-1">
113+
<span className="text-sm text-muted-foreground">
114+
Page {page + 1}
115+
</span>
116+
</div>
117+
<Button
118+
variant="outline"
119+
size="sm"
120+
onClick={() => onPageChange(page + 1)}
121+
disabled={data.length < pageSize}
122+
>
123+
Next
124+
<ChevronRight className="h-4 w-4" />
125+
</Button>
126+
</CardFooter>
127+
</Card>
128+
)
129+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"use client"
2+
3+
import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, Legend } from "recharts"
4+
5+
interface OverviewProps {
6+
data: Array<{
7+
hour: string;
8+
triggered: number;
9+
resolved: number;
10+
escalated: number;
11+
}>;
12+
categories: string[];
13+
colors: Record<string, string>;
14+
index: string;
15+
valueKey?: string;
16+
categoryKey?: string;
17+
}
18+
19+
export default function Overview({ data, categories, colors }: OverviewProps) {
20+
return (
21+
<ResponsiveContainer width="100%" height={350}>
22+
<BarChart data={data}>
23+
<XAxis
24+
dataKey="hour"
25+
stroke="var(--muted-foreground)"
26+
fontSize={12}
27+
tickLine={false}
28+
axisLine={false}
29+
/>
30+
<YAxis
31+
stroke="var(--muted-foreground)"
32+
fontSize={12}
33+
tickLine={false}
34+
axisLine={false}
35+
/>
36+
<Tooltip />
37+
<Legend />
38+
{categories.map((category) => (
39+
<Bar
40+
key={category}
41+
dataKey={category}
42+
name={category}
43+
fill={colors[category]}
44+
stackId="stack"
45+
/>
46+
))}
47+
</BarChart>
48+
</ResponsiveContainer>
49+
)
50+
}

0 commit comments

Comments
 (0)