Allow user to select logs

This commit is contained in:
David Corbitt
2023-08-12 04:07:58 -07:00
parent 89815e1f7f
commit 33751c12d2
5 changed files with 71 additions and 20 deletions

View File

@@ -10,7 +10,7 @@ export default function LoggedCallsTable() {
return (
<Card width="100%" overflow="hidden">
<Table>
<TableHeader />
<TableHeader showCheckbox />
<Tbody>
{loggedCalls?.calls.map((loggedCall) => {
return (
@@ -25,6 +25,7 @@ export default function LoggedCallsTable() {
setExpandedRow(loggedCall.id);
}
}}
showCheckbox
/>
);
})}

View File

@@ -12,6 +12,7 @@ import {
Button,
ButtonGroup,
Text,
Checkbox,
} from "@chakra-ui/react";
import dayjs from "dayjs";
import relativeTime from "dayjs/plugin/relativeTime";
@@ -19,32 +20,60 @@ import Link from "next/link";
import { type RouterOutputs } from "~/utils/api";
import { FormattedJson } from "./FormattedJson";
import { useAppStore } from "~/state/store";
import { useLoggedCalls } from "~/utils/hooks";
import { useMemo } from "react";
dayjs.extend(relativeTime);
type LoggedCall = RouterOutputs["loggedCalls"]["list"]["calls"][0];
export const TableHeader = () => (
<Thead>
<Tr>
<Th>Time</Th>
<Th>Model</Th>
<Th isNumeric>Duration</Th>
<Th isNumeric>Input tokens</Th>
<Th isNumeric>Output tokens</Th>
<Th isNumeric>Status</Th>
</Tr>
</Thead>
);
export const TableHeader = ({ showCheckbox }: { showCheckbox?: boolean }) => {
const matchingLogIds = useLoggedCalls().data?.matchingLogIds;
const selectedLogIds = useAppStore((s) => s.selectedLogs.selectedLogIds);
const addAll = useAppStore((s) => s.selectedLogs.addSelectedLogIds);
const clearAll = useAppStore((s) => s.selectedLogs.clearSelectedLogIds);
const allSelected = useMemo(() => {
if (!matchingLogIds) return false;
return matchingLogIds.every((id) => selectedLogIds.has(id));
}, [selectedLogIds, matchingLogIds]);
return (
<Thead>
<Tr>
{showCheckbox && (
<Th>
<HStack w={8}>
<Checkbox
isChecked={allSelected}
onChange={() => {
allSelected ? clearAll() : addAll(matchingLogIds || []);
}}
/>
<Text>({selectedLogIds.size})</Text>
</HStack>
</Th>
)}
<Th>Time</Th>
<Th>Model</Th>
<Th isNumeric>Duration</Th>
<Th isNumeric>Input tokens</Th>
<Th isNumeric>Output tokens</Th>
<Th isNumeric>Status</Th>
</Tr>
</Thead>
);
};
export const TableRow = ({
loggedCall,
isExpanded,
onToggle,
showCheckbox,
}: {
loggedCall: LoggedCall;
isExpanded: boolean;
onToggle: () => void;
showCheckbox?: boolean;
}) => {
const isError = loggedCall.modelResponse?.statusCode !== 200;
const timeAgo = dayjs(loggedCall.requestedAt).fromNow();
@@ -60,16 +89,24 @@ export const TableRow = ({
</Td>
);
const isChecked = useAppStore((s) => s.selectedLogs.selectedLogIds.has(loggedCall.id));
const toggleChecked = useAppStore((s) => s.selectedLogs.toggleSelectedLogId);
return (
<>
<Tr
onClick={onToggle}
key={loggedCall.id}
_hover={{ bgColor: "gray.100", cursor: "pointer" }}
_hover={{ bgColor: "gray.50", cursor: "pointer" }}
sx={{
"> td": { borderBottom: "none" },
}}
>
{showCheckbox && (
<Td>
<Checkbox isChecked={isChecked} onChange={() => toggleChecked(loggedCall.id)} />
</Td>
)}
<Td>
<Tooltip label={fullTime} placement="top">
<Box whiteSpace="nowrap" minW="120px">

View File

@@ -19,10 +19,15 @@ export const loggedCallsRouter = createTRPCRouter({
take: pageSize,
});
const matchingLogs = await prisma.loggedCall.findMany({
where: { projectId },
select: { id: true },
});
const count = await prisma.loggedCall.count({
where: { projectId },
});
return { count, calls };
return { calls, count, matchingLogIds: matchingLogs.map((log) => log.id) };
}),
});

View File

@@ -4,16 +4,13 @@ export const editorBackground = "#fafafa";
export type SelectedLogsSlice = {
selectedLogIds: Set<string>;
setSelectedLogIds: (ids: Set<string>) => void;
toggleSelectedLogId: (id: string) => void;
addSelectedLogIds: (ids: string[]) => void;
clearSelectedLogIds: () => void;
};
export const createSelectedLogsSlice: SliceCreator<SelectedLogsSlice> = (set, get) => ({
selectedLogIds: new Set(),
setSelectedLogIds: (ids: Set<string>) =>
set((state) => {
state.selectedLogs.selectedLogIds = ids;
}),
toggleSelectedLogId: (id: string) =>
set((state) => {
if (state.selectedLogs.selectedLogIds.has(id)) {
@@ -22,4 +19,12 @@ export const createSelectedLogsSlice: SliceCreator<SelectedLogsSlice> = (set, ge
state.selectedLogs.selectedLogIds.add(id);
}
}),
addSelectedLogIds: (ids: string[]) =>
set((state) => {
state.selectedLogs.selectedLogIds = new Set([...state.selectedLogs.selectedLogIds, ...ids]);
}),
clearSelectedLogIds: () =>
set((state) => {
state.selectedLogs.selectedLogIds = new Set();
}),
});

View File

@@ -1,5 +1,6 @@
import { type StateCreator, create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { enableMapSet } from "immer";
import { persist } from "zustand/middleware";
import { createSelectors } from "./createSelectors";
import {
@@ -10,6 +11,8 @@ import { type APIClient } from "~/utils/api";
import { persistOptions, type stateToPersist } from "./persist";
import { type SelectedLogsSlice, createSelectedLogsSlice } from "./selectedLogsSlice";
enableMapSet();
export type State = {
drawerOpen: boolean;
openDrawer: () => void;