prettifying the ux
This commit is contained in:
@@ -16,8 +16,6 @@
|
|||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/server": "^11.11.0",
|
"@emotion/server": "^11.11.0",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@fontsource/poppins": "^5.0.3",
|
|
||||||
"@fontsource/roboto": "^5.0.3",
|
|
||||||
"@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",
|
||||||
|
|||||||
14
pnpm-lock.yaml
generated
14
pnpm-lock.yaml
generated
@@ -20,12 +20,6 @@ dependencies:
|
|||||||
'@emotion/styled':
|
'@emotion/styled':
|
||||||
specifier: ^11.11.0
|
specifier: ^11.11.0
|
||||||
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.6)(react@18.2.0)
|
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.6)(react@18.2.0)
|
||||||
'@fontsource/poppins':
|
|
||||||
specifier: ^5.0.3
|
|
||||||
version: 5.0.3
|
|
||||||
'@fontsource/roboto':
|
|
||||||
specifier: ^5.0.3
|
|
||||||
version: 5.0.3
|
|
||||||
'@monaco-editor/react':
|
'@monaco-editor/react':
|
||||||
specifier: ^4.5.1
|
specifier: ^4.5.1
|
||||||
version: 4.5.1(monaco-editor@0.39.0)(react-dom@18.2.0)(react@18.2.0)
|
version: 4.5.1(monaco-editor@0.39.0)(react-dom@18.2.0)(react@18.2.0)
|
||||||
@@ -1682,14 +1676,6 @@ packages:
|
|||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@fontsource/poppins@5.0.3:
|
|
||||||
resolution: {integrity: sha512-5lL2vmvmh4zbknTh1nVH9pTWyhYqAlYMHIAnkNhqo5qwMZGlr+coM1dtMwiQHRBgmHAl3ZvJ35Bj0s8cpmXZbg==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@fontsource/roboto@5.0.3:
|
|
||||||
resolution: {integrity: sha512-jbZDFwEFARDlo8TqG7th/xjhuq87GYfFpFb+uxuy+0Ng1bhRVgBRWlLj8+WIKhCTOr+h4QXbjpybLWFLUirOwQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@humanwhocodes/config-array@0.11.10:
|
/@humanwhocodes/config-array@0.11.10:
|
||||||
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
|
resolution: {integrity: sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==}
|
||||||
engines: {node: '>=10.10.0'}
|
engines: {node: '>=10.10.0'}
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
import { useRef } from "react";
|
|
||||||
import { type PromptVariant } from "./types";
|
|
||||||
import { api } from "~/utils/api";
|
|
||||||
import { useHandledAsyncCallback } from "~/utils/hooks";
|
|
||||||
import { Heading } from "@chakra-ui/react";
|
|
||||||
|
|
||||||
export default function EditableVariantLabel(props: { variant: PromptVariant }) {
|
|
||||||
const labelRef = useRef<HTMLHeadingElement | null>(null);
|
|
||||||
|
|
||||||
const mutation = api.promptVariants.update.useMutation();
|
|
||||||
|
|
||||||
const [onBlur] = useHandledAsyncCallback(async () => {
|
|
||||||
const newLabel = labelRef.current?.innerText;
|
|
||||||
if (newLabel && newLabel !== props.variant.label) {
|
|
||||||
await mutation.mutateAsync({
|
|
||||||
id: props.variant.id,
|
|
||||||
updates: { label: newLabel },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [mutation, props.variant.id, props.variant.label]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Heading
|
|
||||||
fontWeight="bold"
|
|
||||||
size="md"
|
|
||||||
ref={labelRef}
|
|
||||||
contentEditable
|
|
||||||
suppressContentEditableWarning
|
|
||||||
borderWidth={1}
|
|
||||||
borderColor="transparent"
|
|
||||||
_hover={{ borderColor: "gray.300" }}
|
|
||||||
_focus={{ borderColor: "blue.500", outline: "none" }}
|
|
||||||
onBlur={onBlur}
|
|
||||||
px={4}
|
|
||||||
py={2}
|
|
||||||
>
|
|
||||||
{props.variant.label}
|
|
||||||
</Heading>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ import { Button, Tooltip } from "@chakra-ui/react";
|
|||||||
import { BsPlus } from "react-icons/bs";
|
import { BsPlus } from "react-icons/bs";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
|
import { useExperiment, useHandledAsyncCallback } from "~/utils/hooks";
|
||||||
|
import { cellPadding, headerMinHeight } from "../constants";
|
||||||
|
|
||||||
export default function NewVariantButton() {
|
export default function NewVariantButton() {
|
||||||
const experiment = useExperiment();
|
const experiment = useExperiment();
|
||||||
@@ -21,16 +22,19 @@ export default function NewVariantButton() {
|
|||||||
<Button
|
<Button
|
||||||
w="100%"
|
w="100%"
|
||||||
borderRadius={0}
|
borderRadius={0}
|
||||||
alignItems="flex-start"
|
alignItems="center"
|
||||||
justifyContent="center"
|
justifyContent="center"
|
||||||
fontWeight="normal"
|
fontWeight="normal"
|
||||||
bgColor="blue.100"
|
bgColor="transparent"
|
||||||
_hover={{ bgColor: "blue.200" }}
|
_hover={{ bgColor: "gray.100" }}
|
||||||
py={2}
|
px={cellPadding.x}
|
||||||
px={0}
|
// py={cellPadding.y}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
|
height="unset"
|
||||||
|
minH={headerMinHeight}
|
||||||
>
|
>
|
||||||
<BsPlus size={24} />
|
<BsPlus size={24} />
|
||||||
|
New Variant
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,13 @@ import { PromptVariant, Scenario } from "./types";
|
|||||||
import { Center, Spinner, Text } from "@chakra-ui/react";
|
import { Center, Spinner, Text } from "@chakra-ui/react";
|
||||||
import { useExperiment } from "~/utils/hooks";
|
import { useExperiment } from "~/utils/hooks";
|
||||||
import { JSONSerializable } from "~/server/types";
|
import { JSONSerializable } from "~/server/types";
|
||||||
|
import { cellPadding } from "../constants";
|
||||||
|
|
||||||
|
const CellShell = ({ children }: { children: React.ReactNode }) => (
|
||||||
|
<Center h="100%" w="100%" px={cellPadding.x} py={cellPadding.y}>
|
||||||
|
{children}
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
|
||||||
export default function OutputCell({
|
export default function OutputCell({
|
||||||
scenario,
|
scenario,
|
||||||
@@ -36,23 +43,21 @@ export default function OutputCell({
|
|||||||
|
|
||||||
if (disabledReason)
|
if (disabledReason)
|
||||||
return (
|
return (
|
||||||
<Center h="100%">
|
<CellShell>
|
||||||
<Text color="gray.500">{disabledReason}</Text>
|
<Text color="gray.500">{disabledReason}</Text>
|
||||||
</Center>
|
</CellShell>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (output.isLoading)
|
if (output.isLoading)
|
||||||
return (
|
return (
|
||||||
<Center h="100%">
|
<CellShell>
|
||||||
<Spinner />
|
<Spinner />
|
||||||
</Center>
|
</CellShell>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!output.data) return <Center h="100%">No output</Center>;
|
if (!output.data) return <CellShell>No output</CellShell>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Center h="100%">
|
<CellShell>{JSON.stringify(output.data.output.choices[0].message.content, null, 2)}</CellShell>
|
||||||
{JSON.stringify(output.data.output.choices[0].message.content, null, 2)}
|
|
||||||
</Center>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ import { useState } from "react";
|
|||||||
import ResizeTextarea from "react-textarea-autosize";
|
import ResizeTextarea from "react-textarea-autosize";
|
||||||
|
|
||||||
import { Box, Button, Flex, HStack, Stack, Textarea } from "@chakra-ui/react";
|
import { Box, Button, Flex, HStack, Stack, Textarea } from "@chakra-ui/react";
|
||||||
|
import { cellPadding } from "../constants";
|
||||||
|
|
||||||
export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
|
export default function ScenarioEditor({ scenario }: { scenario: Scenario }) {
|
||||||
const savedValues = scenario.variableValues as Record<string, string>;
|
const savedValues = scenario.variableValues as Record<string, string>;
|
||||||
const utils = api.useContext();
|
const utils = api.useContext();
|
||||||
|
|
||||||
@@ -30,7 +31,7 @@ export default function ScenarioHeader({ scenario }: { scenario: Scenario }) {
|
|||||||
}, [mutation, values]);
|
}, [mutation, values]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack px={cellPadding.x} py={cellPadding.y}>
|
||||||
{variableLabels.map((key) => {
|
{variableLabels.map((key) => {
|
||||||
const value = values[key] ?? "";
|
const value = values[key] ?? "";
|
||||||
const layoutDirection = value.length > 20 ? "column" : "row";
|
const layoutDirection = value.length > 20 ? "column" : "row";
|
||||||
@@ -1,53 +1,66 @@
|
|||||||
import { useCallback } from "react";
|
import { useRef } from "react";
|
||||||
import type { PromptVariant } from "./types";
|
import { type PromptVariant } from "./types";
|
||||||
import { api } from "~/utils/api";
|
import { api } from "~/utils/api";
|
||||||
import { type JSONSerializable } from "~/server/types";
|
import { useHandledAsyncCallback } from "~/utils/hooks";
|
||||||
import VariantConfigEditor from "./VariantConfigEditor";
|
import { Button, HStack, Heading, Tooltip } from "@chakra-ui/react";
|
||||||
import EditableVariantLabel from "./EditableVariantLabel";
|
import { BsX } from "react-icons/bs";
|
||||||
import { Stack, useToast } from "@chakra-ui/react";
|
import { cellPadding, headerMinHeight } from "../constants";
|
||||||
|
|
||||||
export default function VariantHeader({ variant }: { variant: PromptVariant }) {
|
export default function VariantHeader(props: { variant: PromptVariant }) {
|
||||||
const replaceWithConfig = api.promptVariants.replaceWithConfig.useMutation();
|
|
||||||
const utils = api.useContext();
|
const utils = api.useContext();
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
const onSave = useCallback(
|
const labelRef = useRef<HTMLHeadingElement | null>(null);
|
||||||
async (currentConfig: string) => {
|
const updateMutation = api.promptVariants.update.useMutation();
|
||||||
let parsedConfig: JSONSerializable;
|
const [onSaveLabel] = useHandledAsyncCallback(async () => {
|
||||||
try {
|
const newLabel = labelRef.current?.innerText;
|
||||||
parsedConfig = JSON.parse(currentConfig) as JSONSerializable;
|
if (newLabel && newLabel !== props.variant.label) {
|
||||||
} catch (e) {
|
await updateMutation.mutateAsync({
|
||||||
toast({
|
id: props.variant.id,
|
||||||
title: "Invalid JSON",
|
updates: { label: newLabel },
|
||||||
description: "Please fix the JSON before saving.",
|
|
||||||
status: "error",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsedConfig === null) {
|
|
||||||
toast({
|
|
||||||
title: "Invalid JSON",
|
|
||||||
description: "Please fix the JSON before saving.",
|
|
||||||
status: "error",
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await replaceWithConfig.mutateAsync({
|
|
||||||
id: variant.id,
|
|
||||||
config: currentConfig,
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}, [updateMutation, props.variant.id, props.variant.label]);
|
||||||
|
|
||||||
await utils.promptVariants.list.invalidate();
|
const hideMutation = api.promptVariants.hide.useMutation();
|
||||||
},
|
const [onHide] = useHandledAsyncCallback(async () => {
|
||||||
[variant.id, replaceWithConfig, utils.promptVariants.list, toast]
|
await hideMutation.mutateAsync({
|
||||||
);
|
id: props.variant.id,
|
||||||
|
});
|
||||||
|
await utils.promptVariants.list.invalidate();
|
||||||
|
}, [hideMutation, props.variant.id]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack w="100%">
|
<HStack spacing={4} alignItems="center" minH={headerMinHeight}>
|
||||||
<EditableVariantLabel variant={variant} />
|
<Heading
|
||||||
<VariantConfigEditor variant={variant} />
|
fontWeight="bold"
|
||||||
</Stack>
|
size="md"
|
||||||
|
ref={labelRef}
|
||||||
|
contentEditable
|
||||||
|
suppressContentEditableWarning
|
||||||
|
borderWidth={1}
|
||||||
|
borderColor="transparent"
|
||||||
|
_hover={{ borderColor: "gray.300" }}
|
||||||
|
_focus={{ borderColor: "blue.500", outline: "none" }}
|
||||||
|
onBlur={onSaveLabel}
|
||||||
|
flex={1}
|
||||||
|
px={cellPadding.x}
|
||||||
|
// py={cellPadding.y}
|
||||||
|
>
|
||||||
|
{props.variant.label}
|
||||||
|
</Heading>
|
||||||
|
<Tooltip label="Hide Variant" hasArrow>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
colorScheme="gray"
|
||||||
|
size="sm"
|
||||||
|
onClick={onHide}
|
||||||
|
borderRadius={0}
|
||||||
|
// px={cellPadding.x}
|
||||||
|
// py={cellPadding.y}
|
||||||
|
>
|
||||||
|
<BsX size={24} />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
|
</HStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import { RouterOutputs, api } from "~/utils/api";
|
import { RouterOutputs, api } from "~/utils/api";
|
||||||
import { Scenario, type PromptVariant } from "./types";
|
import { Scenario, type PromptVariant } from "./types";
|
||||||
import VariantHeader from "./VariantHeader";
|
|
||||||
import OutputCell from "./OutputCell";
|
import OutputCell from "./OutputCell";
|
||||||
import ScenarioHeader from "./ScenarioHeader";
|
import ScenarioEditor from "./ScenarioEditor";
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Box, Grid, GridItem, Heading } from "@chakra-ui/react";
|
import { Box, Grid, GridItem, Heading, SystemStyleObject } from "@chakra-ui/react";
|
||||||
import NewScenarioButton from "./NewScenarioButton";
|
import NewScenarioButton from "./NewScenarioButton";
|
||||||
import NewVariantButton from "./NewVariantButton";
|
import NewVariantButton from "./NewVariantButton";
|
||||||
import EditableVariantLabel from "./EditableVariantLabel";
|
import VariantHeader from "./VariantHeader";
|
||||||
import VariantConfigEditor from "./VariantConfigEditor";
|
import VariantConfigEditor from "./VariantConfigEditor";
|
||||||
|
|
||||||
|
const stickyHeaderStyle: SystemStyleObject = {
|
||||||
|
position: "sticky",
|
||||||
|
top: 0,
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
zIndex: 1,
|
||||||
|
};
|
||||||
|
|
||||||
const ScenarioRow = (props: { scenario: Scenario; variants: PromptVariant[] }) => {
|
const ScenarioRow = (props: { scenario: Scenario; variants: PromptVariant[] }) => {
|
||||||
const [isHovered, setIsHovered] = useState(false);
|
const [isHovered, setIsHovered] = useState(false);
|
||||||
|
|
||||||
@@ -22,16 +28,16 @@ const ScenarioRow = (props: { scenario: Scenario; variants: PromptVariant[] }) =
|
|||||||
<GridItem
|
<GridItem
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
sx={isHovered ? highlightStyle : null}
|
sx={isHovered ? highlightStyle : undefined}
|
||||||
>
|
>
|
||||||
<ScenarioHeader scenario={props.scenario} />
|
<ScenarioEditor scenario={props.scenario} />
|
||||||
</GridItem>
|
</GridItem>
|
||||||
{props.variants.map((variant) => (
|
{props.variants.map((variant) => (
|
||||||
<GridItem
|
<GridItem
|
||||||
key={variant.id}
|
key={variant.id}
|
||||||
onMouseEnter={() => setIsHovered(true)}
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
onMouseLeave={() => setIsHovered(false)}
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
sx={isHovered ? highlightStyle : null}
|
sx={isHovered ? highlightStyle : undefined}
|
||||||
>
|
>
|
||||||
<OutputCell key={variant.id} scenario={props.scenario} variant={variant} />
|
<OutputCell key={variant.id} scenario={props.scenario} variant={variant} />
|
||||||
</GridItem>
|
</GridItem>
|
||||||
@@ -57,14 +63,12 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
|||||||
<Grid
|
<Grid
|
||||||
p={4}
|
p={4}
|
||||||
display="grid"
|
display="grid"
|
||||||
gridTemplateColumns={`200px repeat(${variants.data.length}, minmax(300px, 1fr)) 40px`}
|
gridTemplateColumns={`200px repeat(${variants.data.length}, minmax(300px, 1fr)) auto`}
|
||||||
sx={{
|
sx={{
|
||||||
"> *": {
|
"> *": {
|
||||||
borderColor: "gray.300",
|
borderColor: "gray.300",
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
borderRightWidth: 1,
|
borderRightWidth: 1,
|
||||||
paddingX: 4,
|
|
||||||
paddingY: 2,
|
|
||||||
},
|
},
|
||||||
"> *:last-child": {
|
"> *:last-child": {
|
||||||
borderRightWidth: 0,
|
borderRightWidth: 0,
|
||||||
@@ -72,25 +76,24 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<GridItem display="flex" alignItems="flex-end" rowSpan={2}>
|
<GridItem display="flex" alignItems="flex-end" rowSpan={2}>
|
||||||
<Heading size="md" fontWeight="bold">
|
<Box sx={stickyHeaderStyle} flex={1}>
|
||||||
Scenario
|
<Heading size="md" fontWeight="bold">
|
||||||
</Heading>
|
Scenario
|
||||||
|
</Heading>
|
||||||
|
</Box>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
{variants.data.map((variant) => (
|
{variants.data.map((variant) => (
|
||||||
<GridItem
|
<GridItem key={variant.uiId} padding={0} sx={stickyHeaderStyle}>
|
||||||
key={variant.uiId}
|
<VariantHeader variant={variant} />
|
||||||
padding={0}
|
|
||||||
sx={{ position: "sticky", top: 0, backgroundColor: "#fff", zIndex: 1 }}
|
|
||||||
>
|
|
||||||
<EditableVariantLabel variant={variant} />
|
|
||||||
</GridItem>
|
</GridItem>
|
||||||
))}
|
))}
|
||||||
<GridItem
|
<GridItem
|
||||||
borderBottomWidth={0}
|
|
||||||
rowSpan={scenarios.data.length + 1}
|
rowSpan={scenarios.data.length + 1}
|
||||||
padding={0}
|
padding={0}
|
||||||
borderRightWidth={0}
|
// Have to use `style` instead of emotion style props to work around css specificity issues conflicting with the "> *" selector on Grid
|
||||||
sx={{ position: "sticky", top: 0, backgroundColor: "#fff", zIndex: 1 }}
|
style={{ borderRightWidth: 0, borderBottomWidth: 0 }}
|
||||||
|
sx={stickyHeaderStyle}
|
||||||
|
className="new-variant-button"
|
||||||
>
|
>
|
||||||
<NewVariantButton />
|
<NewVariantButton />
|
||||||
</GridItem>
|
</GridItem>
|
||||||
|
|||||||
6
src/components/constants.ts
Normal file
6
src/components/constants.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export const cellPadding = {
|
||||||
|
x: 4,
|
||||||
|
y: 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const headerMinHeight = 8;
|
||||||
@@ -3,13 +3,17 @@ import Head from "next/head";
|
|||||||
|
|
||||||
export default function AppNav(props: { children: React.ReactNode; title?: string }) {
|
export default function AppNav(props: { children: React.ReactNode; title?: string }) {
|
||||||
return (
|
return (
|
||||||
<Flex minH="100vh" align="stretch">
|
<Flex minH="100vh">
|
||||||
<Head>
|
<Head>
|
||||||
<title>{props.title ? `${props.title} | Prompt Bench` : "Prompt Bench"}</title>
|
<title>{props.title ? `${props.title} | Prompt Bench` : "Prompt Bench"}</title>
|
||||||
</Head>
|
</Head>
|
||||||
{/* Placeholder for now */}
|
{/* Placeholder for now */}
|
||||||
<Box bgColor="gray.100" height="100vh" width="200px" />
|
<Box bgColor="gray.100" flexShrink={0} width="200px">
|
||||||
<Box flex={1}>{props.children}</Box>
|
Nav Sidebar
|
||||||
|
</Box>
|
||||||
|
<Box flex={1} overflowX="auto" overflowY="auto" h="100vh">
|
||||||
|
{props.children}
|
||||||
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
import styles from "./index.module.css";
|
|
||||||
import { signIn, signOut, useSession } from "next-auth/react";
|
|
||||||
import Head from "next/head";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { api } from "~/utils/api";
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
const hello = api.example.hello.useQuery({ text: "from tRPC" });
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Head>
|
|
||||||
<title>Create T3 App</title>
|
|
||||||
<meta name="description" content="Generated by create-t3-app" />
|
|
||||||
<link rel="icon" href="/favicon.ico" />
|
|
||||||
</Head>
|
|
||||||
<main className={styles.main}>
|
|
||||||
<div className={styles.container}>
|
|
||||||
<h1 className={styles.title}>
|
|
||||||
Create <span className={styles.pinkSpan}>T3</span> App
|
|
||||||
</h1>
|
|
||||||
<div className={styles.cardRow}>
|
|
||||||
<Link
|
|
||||||
className={styles.card}
|
|
||||||
href="https://create.t3.gg/en/usage/first-steps"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<h3 className={styles.cardTitle}>First Steps →</h3>
|
|
||||||
<div className={styles.cardText}>
|
|
||||||
Just the basics - Everything you need to know to set up your database and
|
|
||||||
authentication.
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
<Link
|
|
||||||
className={styles.card}
|
|
||||||
href="https://create.t3.gg/en/introduction"
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
<h3 className={styles.cardTitle}>Documentation →</h3>
|
|
||||||
<div className={styles.cardText}>
|
|
||||||
Learn more about Create T3 App, the libraries it uses, and how to deploy it.
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className={styles.showcaseContainer}>
|
|
||||||
<p className={styles.showcaseText}>
|
|
||||||
{hello.data ? hello.data.greeting : "Loading tRPC query..."}
|
|
||||||
</p>
|
|
||||||
<AuthShowcase />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function AuthShowcase() {
|
|
||||||
const { data: sessionData } = useSession();
|
|
||||||
|
|
||||||
const { data: secretMessage } = api.example.getSecretMessage.useQuery(
|
|
||||||
undefined, // no input
|
|
||||||
{ enabled: sessionData?.user !== undefined }
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.authContainer}>
|
|
||||||
<p className={styles.showcaseText}>
|
|
||||||
{sessionData && <span>Logged in as {sessionData.user?.name}</span>}
|
|
||||||
{secretMessage && <span> - {secretMessage}</span>}
|
|
||||||
</p>
|
|
||||||
<button
|
|
||||||
className={styles.loginButton}
|
|
||||||
onClick={sessionData ? () => void signOut() : () => void signIn()}
|
|
||||||
>
|
|
||||||
{sessionData ? "Sign out" : "Sign in"}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -75,6 +75,23 @@ export const promptVariantsRouter = createTRPCRouter({
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
hide: publicProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
id: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
return await prisma.promptVariant.update({
|
||||||
|
where: {
|
||||||
|
id: input.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
visible: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
replaceWithConfig: publicProcedure
|
replaceWithConfig: publicProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import { extendTheme } from "@chakra-ui/react";
|
import { extendTheme } from "@chakra-ui/react";
|
||||||
import "@fontsource/poppins";
|
|
||||||
import "@fontsource/roboto";
|
const systemFont =
|
||||||
|
'ui-sans-serif, -apple-system, "system-ui", "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"';
|
||||||
|
|
||||||
const theme = extendTheme({
|
const theme = extendTheme({
|
||||||
fonts: {
|
fonts: {
|
||||||
heading: "Poppins, sans-serif",
|
heading: systemFont,
|
||||||
body: "Roboto, sans-serif",
|
body: systemFont,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user