import { useSuspenseQuery } from "@tanstack/react-query"; import { flexRender, getCoreRowModel, getFilteredRowModel, getSortedRowModel, type ColumnDef, type ColumnFiltersState, type SortingState, useReactTable, } from "@tanstack/react-table"; import { Bell, Plus, RotateCcw } from "lucide-react"; import { useState } from "react"; import { listNotificationDestinationsOptions } from "~/client/api-client/@tanstack/react-query.gen"; import { DataTableSortHeader } from "~/client/components/data-table-sort-header"; import { EmptyState } from "~/client/components/empty-state"; import { StatusDot } from "~/client/components/status-dot"; import { Button } from "~/client/components/ui/button"; import { Card } from "~/client/components/ui/card"; import { Input } from "~/client/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/client/components/ui/table"; import { useNavigate } from "@tanstack/react-router"; import { cn } from "~/client/lib/utils"; type NotificationRow = { id: number; name: string; type: "email" | "slack" | "discord" | "gotify" | "ntfy" | "pushover" | "telegram" | "custom" | "generic"; enabled: boolean; status: "healthy" | "error" | "unknown"; }; const getNotificationStatus = (row: NotificationRow) => (row.enabled ? row.status : "disabled"); const getNotificationStatusVariant = (row: NotificationRow) => { if (!row.enabled) return "neutral"; if (row.status === "healthy") return "success"; if (row.status === "error") return "error"; return "warning"; }; const notificationColumns: ColumnDef[] = [ { accessorKey: "name", header: ({ column }) => , cell: ({ row }) => row.original.name, }, { accessorKey: "type", header: ({ column }) => , cell: ({ row }) => row.original.type, filterFn: (row, id, value) => row.getValue(id) === value, }, { accessorFn: getNotificationStatus, id: "status", header: ({ column }) => ( ), cell: ({ row }) => ( ), filterFn: (row, id, value) => row.getValue(id) === value, }, ]; export function NotificationsPage() { const [columnFilters, setColumnFilters] = useState([]); const [sorting, setSorting] = useState([]); const navigate = useNavigate(); const { data } = useSuspenseQuery({ ...listNotificationDestinationsOptions(), }); const table = useReactTable({ data, columns: notificationColumns, state: { columnFilters, sorting }, onColumnFiltersChange: setColumnFilters, onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), getFilteredRowModel: getFilteredRowModel(), getSortedRowModel: getSortedRowModel(), }); const rows = table.getRowModel().rows; const hasFilters = columnFilters.length > 0; const clearFilters = () => table.resetColumnFilters(); const hasNoNotifications = data.length === 0; const hasNoFilteredNotifications = rows.length === 0 && !hasNoNotifications; if (hasNoNotifications) { return ( navigate({ to: "/notifications/create" })}> Create Destination } /> ); } const search = (table.getColumn("name")?.getFilterValue() as string) ?? ""; const type = (table.getColumn("type")?.getFilterValue() as string) ?? ""; const status = (table.getColumn("status")?.getFilterValue() as string) ?? ""; return (
table.getColumn("name")?.setFilterValue(e.target.value)} /> {hasFilters && ( )}
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} ))} ))}

No destinations match your filters.

{rows.map((row) => ( navigate({ to: `/notifications/${row.original.id}` })} > {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} ))}
{hasNoFilteredNotifications ? ( "No destinations match filters." ) : ( {rows.length} destination {rows.length !== 1 ? "s" : ""} )}
); }