diff --git a/.vscode/settings.json b/.vscode/settings.json
index a9d566c..d012460 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,3 @@
{
- "eslint.format.enable": true,
- "editor.codeActionsOnSave": {
- "source.fixAll.eslint": true
- }
+ "eslint.format.enable": true
}
diff --git a/package.json b/package.json
index ceec72b..a4bf463 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
"lodash-es": "^4.17.21",
"next": "^13.4.2",
"next-auth": "^4.22.1",
+ "next-query-params": "^4.2.3",
"nextjs-routes": "^2.0.1",
"openai": "4.0.0-beta.2",
"pluralize": "^8.0.0",
@@ -79,6 +80,7 @@
"superjson": "1.12.2",
"tsx": "^3.12.7",
"type-fest": "^4.0.0",
+ "use-query-params": "^2.2.1",
"vite-tsconfig-paths": "^4.2.0",
"zod": "^3.21.4",
"zustand": "^4.3.9"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d2d34b7..7b4f17e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -119,6 +119,9 @@ dependencies:
next-auth:
specifier: ^4.22.1
version: 4.22.1(next@13.4.2)(react-dom@18.2.0)(react@18.2.0)
+ next-query-params:
+ specifier: ^4.2.3
+ version: 4.2.3(next@13.4.2)(react@18.2.0)(use-query-params@2.2.1)
nextjs-routes:
specifier: ^2.0.1
version: 2.0.1(next@13.4.2)
@@ -179,6 +182,9 @@ dependencies:
type-fest:
specifier: ^4.0.0
version: 4.0.0
+ use-query-params:
+ specifier: ^2.2.1
+ version: 2.2.1(react-dom@18.2.0)(react@18.2.0)
vite-tsconfig-paths:
specifier: ^4.2.0
version: 4.2.0(typescript@5.0.4)
@@ -6037,6 +6043,19 @@ packages:
uuid: 8.3.2
dev: false
+ /next-query-params@4.2.3(next@13.4.2)(react@18.2.0)(use-query-params@2.2.1):
+ resolution: {integrity: sha512-hGNCYRH8YyA5ItiBGSKrtMl21b2MAqfPkdI1mvwloNVqSU142IaGzqHN+OTovyeLIpQfonY01y7BAHb/UH4POg==}
+ peerDependencies:
+ next: ^10.0.0 || ^11.0.0 || ^12.0.0 || ^13.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ use-query-params: ^2.0.0
+ dependencies:
+ next: 13.4.2(@babel/core@7.22.9)(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ tslib: 2.6.0
+ use-query-params: 2.2.1(react-dom@18.2.0)(react@18.2.0)
+ dev: false
+
/next-tick@1.1.0:
resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==}
dev: false
@@ -7147,6 +7166,10 @@ packages:
randombytes: 2.1.0
dev: true
+ /serialize-query-params@2.0.2:
+ resolution: {integrity: sha512-1chMo1dST4pFA9RDXAtF0Rbjaut4is7bzFbI1Z26IuMub68pNCILku85aYmeFhvnY//BXUPUhoRMjYcsT93J/Q==}
+ dev: false
+
/serve-static@1.15.0:
resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
engines: {node: '>= 0.8.0'}
@@ -7824,6 +7847,24 @@ packages:
use-isomorphic-layout-effect: 1.1.2(@types/react@18.2.6)(react@18.2.0)
dev: false
+ /use-query-params@2.2.1(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-i6alcyLB8w9i3ZK3caNftdb+UnbfBRNPDnc89CNQWkGRmDrm/gfydHvMBfVsQJRq3NoHOM2dt/ceBWG2397v1Q==}
+ peerDependencies:
+ '@reach/router': ^1.2.1
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+ react-router-dom: '>=5'
+ peerDependenciesMeta:
+ '@reach/router':
+ optional: true
+ react-router-dom:
+ optional: true
+ dependencies:
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ serialize-query-params: 2.0.2
+ dev: false
+
/use-sidecar@1.1.2(@types/react@18.2.6)(react@18.2.0):
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
engines: {node: '>=10'}
diff --git a/prisma/seed.ts b/prisma/seed.ts
index 0e5ac9c..abdc071 100644
--- a/prisma/seed.ts
+++ b/prisma/seed.ts
@@ -7,9 +7,13 @@ const defaultId = "11111111-1111-1111-1111-111111111111";
await prisma.organization.deleteMany({
where: { id: defaultId },
});
-await prisma.organization.create({
- data: { id: defaultId },
-});
+
+// If there's an existing org, just seed into it
+const org =
+ (await prisma.organization.findFirst({})) ??
+ (await prisma.organization.create({
+ data: { id: defaultId },
+ }));
await prisma.experiment.deleteMany({
where: {
@@ -21,7 +25,7 @@ await prisma.experiment.create({
data: {
id: defaultId,
label: "Country Capitals Example",
- organizationId: defaultId,
+ organizationId: org.id,
},
});
@@ -103,30 +107,41 @@ await prisma.testScenario.deleteMany({
},
});
+const countries = [
+ "Afghanistan",
+ "Albania",
+ "Algeria",
+ "Andorra",
+ "Angola",
+ "Antigua and Barbuda",
+ "Argentina",
+ "Armenia",
+ "Australia",
+ "Austria",
+ "Austrian Empire",
+ "Azerbaijan",
+ "Baden",
+ "Bahamas, The",
+ "Bahrain",
+ "Bangladesh",
+ "Barbados",
+ "Bavaria",
+ "Belarus",
+ "Belgium",
+ "Belize",
+ "Benin (Dahomey)",
+ "Bolivia",
+ "Bosnia and Herzegovina",
+ "Botswana",
+];
await prisma.testScenario.createMany({
- data: [
- {
- experimentId: defaultId,
- sortIndex: 0,
- variableValues: {
- country: "Spain",
- },
+ data: countries.map((country, i) => ({
+ experimentId: defaultId,
+ sortIndex: i,
+ variableValues: {
+ country: country,
},
- {
- experimentId: defaultId,
- sortIndex: 1,
- variableValues: {
- country: "USA",
- },
- },
- {
- experimentId: defaultId,
- sortIndex: 2,
- variableValues: {
- country: "Chile",
- },
- },
- ],
+ })),
});
const variants = await prisma.promptVariant.findMany({
diff --git a/src/components/OutputsTable/ScenarioPaginator.tsx b/src/components/OutputsTable/ScenarioPaginator.tsx
new file mode 100644
index 0000000..264d803
--- /dev/null
+++ b/src/components/OutputsTable/ScenarioPaginator.tsx
@@ -0,0 +1,74 @@
+import { Box, HStack, IconButton } from "@chakra-ui/react";
+import {
+ BsChevronDoubleLeft,
+ BsChevronDoubleRight,
+ BsChevronLeft,
+ BsChevronRight,
+} from "react-icons/bs";
+import { usePage, useScenarios } from "~/utils/hooks";
+
+const ScenarioPaginator = () => {
+ const [page, setPage] = usePage();
+ const { data } = useScenarios();
+
+ if (!data) return null;
+
+ const { scenarios, startIndex, lastPage, count } = data;
+
+ const nextPage = () => {
+ if (page < lastPage) {
+ setPage(page + 1, "replace");
+ }
+ };
+
+ const prevPage = () => {
+ if (page > 1) {
+ setPage(page - 1, "replace");
+ }
+ };
+
+ const goToLastPage = () => setPage(lastPage, "replace");
+ const goToFirstPage = () => setPage(1, "replace");
+
+ return (
+
+ }
+ />
+ }
+ />
+
+ {startIndex}-{startIndex + scenarios.length - 1} / {count}
+
+ }
+ />
+ }
+ />
+
+ );
+};
+
+export default ScenarioPaginator;
diff --git a/src/components/OutputsTable/ScenariosHeader.tsx b/src/components/OutputsTable/ScenariosHeader.tsx
index f3ccdc6..37bb7ce 100644
--- a/src/components/OutputsTable/ScenariosHeader.tsx
+++ b/src/components/OutputsTable/ScenariosHeader.tsx
@@ -12,7 +12,12 @@ import {
Spinner,
} from "@chakra-ui/react";
import { cellPadding } from "../constants";
-import { useExperiment, useExperimentAccess, useHandledAsyncCallback } from "~/utils/hooks";
+import {
+ useExperiment,
+ useExperimentAccess,
+ useHandledAsyncCallback,
+ useScenarios,
+} from "~/utils/hooks";
import { BsGear, BsPencil, BsPlus, BsStars } from "react-icons/bs";
import { useAppStore } from "~/state/store";
import { api } from "~/utils/api";
@@ -21,9 +26,10 @@ export const ActionButton = (props: ButtonProps) => (
);
-export const ScenariosHeader = (props: { numScenarios: number }) => {
+export const ScenariosHeader = () => {
const openDrawer = useAppStore((s) => s.openDrawer);
const { canModify } = useExperimentAccess();
+ const scenarios = useScenarios();
const experiment = useExperiment();
const createScenarioMutation = api.scenarios.create.useMutation();
@@ -44,7 +50,7 @@ export const ScenariosHeader = (props: { numScenarios: number }) => {
return (
- Scenarios ({props.numScenarios})
+ Scenarios ({scenarios.data?.count})
{canModify && (