ripped out mantine, replaced with chakra
This commit is contained in:
10
package.json
10
package.json
@@ -11,14 +11,11 @@
|
|||||||
"start": "next start"
|
"start": "next start"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@chakra-ui/next-js": "^2.1.4",
|
||||||
|
"@chakra-ui/react": "^2.7.1",
|
||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@mantine/core": "^6.0.14",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@mantine/dates": "^6.0.14",
|
|
||||||
"@mantine/form": "^6.0.14",
|
|
||||||
"@mantine/hooks": "^6.0.14",
|
|
||||||
"@mantine/next": "^6.0.14",
|
|
||||||
"@mantine/notifications": "^6.0.14",
|
|
||||||
"@monaco-editor/react": "^4.5.1",
|
"@monaco-editor/react": "^4.5.1",
|
||||||
"@next-auth/prisma-adapter": "^1.0.5",
|
"@next-auth/prisma-adapter": "^1.0.5",
|
||||||
"@prisma/client": "^4.14.0",
|
"@prisma/client": "^4.14.0",
|
||||||
@@ -31,6 +28,7 @@
|
|||||||
"@trpc/server": "^10.26.0",
|
"@trpc/server": "^10.26.0",
|
||||||
"dayjs": "^1.11.8",
|
"dayjs": "^1.11.8",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
|
"framer-motion": "^10.12.17",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"next": "^13.4.2",
|
"next": "^13.4.2",
|
||||||
"next-auth": "^4.22.1",
|
"next-auth": "^4.22.1",
|
||||||
|
|||||||
1730
pnpm-lock.yaml
generated
1730
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
|||||||
import { useRef } from "react";
|
import { useRef } from "react";
|
||||||
import { Title } from "@mantine/core";
|
|
||||||
import { type PromptVariant } from "./types";
|
import { type PromptVariant } from "./types";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { useHandledAsyncCallback } from "~/utils/hooks";
|
import { useHandledAsyncCallback } from "~/utils/hooks";
|
||||||
|
import { Heading } from "@chakra-ui/react";
|
||||||
|
|
||||||
export default function EditableVariantLabel(props: { variant: PromptVariant }) {
|
export default function EditableVariantLabel(props: { variant: PromptVariant }) {
|
||||||
const labelRef = useRef<HTMLHeadingElement | null>(null);
|
const labelRef = useRef<HTMLHeadingElement | null>(null);
|
||||||
@@ -20,8 +20,15 @@ export default function EditableVariantLabel(props: { variant: PromptVariant })
|
|||||||
}, [mutation, props.variant.id, props.variant.label]);
|
}, [mutation, props.variant.id, props.variant.label]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Title order={4} ref={labelRef} contentEditable suppressContentEditableWarning onBlur={onBlur}>
|
<Heading
|
||||||
|
fontWeight="bold"
|
||||||
|
size="md"
|
||||||
|
ref={labelRef}
|
||||||
|
contentEditable
|
||||||
|
suppressContentEditableWarning
|
||||||
|
onBlur={onBlur}
|
||||||
|
>
|
||||||
{props.variant.label}
|
{props.variant.label}
|
||||||
</Title>
|
</Heading>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { PromptVariant, Scenario } from "./types";
|
import { PromptVariant, Scenario } from "./types";
|
||||||
import { Center } from "@mantine/core";
|
import { Center } from "@chakra-ui/react";
|
||||||
|
|
||||||
export default function OutputCell({
|
export default function OutputCell({
|
||||||
scenario,
|
scenario,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { isEqual } from "lodash";
|
import { isEqual } from "lodash";
|
||||||
import { PromptVariant, Scenario } from "./types";
|
import { PromptVariant, Scenario } from "./types";
|
||||||
import { Badge, Button, Group, Stack, TextInput, Textarea, Tooltip } from "@mantine/core";
|
|
||||||
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
|
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { Badge, Button, Flex, HStack, Stack, Textarea } from "@chakra-ui/react";
|
||||||
|
|
||||||
export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
|
export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
|
||||||
const savedValues = scenario.variableValues as Record<string, string>;
|
const savedValues = scenario.variableValues as Record<string, string>;
|
||||||
@@ -31,21 +31,23 @@ export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
|
|||||||
<Stack>
|
<Stack>
|
||||||
{variableLabels.map((key) => {
|
{variableLabels.map((key) => {
|
||||||
return (
|
return (
|
||||||
<Textarea
|
<Flex key={key}>
|
||||||
key={key}
|
<Badge>{key}</Badge>
|
||||||
label={key}
|
<Textarea
|
||||||
value={values[key] ?? ""}
|
key={key}
|
||||||
onChange={(e) => {
|
value={values[key] ?? ""}
|
||||||
setValues((prev) => ({ ...prev, [key]: e.target.value }));
|
onChange={(e) => {
|
||||||
}}
|
setValues((prev) => ({ ...prev, [key]: e.target.value }));
|
||||||
autosize
|
}}
|
||||||
rows={1}
|
rows={1}
|
||||||
maxRows={20}
|
// TODO: autosize
|
||||||
/>
|
maxRows={20}
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
{hasChanged && (
|
{hasChanged && (
|
||||||
<Group spacing={4} position="right">
|
<HStack spacing={4}>
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -58,7 +60,7 @@ export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
|
|||||||
<Button size="xs" onClick={onSave}>
|
<Button size="xs" onClick={onSave}>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</HStack>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { Box, Button, Group, Stack, Title, Tooltip } from "@mantine/core";
|
import { Box, Button, HStack, Tooltip } from "@chakra-ui/react";
|
||||||
import { useMonaco } from "@monaco-editor/react";
|
import { useMonaco } from "@monaco-editor/react";
|
||||||
import { useRef, useEffect, useState, useCallback } from "react";
|
import { useRef, useEffect, useState, useCallback } from "react";
|
||||||
import { set } from "zod";
|
|
||||||
import { useHandledAsyncCallback, useModifierKeyLabel } from "~/utils/hooks";
|
import { useHandledAsyncCallback, useModifierKeyLabel } from "~/utils/hooks";
|
||||||
|
|
||||||
let isThemeDefined = false;
|
let isThemeDefined = false;
|
||||||
@@ -109,23 +108,23 @@ export default function VariantConfigEditor(props: {
|
|||||||
<Box w="100%" pos="relative">
|
<Box w="100%" pos="relative">
|
||||||
<div id={editorId} style={{ height: "300px", width: "100%" }}></div>
|
<div id={editorId} style={{ height: "300px", width: "100%" }}></div>
|
||||||
{isChanged && (
|
{isChanged && (
|
||||||
<Group sx={{ position: "absolute", bottom: 0, right: 0 }} spacing={4}>
|
<HStack pos="absolute" bottom={0} right={0} spacing={4}>
|
||||||
<Button
|
<Button
|
||||||
|
colorScheme="gray"
|
||||||
size="xs"
|
size="xs"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
editorRef.current?.setValue(props.savedConfig);
|
editorRef.current?.setValue(props.savedConfig);
|
||||||
checkForChanges();
|
checkForChanges();
|
||||||
}}
|
}}
|
||||||
color="gray"
|
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
<Tooltip label={`${modifierKey} + Enter`} withArrow>
|
<Tooltip label={`${modifierKey} + Enter`}>
|
||||||
<Button size="xs" onClick={onSave}>
|
<Button size="xs" onClick={onSave} colorScheme="blue">
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Group>
|
</HStack>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { Stack, Title } from "@mantine/core";
|
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import type { PromptVariant } from "./types";
|
import type { PromptVariant } from "./types";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { notifications } from "@mantine/notifications";
|
|
||||||
import { type JSONSerializable } from "~/server/types";
|
import { type JSONSerializable } from "~/server/types";
|
||||||
import VariantConfigEditor from "./VariantConfigEditor";
|
import VariantConfigEditor from "./VariantConfigEditor";
|
||||||
import EditableVariantLabel from "./EditableVariantLabel";
|
import EditableVariantLabel from "./EditableVariantLabel";
|
||||||
|
import { Stack, useToast } from "@chakra-ui/react";
|
||||||
|
|
||||||
export default function VariantHeader({ variant }: { variant: PromptVariant }) {
|
export default function VariantHeader({ variant }: { variant: PromptVariant }) {
|
||||||
const replaceWithConfig = api.promptVariants.replaceWithConfig.useMutation();
|
const replaceWithConfig = api.promptVariants.replaceWithConfig.useMutation();
|
||||||
const utils = api.useContext();
|
const utils = api.useContext();
|
||||||
|
const toast = useToast();
|
||||||
|
|
||||||
const onSave = useCallback(
|
const onSave = useCallback(
|
||||||
async (currentConfig: string) => {
|
async (currentConfig: string) => {
|
||||||
@@ -17,10 +17,12 @@ export default function VariantHeader({ variant }: { variant: PromptVariant }) {
|
|||||||
try {
|
try {
|
||||||
parsedConfig = JSON.parse(currentConfig) as JSONSerializable;
|
parsedConfig = JSON.parse(currentConfig) as JSONSerializable;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
notifications.show({
|
toast({
|
||||||
title: "Invalid JSON",
|
title: "Invalid JSON",
|
||||||
message: "Please fix the JSON before saving.",
|
description: "Please fix the JSON before saving.",
|
||||||
color: "red",
|
status: "error",
|
||||||
|
duration: 5000,
|
||||||
|
position: "top",
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { type PromptVariant } from "./types";
|
|||||||
import VariantHeader from "./VariantHeader";
|
import VariantHeader from "./VariantHeader";
|
||||||
import OutputCell from "./OutputCell";
|
import OutputCell from "./OutputCell";
|
||||||
import ScenarioHeader from "./ScenarioHeader";
|
import ScenarioHeader from "./ScenarioHeader";
|
||||||
import { Box, Header, Title } from "@mantine/core";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { Box, Heading } from "@chakra-ui/react";
|
||||||
|
|
||||||
const cellPaddingX = 8;
|
const cellPaddingX = 4;
|
||||||
const cellPaddingY = 4;
|
const cellPaddingY = 2;
|
||||||
|
|
||||||
export default function OutputsTable({ experimentId }: { experimentId: string | undefined }) {
|
export default function OutputsTable({ experimentId }: { experimentId: string | undefined }) {
|
||||||
const variants = api.promptVariants.list.useQuery(
|
const variants = api.promptVariants.list.useQuery(
|
||||||
@@ -24,7 +24,7 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
|||||||
if (!variants.data || !scenarios.data) return null;
|
if (!variants.data || !scenarios.data) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box p={12}>
|
<Box p={4}>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
display: "grid",
|
display: "grid",
|
||||||
@@ -33,7 +33,9 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box px={cellPaddingX} py={cellPaddingY} display="flex" sx={{}}>
|
<Box px={cellPaddingX} py={cellPaddingY} display="flex" sx={{}}>
|
||||||
<Title order={4}>Scenario</Title>
|
<Heading size="md" fontWeight="bold">
|
||||||
|
Scenario
|
||||||
|
</Heading>
|
||||||
</Box>
|
</Box>
|
||||||
{variants.data.map((variant) => (
|
{variants.data.map((variant) => (
|
||||||
<Box key={variant.uiId} px={cellPaddingX} py={cellPaddingY}>
|
<Box key={variant.uiId} px={cellPaddingX} py={cellPaddingY}>
|
||||||
|
|||||||
@@ -1,129 +1,15 @@
|
|||||||
import { useState } from "react";
|
import { Box, Flex } from "@chakra-ui/react";
|
||||||
import { createStyles, Navbar, Group, Code, getStylesRef, rem, Box } from "@mantine/core";
|
|
||||||
import {
|
|
||||||
IconBellRinging,
|
|
||||||
IconFingerprint,
|
|
||||||
IconKey,
|
|
||||||
IconSettings,
|
|
||||||
Icon2fa,
|
|
||||||
IconDatabaseImport,
|
|
||||||
IconReceipt2,
|
|
||||||
IconSwitchHorizontal,
|
|
||||||
IconLogout,
|
|
||||||
} from "@tabler/icons-react";
|
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
// import { MantineLogo } from '@mantine/ds';
|
|
||||||
|
|
||||||
const useStyles = createStyles((theme) => ({
|
|
||||||
header: {
|
|
||||||
paddingBottom: theme.spacing.md,
|
|
||||||
marginBottom: `calc(${theme.spacing.md} * 1.5)`,
|
|
||||||
borderBottom: `${rem(1)} solid ${
|
|
||||||
theme.colorScheme === "dark" ? theme.colors.dark[4] : theme.colors.gray[4]
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
|
|
||||||
footer: {
|
|
||||||
paddingTop: theme.spacing.md,
|
|
||||||
marginTop: theme.spacing.md,
|
|
||||||
borderTop: `${rem(1)} solid ${
|
|
||||||
theme.colorScheme === "dark" ? theme.colors.dark[4] : theme.colors.gray[4]
|
|
||||||
}`,
|
|
||||||
},
|
|
||||||
|
|
||||||
link: {
|
|
||||||
...(theme.fn.focusStyles() as Record<string, any>),
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
textDecoration: "none",
|
|
||||||
fontSize: theme.fontSizes.sm,
|
|
||||||
color: theme.colorScheme === "dark" ? theme.colors.dark[1] : theme.colors.gray[7],
|
|
||||||
padding: `${theme.spacing.xs} ${theme.spacing.sm}`,
|
|
||||||
borderRadius: theme.radius.sm,
|
|
||||||
fontWeight: 500,
|
|
||||||
|
|
||||||
"&:hover": {
|
|
||||||
backgroundColor: theme.colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[0],
|
|
||||||
color: theme.colorScheme === "dark" ? theme.white : theme.black,
|
|
||||||
|
|
||||||
[`& .${getStylesRef("icon")}`]: {
|
|
||||||
color: theme.colorScheme === "dark" ? theme.white : theme.black,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
linkIcon: {
|
|
||||||
ref: getStylesRef("icon"),
|
|
||||||
color: theme.colorScheme === "dark" ? theme.colors.dark[2] : theme.colors.gray[6],
|
|
||||||
marginRight: theme.spacing.sm,
|
|
||||||
},
|
|
||||||
|
|
||||||
linkActive: {
|
|
||||||
"&, &:hover": {
|
|
||||||
backgroundColor: theme.fn.variant({ variant: "dark", color: theme.primaryColor }).background,
|
|
||||||
color: theme.fn.variant({ variant: "dark", color: theme.primaryColor }).color,
|
|
||||||
[`& .${getStylesRef("icon")}`]: {
|
|
||||||
color: theme.fn.variant({ variant: "dark", color: theme.primaryColor }).color,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const data = [
|
|
||||||
{ link: "", label: "Notifications", icon: IconBellRinging },
|
|
||||||
{ link: "", label: "Billing", icon: IconReceipt2 },
|
|
||||||
{ link: "", label: "Security", icon: IconFingerprint },
|
|
||||||
{ link: "", label: "SSH Keys", icon: IconKey },
|
|
||||||
{ link: "", label: "Databases", icon: IconDatabaseImport },
|
|
||||||
{ link: "", label: "Authentication", icon: Icon2fa },
|
|
||||||
{ link: "", label: "Other Settings", icon: IconSettings },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function AppNav(props: { children: React.ReactNode; title?: string }) {
|
export default function AppNav(props: { children: React.ReactNode; title?: string }) {
|
||||||
const { classes, cx } = useStyles();
|
|
||||||
const [active, setActive] = useState("Billing");
|
|
||||||
|
|
||||||
const links = data.map((item) => (
|
|
||||||
<a
|
|
||||||
className={cx(classes.link, { [classes.linkActive]: item.label === active })}
|
|
||||||
href={item.link}
|
|
||||||
key={item.label}
|
|
||||||
onClick={(event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
setActive(item.label);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<item.icon className={classes.linkIcon} stroke={1.5} />
|
|
||||||
<span>{item.label}</span>
|
|
||||||
</a>
|
|
||||||
));
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box mih="100vh" sx={{ display: "flex" }}>
|
<Flex minH="100vh" align="stretch">
|
||||||
<Head>
|
<Head>
|
||||||
<title>{props.title ? `${props.title} | Prompt Bench` : "Prompt Bench"}</title>
|
<title>{props.title ? `${props.title} | Prompt Bench` : "Prompt Bench"}</title>
|
||||||
</Head>
|
</Head>
|
||||||
<Navbar height="100vh" width={{ sm: 250 }} p="md" bg="gray.1">
|
{/* Placeholder for now */}
|
||||||
<Navbar.Section grow>
|
<Box bgColor="gray.100" height="100vh" width="200px" />
|
||||||
<Group className={classes.header} position="apart">
|
<Box flex={1}>{props.children}</Box>
|
||||||
<Code sx={{ fontWeight: 700 }}>v3.1.2</Code>
|
</Flex>
|
||||||
</Group>
|
|
||||||
{links}
|
|
||||||
</Navbar.Section>
|
|
||||||
|
|
||||||
<Navbar.Section className={classes.footer}>
|
|
||||||
<a href="#" className={classes.link} onClick={(event) => event.preventDefault()}>
|
|
||||||
<IconSwitchHorizontal className={classes.linkIcon} stroke={1.5} />
|
|
||||||
<span>Change account</span>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a href="#" className={classes.link} onClick={(event) => event.preventDefault()}>
|
|
||||||
<IconLogout className={classes.linkIcon} stroke={1.5} />
|
|
||||||
<span>Logout</span>
|
|
||||||
</a>
|
|
||||||
</Navbar.Section>
|
|
||||||
</Navbar>
|
|
||||||
<Box sx={{ flex: 1 }}>{props.children}</Box>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { type Session } from "next-auth";
|
|||||||
import { SessionProvider } from "next-auth/react";
|
import { SessionProvider } from "next-auth/react";
|
||||||
import { type AppType } from "next/app";
|
import { type AppType } from "next/app";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { MantineProvider } from "@mantine/core";
|
import { ChakraProvider } from "@chakra-ui/react";
|
||||||
import { Notifications } from "@mantine/notifications";
|
|
||||||
|
|
||||||
const MyApp: AppType<{ session: Session | null }> = ({
|
const MyApp: AppType<{ session: Session | null }> = ({
|
||||||
Component,
|
Component,
|
||||||
@@ -11,10 +10,9 @@ const MyApp: AppType<{ session: Session | null }> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<SessionProvider session={session}>
|
||||||
<MantineProvider withGlobalStyles withNormalizeCSS>
|
<ChakraProvider>
|
||||||
<Notifications position="bottom-center" />
|
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</MantineProvider>
|
</ChakraProvider>
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
import { createGetInitialProps } from "@mantine/next";
|
|
||||||
import Document, { Head, Html, Main, NextScript } from "next/document";
|
|
||||||
|
|
||||||
const getInitialProps = createGetInitialProps();
|
|
||||||
|
|
||||||
export default class _Document extends Document {
|
|
||||||
static getInitialProps = getInitialProps;
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<Html>
|
|
||||||
<Head />
|
|
||||||
<body>
|
|
||||||
<Main />
|
|
||||||
<NextScript />
|
|
||||||
</body>
|
|
||||||
</Html>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Box, Center } from "@mantine/core";
|
import { Box, Center } from "@chakra-ui/react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import OutputsTable from "~/components/OutputsTable";
|
import OutputsTable from "~/components/OutputsTable";
|
||||||
import AppNav from "~/components/nav/AppNav";
|
import AppNav from "~/components/nav/AppNav";
|
||||||
|
|||||||
Reference in New Issue
Block a user