Alert history updates

This commit is contained in:
henrygd
2025-07-21 20:07:52 -04:00
parent 9d7fb8ab80
commit 18d9258907
29 changed files with 1301 additions and 643 deletions

View File

@@ -1,146 +1,164 @@
import { ColumnDef } from "@tanstack/react-table"
import { AlertsHistoryRecord } from "@/types"
import { Button } from "@/components/ui/button"
import { ArrowUpDown } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { alertInfo, formatShortDate, toFixedFloat, formatDuration, cn } from "@/lib/utils"
import { Trans } from "@lingui/react/macro"
import { t } from "@lingui/core/macro"
export const alertsHistoryColumns: ColumnDef<AlertsHistoryRecord>[] = [
{
accessorKey: "system",
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
System <ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => <span className="text-center block">{row.original.expand?.system?.name || row.original.system}</span>,
enableSorting: true,
filterFn: (row, _, filterValue) => {
const display = row.original.expand?.system?.name || row.original.system || ""
return display.toLowerCase().includes(filterValue.toLowerCase())
},
},
{
accessorKey: "name",
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Name <ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => <span className="text-center block">{row.getValue("name")}</span>,
enableSorting: true,
filterFn: (row, _, filterValue) => {
const value = row.getValue("name") || ""
return String(value).toLowerCase().includes(filterValue.toLowerCase())
},
},
{
accessorKey: "value",
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="text-right w-full justify-end"
>
Value <ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => <span className="text-center block">{Math.round(Number(row.getValue("value")))}</span>,
enableSorting: true,
},
{
accessorKey: "state",
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
className="text-center w-full justify-start"
>
State <ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => {
const state = row.getValue("state") as string
let color = ""
if (state === "solved") color = "bg-green-100 text-green-800 border-green-200"
else if (state === "active") color = "bg-yellow-100 text-yellow-800 border-yellow-200"
return (
<span className="text-center block">
<Badge className={`capitalize ${color}`}>{state}</Badge>
</span>
)
},
enableSorting: true,
},
{
accessorKey: "create_date",
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Created <ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<span className="text-center block">
{row.original.created_date ? new Date(row.original.created_date).toLocaleString() : ""}
</span>
),
enableSorting: true,
},
{
accessorKey: "solved_date",
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Solved <ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => (
<span className="text-center block">
{row.original.solved_date ? new Date(row.original.solved_date).toLocaleString() : ""}
</span>
),
enableSorting: true,
},
{
accessorKey: "duration",
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
>
Duration <ArrowUpDown className="ml-2 h-4 w-4" />
</Button>
),
cell: ({ row }) => {
const created = row.original.created_date ? new Date(row.original.created_date) : null
const solved = row.original.solved_date ? new Date(row.original.solved_date) : null
if (!created || !solved) return <span className="text-center block"></span>
const diffMs = solved.getTime() - created.getTime()
if (diffMs < 0) return <span className="text-center block"></span>
const totalSeconds = Math.floor(diffMs / 1000)
const hours = Math.floor(totalSeconds / 3600)
const minutes = Math.floor((totalSeconds % 3600) / 60)
const seconds = totalSeconds % 60
return (
<span className="text-center block">
{[
hours ? `${hours}h` : null,
minutes ? `${minutes}m` : null,
`${seconds}s`
].filter(Boolean).join(" ")}
</span>
)
},
enableSorting: true,
},
]
{
accessorKey: "system",
enableSorting: true,
header: ({ column }) => (
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
<Trans>System</Trans>
</Button>
),
cell: ({ row }) => <span className="ps-2">{row.original.expand?.system?.name || row.original.system}</span>,
filterFn: (row, _, filterValue) => {
const display = row.original.expand?.system?.name || row.original.system || ""
return display.toLowerCase().includes(filterValue.toLowerCase())
},
},
{
// accessorKey: "name",
id: "name",
accessorFn: (record) => {
const name = record.name
const info = alertInfo[name]
return info?.name().replace("cpu", "CPU") || name
},
header: ({ column }) => (
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
<Trans>Name</Trans>
</Button>
),
cell: ({ getValue, row }) => {
let name = getValue() as string
const info = alertInfo[row.original.name]
const Icon = info?.icon
return (
<span className="flex items-center gap-2 ps-1 min-w-40">
{Icon && <Icon className="size-3.5" />}
{name}
</span>
)
},
},
{
accessorKey: "value",
enableSorting: false,
header: () => (
<Button variant="ghost">
<Trans>Value</Trans>
</Button>
),
cell({ row, getValue }) {
const name = row.original.name
if (name === "Status") {
return <span className="ps-2">{t`Down`}</span>
}
const value = getValue() as number
const unit = alertInfo[name]?.unit
return (
<span className="tabular-nums ps-2.5">
{toFixedFloat(value, value < 10 ? 2 : 1)}
{unit}
</span>
)
},
},
{
accessorKey: "state",
enableSorting: true,
sortingFn: (rowA, rowB) => (rowA.original.resolved ? 1 : 0) - (rowB.original.resolved ? 1 : 0),
header: ({ column }) => (
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
<Trans>State</Trans>
</Button>
),
cell: ({ row }) => {
const resolved = row.original.resolved
return (
<Badge
className={cn(
"capitalize pointer-events-none",
resolved
? "bg-green-100 text-green-800 border-green-200 dark:opacity-80"
: "bg-yellow-100 text-yellow-800 border-yellow-200"
)}
>
{/* {resolved ? <CircleCheckIcon className="size-3 me-0.5" /> : <CircleAlertIcon className="size-3 me-0.5" />} */}
<Trans>{resolved ? "Resolved" : "Active"}</Trans>
</Badge>
)
},
},
{
accessorKey: "created",
accessorFn: (record) => formatShortDate(record.created),
enableSorting: true,
invertSorting: true,
header: ({ column }) => (
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
<Trans>Created</Trans>
</Button>
),
cell: ({ getValue, row }) => (
<span className="ps-1 tabular-nums tracking-tight" title={`${row.original.created} UTC`}>
{getValue() as string}
</span>
),
},
{
accessorKey: "resolved",
enableSorting: true,
invertSorting: true,
header: ({ column }) => (
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
<Trans>Resolved</Trans>
</Button>
),
cell: ({ row, getValue }) => {
const resolved = getValue() as string | null
if (!resolved) {
return null
}
return (
<span className="ps-1 tabular-nums tracking-tight" title={`${row.original.resolved} UTC`}>
{formatShortDate(resolved)}
</span>
)
},
},
{
accessorKey: "duration",
invertSorting: true,
enableSorting: true,
sortingFn: (rowA, rowB) => {
const aCreated = new Date(rowA.original.created)
const bCreated = new Date(rowB.original.created)
const aResolved = rowA.original.resolved ? new Date(rowA.original.resolved) : null
const bResolved = rowB.original.resolved ? new Date(rowB.original.resolved) : null
const aDuration = aResolved ? aResolved.getTime() - aCreated.getTime() : null
const bDuration = bResolved ? bResolved.getTime() - bCreated.getTime() : null
if (!aDuration && bDuration) return -1
if (aDuration && !bDuration) return 1
return (aDuration || 0) - (bDuration || 0)
},
header: ({ column }) => (
<Button variant="ghost" onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}>
<Trans>Duration</Trans>
</Button>
),
cell: ({ row }) => {
const duration = formatDuration(row.original.created, row.original.resolved)
if (!duration) {
return null
}
return <span className="ps-2">{duration}</span>
},
},
]