import { useMemo, useState } from "react"; import { Button } from "@/src/components/ui/button"; import { Badge } from "@/src/components/ui/badge"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/src/components/ui/dropdown-menu"; import { MoreHorizontal, Edit, Trash2 } from "lucide-react"; import { api } from "@/src/utils/api"; import { useHasOrganizationAccess } from "@/src/features/rbac/utils/checkOrganizationAccess"; import { formatDistanceToNow } from "date-fns"; import { SpendAlertDialog } from "./SpendAlertDialog"; import { DeleteSpendAlertDialog } from "./DeleteSpendAlertDialog"; import { DataTable } from "@/src/components/table/data-table"; import { DataTableToolbar } from "@/src/components/table/data-table-toolbar"; import { type LangfuseColumnDef } from "@/src/components/table/types"; import { usdFormatter } from "@/src/utils/numbers"; interface SpendAlertsTableProps { orgId: string; } type AlertRow = { id: string; title: string; threshold: number; // USD triggeredAt: Date | null; createdAt: Date; }; export function SpendAlertsTable({ orgId }: SpendAlertsTableProps) { const [editingAlert, setEditingAlert] = useState(null); const [deletingAlert, setDeletingAlert] = useState(null); const hasAccess = useHasOrganizationAccess({ organizationId: orgId, scope: "langfuseCloudBilling:CRUD", }); const { data: spendAlerts, isLoading, isError, refetch, } = api.spendAlerts.getSpendAlerts.useQuery( { orgId }, { enabled: hasAccess }, ); const rows = useMemo(() => { return (spendAlerts ?? []).map((a: any) => ({ id: a.id, title: a.title, threshold: parseFloat(a.threshold?.toString?.() ?? "0"), triggeredAt: a.triggeredAt ? new Date(a.triggeredAt) : null, createdAt: new Date(a.createdAt), })); }, [spendAlerts]); const data = useMemo(() => { if (isLoading) return { isLoading: true, isError: false } as const; if (isError) return { isLoading: false, isError: false, data: [] as AlertRow[], } as const; return { isLoading: false, isError: false, data: rows } as const; }, [isLoading, isError, rows]); const columns: LangfuseColumnDef[] = [ { accessorKey: "title", id: "title", header: "Title", cell: ({ row }) => row.original.title, size: 160, }, { accessorKey: "Limit", id: "limit", header: "Limit (USD)", size: 140, cell: ({ row }) => usdFormatter(row.original.threshold, 2, 2), }, { accessorKey: "status", id: "status", header: "Status", size: 110, cell: ({ row }) => ( {row.original.triggeredAt ? "Triggered" : "Active"} ), }, { accessorKey: "lastTriggered", id: "lastTriggered", header: "Last Triggered", size: 160, cell: ({ row }) => row.original.triggeredAt ? formatDistanceToNow(new Date(row.original.triggeredAt), { addSuffix: true, }) : "Never", }, { accessorKey: "actions", id: "actions", header: "Actions", size: 120, cell: ({ row }) => ( setEditingAlert(row.original.id)}> Edit setDeletingAlert(row.original.id)} className="text-destructive" > Delete ), }, ]; const editingAlertData = spendAlerts?.find((a) => a.id === editingAlert); return ( <> {editingAlert && editingAlertData && ( !open && setEditingAlert(null)} onSuccess={() => { setEditingAlert(null); void refetch(); }} /> )} {deletingAlert && ( !open && setDeletingAlert(null)} onSuccess={() => { setDeletingAlert(null); void refetch(); }} /> )} ); }