import React, { useState } from "react"; import { useRowHeightLocalStorage } from "@/src/components/table/data-table-row-height-switch"; import useColumnVisibility from "@/src/features/column-visibility/hooks/useColumnVisibility"; import { api } from "@/src/utils/api"; import { type LangfuseColumnDef } from "@/src/components/table/types"; import { DataTableToolbar } from "@/src/components/table/data-table-toolbar"; import { DataTable } from "@/src/components/table/data-table"; import { type ScoreConfigDataType, type Prisma, type ScoreConfigCategoryDomain, } from "@langfuse/shared"; import { IOTableCell } from "../../ui/IOTableCell"; import { NumberParam, useQueryParams, withDefault } from "use-query-params"; import { isBooleanDataType, isCategoricalDataType, isNumericDataType, } from "@/src/features/scores/lib/helpers"; import { Edit, MoreVertical } from "lucide-react"; import { Button } from "@/src/components/ui/button"; import useColumnOrder from "@/src/features/column-visibility/hooks/useColumnOrder"; import { SettingsTableCard } from "@/src/components/layouts/settings-table-card"; import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess"; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, } from "@/src/components/ui/dropdown-menu"; import { ArchiveScoreConfigButton } from "@/src/features/score-configs/components/ArchiveScoreConfigButton"; import { UpsertScoreConfigDialog } from "@/src/features/score-configs/components/UpsertScoreConfigDialog"; type ScoreConfigTableRow = { id: string; name: string; dataType: ScoreConfigDataType; createdAt: string; updatedAt: string; range: { maxValue?: number | null; minValue?: number | null; categories?: ScoreConfigCategoryDomain[] | null; }; description?: string | null; isArchived: boolean; }; function getConfigRange( originalRow: ScoreConfigTableRow, ): undefined | Prisma.JsonValue { const { range, dataType } = originalRow; if (isNumericDataType(dataType)) { return { Minimum: range.minValue ?? "-∞", Maximum: range.maxValue ?? "∞", }; } if (isCategoricalDataType(dataType) || isBooleanDataType(dataType)) { const configCategories = range.categories ?? []; return configCategories.reduce( (acc, category) => { acc[category.value] = category.label; return acc; }, {} as Record, ); } } export function ScoreConfigsTable({ projectId }: { projectId: string }) { const [editConfigId, setEditConfigId] = useState(null); const [createConfigOpen, setCreateConfigOpen] = useState(false); const [paginationState, setPaginationState] = useQueryParams({ pageIndex: withDefault(NumberParam, 0), pageSize: withDefault(NumberParam, 50), }); const hasAccess = useHasProjectAccess({ projectId, scope: "scoreConfigs:CUD", }); const [rowHeight, setRowHeight] = useRowHeightLocalStorage( "scoreConfigs", "s", ); const configs = api.scoreConfigs.all.useQuery( { projectId, page: paginationState.pageIndex, limit: paginationState.pageSize, }, { enabled: hasAccess }, ); const configQuery = api.scoreConfigs.byId.useQuery( { projectId, id: editConfigId as string }, { enabled: !!editConfigId && hasAccess }, ); const totalCount = configs.data?.totalCount ?? null; const columns: LangfuseColumnDef[] = [ { accessorKey: "name", id: "name", header: "Name", enableHiding: true, }, { accessorKey: "dataType", id: "dataType", header: "Data Type", size: 80, enableHiding: true, }, { accessorKey: "range", id: "range", header: "Range", enableHiding: true, size: 300, cell: ({ row }) => { const range = getConfigRange(row.original); return !!range ? ( ) : null; }, }, { accessorKey: "description", id: "description", header: "Description", enableHiding: true, cell: ({ row }) => { const value = row.original.description; return !!value ? ( ) : null; }, }, { accessorKey: "id", id: "id", header: "Config ID", enableHiding: true, defaultHidden: true, }, { accessorKey: "createdAt", id: "createdAt", header: "Created At", enableHiding: true, defaultHidden: true, }, { accessorKey: "isArchived", id: "isArchived", header: "Status", size: 80, enableHiding: true, cell: ({ row }) => { const { isArchived } = row.original; return isArchived ? "Archived" : "Active"; }, }, { accessorKey: "action", header: "Action", size: 70, isFixedPosition: true, enableHiding: true, cell: ({ row }) => { const { id: configId, isArchived, name } = row.original; return ( setEditConfigId(configId)} > Edit ); }, }, ]; const [columnVisibility, setColumnVisibility] = useColumnVisibility( "scoreConfigsColumnVisibility", columns, ); const [columnOrder, setColumnOrder] = useColumnOrder( "scoreConfigsColumnOrder", columns, ); return ( <> } className="px-0" /> ({ id: config.id, name: config.name, dataType: config.dataType, description: config.description, createdAt: config.createdAt.toLocaleString(), updatedAt: config.updatedAt.toLocaleString(), range: { maxValue: config.maxValue, minValue: config.minValue, categories: config.categories, }, isArchived: config.isArchived, })), } } pagination={{ totalCount, onChange: setPaginationState, state: paginationState, }} columnVisibility={columnVisibility} onColumnVisibilityChange={setColumnVisibility} columnOrder={columnOrder} onColumnOrderChange={setColumnOrder} rowHeight={rowHeight} className="gap-2" /> {!!editConfigId && configQuery.isSuccess && ( { if (!open) setEditConfigId(null); }} defaultValues={ configQuery.data ? { id: editConfigId, name: configQuery.data.name, dataType: configQuery.data.dataType, minValue: configQuery.data.minValue ?? undefined, maxValue: configQuery.data.maxValue ?? undefined, description: configQuery.data.description ?? undefined, categories: configQuery.data.categories?.length ? configQuery.data.categories : undefined, } : undefined } /> )} ); }