Add logged calls pagination (#140)
* Store model on LoggedCall * Allow mulitple page sizes * Add logged calls pagination
This commit is contained in:
		@@ -1,21 +1,16 @@
 | 
			
		||||
import { type StackProps } from "@chakra-ui/react";
 | 
			
		||||
 | 
			
		||||
import { useScenarios } from "~/utils/hooks";
 | 
			
		||||
import Paginator from "../Paginator";
 | 
			
		||||
 | 
			
		||||
const ScenarioPaginator = () => {
 | 
			
		||||
const ScenarioPaginator = (props: StackProps) => {
 | 
			
		||||
  const { data } = useScenarios();
 | 
			
		||||
 | 
			
		||||
  if (!data) return null;
 | 
			
		||||
 | 
			
		||||
  const { scenarios, startIndex, lastPage, count } = data;
 | 
			
		||||
  const { count } = data;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Paginator
 | 
			
		||||
      numItemsLoaded={scenarios.length}
 | 
			
		||||
      startIndex={startIndex}
 | 
			
		||||
      lastPage={lastPage}
 | 
			
		||||
      count={count}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
  return <Paginator count={count} condense {...props} />;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default ScenarioPaginator;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,77 +1,118 @@
 | 
			
		||||
import { Box, HStack, IconButton } from "@chakra-ui/react";
 | 
			
		||||
import { HStack, IconButton, Text, Select, type StackProps } from "@chakra-ui/react";
 | 
			
		||||
import React, { useCallback } from "react";
 | 
			
		||||
import {
 | 
			
		||||
  BsChevronDoubleLeft,
 | 
			
		||||
  BsChevronDoubleRight,
 | 
			
		||||
  BsChevronLeft,
 | 
			
		||||
  BsChevronRight,
 | 
			
		||||
} from "react-icons/bs";
 | 
			
		||||
import { usePage } from "~/utils/hooks";
 | 
			
		||||
import { usePageParams } from "~/utils/hooks";
 | 
			
		||||
 | 
			
		||||
const pageSizeOptions = [10, 25, 50, 100];
 | 
			
		||||
 | 
			
		||||
const Paginator = ({
 | 
			
		||||
  numItemsLoaded,
 | 
			
		||||
  startIndex,
 | 
			
		||||
  lastPage,
 | 
			
		||||
  count,
 | 
			
		||||
}: {
 | 
			
		||||
  numItemsLoaded: number;
 | 
			
		||||
  startIndex: number;
 | 
			
		||||
  lastPage: number;
 | 
			
		||||
  count: number;
 | 
			
		||||
}) => {
 | 
			
		||||
  const [page, setPage] = usePage();
 | 
			
		||||
  condense,
 | 
			
		||||
  ...props
 | 
			
		||||
}: { count: number; condense?: boolean } & StackProps) => {
 | 
			
		||||
  const { page, pageSize, setPageParams } = usePageParams();
 | 
			
		||||
 | 
			
		||||
  const lastPage = Math.ceil(count / pageSize);
 | 
			
		||||
 | 
			
		||||
  const updatePageSize = useCallback(
 | 
			
		||||
    (newPageSize: number) => {
 | 
			
		||||
      const newPage = Math.floor(((page - 1) * pageSize) / newPageSize) + 1;
 | 
			
		||||
      setPageParams({ page: newPage, pageSize: newPageSize }, "replace");
 | 
			
		||||
    },
 | 
			
		||||
    [page, pageSize, setPageParams],
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const nextPage = () => {
 | 
			
		||||
    if (page < lastPage) {
 | 
			
		||||
      setPage(page + 1, "replace");
 | 
			
		||||
      setPageParams({ page: page + 1 }, "replace");
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const prevPage = () => {
 | 
			
		||||
    if (page > 1) {
 | 
			
		||||
      setPage(page - 1, "replace");
 | 
			
		||||
      setPageParams({ page: page - 1 }, "replace");
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const goToLastPage = () => setPage(lastPage, "replace");
 | 
			
		||||
  const goToFirstPage = () => setPage(1, "replace");
 | 
			
		||||
  const goToLastPage = () => setPageParams({ page: lastPage }, "replace");
 | 
			
		||||
  const goToFirstPage = () => setPageParams({ page: 1 }, "replace");
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <HStack pt={4}>
 | 
			
		||||
      <IconButton
 | 
			
		||||
        variant="ghost"
 | 
			
		||||
        size="sm"
 | 
			
		||||
        onClick={goToFirstPage}
 | 
			
		||||
        isDisabled={page === 1}
 | 
			
		||||
        aria-label="Go to first page"
 | 
			
		||||
        icon={<BsChevronDoubleLeft />}
 | 
			
		||||
      />
 | 
			
		||||
      <IconButton
 | 
			
		||||
        variant="ghost"
 | 
			
		||||
        size="sm"
 | 
			
		||||
        onClick={prevPage}
 | 
			
		||||
        isDisabled={page === 1}
 | 
			
		||||
        aria-label="Previous page"
 | 
			
		||||
        icon={<BsChevronLeft />}
 | 
			
		||||
      />
 | 
			
		||||
      <Box>
 | 
			
		||||
        {startIndex}-{startIndex + numItemsLoaded - 1} / {count}
 | 
			
		||||
      </Box>
 | 
			
		||||
      <IconButton
 | 
			
		||||
        variant="ghost"
 | 
			
		||||
        size="sm"
 | 
			
		||||
        onClick={nextPage}
 | 
			
		||||
        isDisabled={page === lastPage}
 | 
			
		||||
        aria-label="Next page"
 | 
			
		||||
        icon={<BsChevronRight />}
 | 
			
		||||
      />
 | 
			
		||||
      <IconButton
 | 
			
		||||
        variant="ghost"
 | 
			
		||||
        size="sm"
 | 
			
		||||
        onClick={goToLastPage}
 | 
			
		||||
        isDisabled={page === lastPage}
 | 
			
		||||
        aria-label="Go to last page"
 | 
			
		||||
        icon={<BsChevronDoubleRight />}
 | 
			
		||||
      />
 | 
			
		||||
    <HStack
 | 
			
		||||
      pt={4}
 | 
			
		||||
      px={4}
 | 
			
		||||
      spacing={8}
 | 
			
		||||
      justifyContent={condense ? "flex-start" : "space-between"}
 | 
			
		||||
      alignItems="center"
 | 
			
		||||
      w="full"
 | 
			
		||||
      {...props}
 | 
			
		||||
    >
 | 
			
		||||
      {!condense && (
 | 
			
		||||
        <>
 | 
			
		||||
          <HStack>
 | 
			
		||||
            <Text>Rows</Text>
 | 
			
		||||
            <Select
 | 
			
		||||
              value={pageSize}
 | 
			
		||||
              onChange={(e) => updatePageSize(parseInt(e.target.value))}
 | 
			
		||||
              w={20}
 | 
			
		||||
            >
 | 
			
		||||
              {pageSizeOptions.map((option) => (
 | 
			
		||||
                <option key={option} value={option}>
 | 
			
		||||
                  {option}
 | 
			
		||||
                </option>
 | 
			
		||||
              ))}
 | 
			
		||||
            </Select>
 | 
			
		||||
          </HStack>
 | 
			
		||||
          <Text>
 | 
			
		||||
            Page {page} of {lastPage}
 | 
			
		||||
          </Text>
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
 | 
			
		||||
      <HStack>
 | 
			
		||||
        <IconButton
 | 
			
		||||
          variant="outline"
 | 
			
		||||
          size="sm"
 | 
			
		||||
          onClick={goToFirstPage}
 | 
			
		||||
          isDisabled={page === 1}
 | 
			
		||||
          aria-label="Go to first page"
 | 
			
		||||
          icon={<BsChevronDoubleLeft />}
 | 
			
		||||
        />
 | 
			
		||||
        <IconButton
 | 
			
		||||
          variant="outline"
 | 
			
		||||
          size="sm"
 | 
			
		||||
          onClick={prevPage}
 | 
			
		||||
          isDisabled={page === 1}
 | 
			
		||||
          aria-label="Previous page"
 | 
			
		||||
          icon={<BsChevronLeft />}
 | 
			
		||||
        />
 | 
			
		||||
        {condense && (
 | 
			
		||||
          <Text>
 | 
			
		||||
            Page {page} of {lastPage}
 | 
			
		||||
          </Text>
 | 
			
		||||
        )}
 | 
			
		||||
        <IconButton
 | 
			
		||||
          variant="outline"
 | 
			
		||||
          size="sm"
 | 
			
		||||
          onClick={nextPage}
 | 
			
		||||
          isDisabled={page === lastPage}
 | 
			
		||||
          aria-label="Next page"
 | 
			
		||||
          icon={<BsChevronRight />}
 | 
			
		||||
        />
 | 
			
		||||
        <IconButton
 | 
			
		||||
          variant="outline"
 | 
			
		||||
          size="sm"
 | 
			
		||||
          onClick={goToLastPage}
 | 
			
		||||
          isDisabled={page === lastPage}
 | 
			
		||||
          aria-label="Go to last page"
 | 
			
		||||
          icon={<BsChevronDoubleRight />}
 | 
			
		||||
        />
 | 
			
		||||
      </HStack>
 | 
			
		||||
    </HStack>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -23,15 +23,16 @@ import dayjs from "dayjs";
 | 
			
		||||
import relativeTime from "dayjs/plugin/relativeTime";
 | 
			
		||||
import { ChevronUpIcon, ChevronDownIcon, CopyIcon } from "lucide-react";
 | 
			
		||||
import { useMemo, useState } from "react";
 | 
			
		||||
import { type RouterOutputs, api } from "~/utils/api";
 | 
			
		||||
import { type RouterOutputs } from "~/utils/api";
 | 
			
		||||
import SyntaxHighlighter from "react-syntax-highlighter";
 | 
			
		||||
import { atelierCaveLight } from "react-syntax-highlighter/dist/cjs/styles/hljs";
 | 
			
		||||
import stringify from "json-stringify-pretty-compact";
 | 
			
		||||
import Link from "next/link";
 | 
			
		||||
import { useLoggedCalls } from "~/utils/hooks";
 | 
			
		||||
 | 
			
		||||
dayjs.extend(relativeTime);
 | 
			
		||||
 | 
			
		||||
type LoggedCall = RouterOutputs["dashboard"]["loggedCalls"][0];
 | 
			
		||||
type LoggedCall = RouterOutputs["dashboard"]["loggedCalls"]["calls"][0];
 | 
			
		||||
 | 
			
		||||
const FormattedJson = ({ json }: { json: any }) => {
 | 
			
		||||
  const jsonString = stringify(json, { maxLength: 40 });
 | 
			
		||||
@@ -156,7 +157,7 @@ function TableRow({
 | 
			
		||||
 | 
			
		||||
export default function LoggedCallTable() {
 | 
			
		||||
  const [expandedRow, setExpandedRow] = useState<string | null>(null);
 | 
			
		||||
  const loggedCalls = api.dashboard.loggedCalls.useQuery({});
 | 
			
		||||
  const { data: loggedCalls } = useLoggedCalls();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card variant="outline" width="100%" overflow="hidden">
 | 
			
		||||
@@ -178,7 +179,7 @@ export default function LoggedCallTable() {
 | 
			
		||||
          </Tr>
 | 
			
		||||
        </Thead>
 | 
			
		||||
        <Tbody>
 | 
			
		||||
          {loggedCalls.data?.map((loggedCall) => {
 | 
			
		||||
          {loggedCalls?.calls.map((loggedCall) => {
 | 
			
		||||
            return (
 | 
			
		||||
              <TableRow
 | 
			
		||||
                key={loggedCall.id}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										16
									
								
								app/src/components/dashboard/LoggedCallsPaginator.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								app/src/components/dashboard/LoggedCallsPaginator.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
import { type StackProps } from "@chakra-ui/react";
 | 
			
		||||
 | 
			
		||||
import { useLoggedCalls } from "~/utils/hooks";
 | 
			
		||||
import Paginator from "../Paginator";
 | 
			
		||||
 | 
			
		||||
const LoggedCallsPaginator = (props: StackProps) => {
 | 
			
		||||
  const { data } = useLoggedCalls();
 | 
			
		||||
 | 
			
		||||
  if (!data) return null;
 | 
			
		||||
 | 
			
		||||
  const { count } = data;
 | 
			
		||||
 | 
			
		||||
  return <Paginator count={count} {...props} />;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default LoggedCallsPaginator;
 | 
			
		||||
@@ -1,21 +1,16 @@
 | 
			
		||||
import { type StackProps } from "@chakra-ui/react";
 | 
			
		||||
 | 
			
		||||
import { useDatasetEntries } from "~/utils/hooks";
 | 
			
		||||
import Paginator from "../Paginator";
 | 
			
		||||
 | 
			
		||||
const DatasetEntriesPaginator = () => {
 | 
			
		||||
const DatasetEntriesPaginator = (props: StackProps) => {
 | 
			
		||||
  const { data } = useDatasetEntries();
 | 
			
		||||
 | 
			
		||||
  if (!data) return null;
 | 
			
		||||
 | 
			
		||||
  const { entries, startIndex, lastPage, count } = data;
 | 
			
		||||
  const { count } = data;
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Paginator
 | 
			
		||||
      numItemsLoaded={entries.length}
 | 
			
		||||
      startIndex={startIndex}
 | 
			
		||||
      lastPage={lastPage}
 | 
			
		||||
      count={count}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
  return <Paginator count={count} {...props} />;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export default DatasetEntriesPaginator;
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import { useSelectedProject } from "~/utils/hooks";
 | 
			
		||||
import { api } from "~/utils/api";
 | 
			
		||||
import LoggedCallTable from "~/components/dashboard/LoggedCallTable";
 | 
			
		||||
import UsageGraph from "~/components/dashboard/UsageGraph";
 | 
			
		||||
import LoggedCallsPaginator from "~/components/dashboard/LoggedCallsPaginator";
 | 
			
		||||
 | 
			
		||||
export default function LoggedCalls() {
 | 
			
		||||
  const { data: selectedProject } = useSelectedProject();
 | 
			
		||||
@@ -48,7 +49,7 @@ export default function LoggedCalls() {
 | 
			
		||||
          </BreadcrumbItem>
 | 
			
		||||
        </Breadcrumb>
 | 
			
		||||
      </PageHeaderContainer>
 | 
			
		||||
      <VStack px={8} pt={4} alignItems="flex-start" spacing={4}>
 | 
			
		||||
      <VStack px={8} py={4} alignItems="flex-start" spacing={4}>
 | 
			
		||||
        <Text fontSize="2xl" fontWeight="bold">
 | 
			
		||||
          {selectedProject?.name}
 | 
			
		||||
        </Text>
 | 
			
		||||
@@ -121,6 +122,7 @@ export default function LoggedCalls() {
 | 
			
		||||
            </VStack>
 | 
			
		||||
          </HStack>
 | 
			
		||||
          <LoggedCallTable />
 | 
			
		||||
          <LoggedCallsPaginator />
 | 
			
		||||
        </VStack>
 | 
			
		||||
      </VStack>
 | 
			
		||||
    </AppShell>
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ export default function Settings() {
 | 
			
		||||
            </BreadcrumbItem>
 | 
			
		||||
          </Breadcrumb>
 | 
			
		||||
        </PageHeaderContainer>
 | 
			
		||||
        <VStack px={8} pt={4} alignItems="flex-start" spacing={4}>
 | 
			
		||||
        <VStack px={8} py={4} alignItems="flex-start" spacing={4}>
 | 
			
		||||
          <VStack spacing={0} alignItems="flex-start">
 | 
			
		||||
            <Text fontSize="2xl" fontWeight="bold">
 | 
			
		||||
              Project Settings
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import { sql } from "kysely";
 | 
			
		||||
import { z } from "zod";
 | 
			
		||||
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
 | 
			
		||||
import { kysely, prisma } from "~/server/db";
 | 
			
		||||
import { requireCanViewProject } from "~/utils/accessControl";
 | 
			
		||||
import dayjs from "~/utils/dayjs";
 | 
			
		||||
 | 
			
		||||
export const dashboardRouter = createTRPCRouter({
 | 
			
		||||
@@ -13,7 +14,8 @@ export const dashboardRouter = createTRPCRouter({
 | 
			
		||||
        projectId: z.string(),
 | 
			
		||||
      }),
 | 
			
		||||
    )
 | 
			
		||||
    .query(async ({ input }) => {
 | 
			
		||||
    .query(async ({ input, ctx }) => {
 | 
			
		||||
      await requireCanViewProject(input.projectId, ctx);
 | 
			
		||||
      // Return the stats group by hour
 | 
			
		||||
      const periods = await kysely
 | 
			
		||||
        .selectFrom("LoggedCall")
 | 
			
		||||
@@ -106,13 +108,25 @@ export const dashboardRouter = createTRPCRouter({
 | 
			
		||||
 | 
			
		||||
  // TODO useInfiniteQuery
 | 
			
		||||
  // https://discord.com/channels/966627436387266600/1122258443886153758/1122258443886153758
 | 
			
		||||
  loggedCalls: publicProcedure.input(z.object({})).query(async ({ input }) => {
 | 
			
		||||
    const loggedCalls = await prisma.loggedCall.findMany({
 | 
			
		||||
      orderBy: { requestedAt: "desc" },
 | 
			
		||||
      include: { tags: true, modelResponse: true },
 | 
			
		||||
      take: 20,
 | 
			
		||||
    });
 | 
			
		||||
  loggedCalls: publicProcedure
 | 
			
		||||
    .input(z.object({ projectId: z.string(), page: z.number(), pageSize: z.number() }))
 | 
			
		||||
    .query(async ({ input, ctx }) => {
 | 
			
		||||
      const { projectId, page, pageSize } = input;
 | 
			
		||||
 | 
			
		||||
    return loggedCalls;
 | 
			
		||||
  }),
 | 
			
		||||
      await requireCanViewProject(projectId, ctx);
 | 
			
		||||
 | 
			
		||||
      const calls = await prisma.loggedCall.findMany({
 | 
			
		||||
        where: { projectId },
 | 
			
		||||
        orderBy: { requestedAt: "desc" },
 | 
			
		||||
        include: { tags: true, modelResponse: true },
 | 
			
		||||
        skip: (page - 1) * pageSize,
 | 
			
		||||
        take: pageSize,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const count = await prisma.loggedCall.count({
 | 
			
		||||
        where: { projectId },
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return { count, calls };
 | 
			
		||||
    }),
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -4,23 +4,21 @@ import { prisma } from "~/server/db";
 | 
			
		||||
import { requireCanModifyDataset, requireCanViewDataset } from "~/utils/accessControl";
 | 
			
		||||
import { autogenerateDatasetEntries } from "../autogenerate/autogenerateDatasetEntries";
 | 
			
		||||
 | 
			
		||||
const PAGE_SIZE = 10;
 | 
			
		||||
 | 
			
		||||
export const datasetEntries = createTRPCRouter({
 | 
			
		||||
  list: protectedProcedure
 | 
			
		||||
    .input(z.object({ datasetId: z.string(), page: z.number() }))
 | 
			
		||||
    .input(z.object({ datasetId: z.string(), page: z.number(), pageSize: z.number() }))
 | 
			
		||||
    .query(async ({ input, ctx }) => {
 | 
			
		||||
      await requireCanViewDataset(input.datasetId, ctx);
 | 
			
		||||
 | 
			
		||||
      const { datasetId, page } = input;
 | 
			
		||||
      const { datasetId, page, pageSize } = input;
 | 
			
		||||
 | 
			
		||||
      const entries = await prisma.datasetEntry.findMany({
 | 
			
		||||
        where: {
 | 
			
		||||
          datasetId,
 | 
			
		||||
        },
 | 
			
		||||
        orderBy: { createdAt: "desc" },
 | 
			
		||||
        skip: (page - 1) * PAGE_SIZE,
 | 
			
		||||
        take: PAGE_SIZE,
 | 
			
		||||
        skip: (page - 1) * pageSize,
 | 
			
		||||
        take: pageSize,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const count = await prisma.datasetEntry.count({
 | 
			
		||||
@@ -31,8 +29,6 @@ export const datasetEntries = createTRPCRouter({
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        entries,
 | 
			
		||||
        startIndex: (page - 1) * PAGE_SIZE + 1,
 | 
			
		||||
        lastPage: Math.ceil(count / PAGE_SIZE),
 | 
			
		||||
        count,
 | 
			
		||||
      };
 | 
			
		||||
    }),
 | 
			
		||||
 
 | 
			
		||||
@@ -7,15 +7,13 @@ import { runAllEvals } from "~/server/utils/evaluations";
 | 
			
		||||
import { generateNewCell } from "~/server/utils/generateNewCell";
 | 
			
		||||
import { requireCanModifyExperiment, requireCanViewExperiment } from "~/utils/accessControl";
 | 
			
		||||
 | 
			
		||||
const PAGE_SIZE = 10;
 | 
			
		||||
 | 
			
		||||
export const scenariosRouter = createTRPCRouter({
 | 
			
		||||
  list: publicProcedure
 | 
			
		||||
    .input(z.object({ experimentId: z.string(), page: z.number() }))
 | 
			
		||||
    .input(z.object({ experimentId: z.string(), page: z.number(), pageSize: z.number() }))
 | 
			
		||||
    .query(async ({ input, ctx }) => {
 | 
			
		||||
      await requireCanViewExperiment(input.experimentId, ctx);
 | 
			
		||||
 | 
			
		||||
      const { experimentId, page } = input;
 | 
			
		||||
      const { experimentId, page, pageSize } = input;
 | 
			
		||||
 | 
			
		||||
      const scenarios = await prisma.testScenario.findMany({
 | 
			
		||||
        where: {
 | 
			
		||||
@@ -23,8 +21,8 @@ export const scenariosRouter = createTRPCRouter({
 | 
			
		||||
          visible: true,
 | 
			
		||||
        },
 | 
			
		||||
        orderBy: { sortIndex: "asc" },
 | 
			
		||||
        skip: (page - 1) * PAGE_SIZE,
 | 
			
		||||
        take: PAGE_SIZE,
 | 
			
		||||
        skip: (page - 1) * pageSize,
 | 
			
		||||
        take: pageSize,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      const count = await prisma.testScenario.count({
 | 
			
		||||
@@ -36,8 +34,6 @@ export const scenariosRouter = createTRPCRouter({
 | 
			
		||||
 | 
			
		||||
      return {
 | 
			
		||||
        scenarios,
 | 
			
		||||
        startIndex: (page - 1) * PAGE_SIZE + 1,
 | 
			
		||||
        lastPage: Math.ceil(count / PAGE_SIZE),
 | 
			
		||||
        count,
 | 
			
		||||
      };
 | 
			
		||||
    }),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import { useRouter } from "next/router";
 | 
			
		||||
import { type RefObject, useCallback, useEffect, useRef, useState } from "react";
 | 
			
		||||
import { api } from "~/utils/api";
 | 
			
		||||
import { NumberParam, useQueryParam, withDefault } from "use-query-params";
 | 
			
		||||
import { NumberParam, useQueryParams } from "use-query-params";
 | 
			
		||||
import { useAppStore } from "~/state/store";
 | 
			
		||||
 | 
			
		||||
export const useExperiments = () => {
 | 
			
		||||
@@ -46,10 +46,10 @@ export const useDataset = () => {
 | 
			
		||||
 | 
			
		||||
export const useDatasetEntries = () => {
 | 
			
		||||
  const dataset = useDataset();
 | 
			
		||||
  const [page] = usePage();
 | 
			
		||||
  const { page, pageSize } = usePageParams();
 | 
			
		||||
 | 
			
		||||
  return api.datasetEntries.list.useQuery(
 | 
			
		||||
    { datasetId: dataset.data?.id ?? "", page },
 | 
			
		||||
    { datasetId: dataset.data?.id ?? "", page, pageSize },
 | 
			
		||||
    { enabled: dataset.data?.id != null },
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -132,14 +132,23 @@ export const useElementDimensions = (): [RefObject<HTMLElement>, Dimensions | un
 | 
			
		||||
  return [ref, dimensions];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const usePage = () => useQueryParam("page", withDefault(NumberParam, 1));
 | 
			
		||||
export const usePageParams = () => {
 | 
			
		||||
  const [pageParams, setPageParams] = useQueryParams({
 | 
			
		||||
    page: NumberParam,
 | 
			
		||||
    pageSize: NumberParam,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  const { page, pageSize } = pageParams;
 | 
			
		||||
 | 
			
		||||
  return { page: page || 1, pageSize: pageSize || 10, setPageParams };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const useScenarios = () => {
 | 
			
		||||
  const experiment = useExperiment();
 | 
			
		||||
  const [page] = usePage();
 | 
			
		||||
  const { page, pageSize } = usePageParams();
 | 
			
		||||
 | 
			
		||||
  return api.scenarios.list.useQuery(
 | 
			
		||||
    { experimentId: experiment.data?.id ?? "", page },
 | 
			
		||||
    { experimentId: experiment.data?.id ?? "", page, pageSize },
 | 
			
		||||
    { enabled: experiment.data?.id != null },
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@@ -166,3 +175,13 @@ export const useScenarioVars = () => {
 | 
			
		||||
    { enabled: experiment.data?.id != null },
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const useLoggedCalls = () => {
 | 
			
		||||
  const selectedProjectId = useAppStore((state) => state.selectedProjectId);
 | 
			
		||||
  const { page, pageSize } = usePageParams();
 | 
			
		||||
 | 
			
		||||
  return api.dashboard.loggedCalls.useQuery(
 | 
			
		||||
    { projectId: selectedProjectId ?? "", page, pageSize },
 | 
			
		||||
    { enabled: !!selectedProjectId },
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user