prompt reordering works
This commit is contained in:
@@ -18,24 +18,21 @@ export default function NewVariantButton() {
|
|||||||
}, [mutation]);
|
}, [mutation]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip label="Add Prompt Variant" placement="right">
|
<Button
|
||||||
<Button
|
w="100%"
|
||||||
w="100%"
|
borderRadius={0}
|
||||||
borderRadius={0}
|
alignItems="center"
|
||||||
alignItems="center"
|
justifyContent="center"
|
||||||
justifyContent="center"
|
fontWeight="normal"
|
||||||
fontWeight="normal"
|
bgColor="transparent"
|
||||||
bgColor="transparent"
|
_hover={{ bgColor: "gray.100" }}
|
||||||
_hover={{ bgColor: "gray.100" }}
|
px={cellPadding.x}
|
||||||
px={cellPadding.x}
|
onClick={onClick}
|
||||||
// py={cellPadding.y}
|
height="unset"
|
||||||
onClick={onClick}
|
minH={headerMinHeight}
|
||||||
height="unset"
|
>
|
||||||
minH={headerMinHeight}
|
<BsPlus size={24} />
|
||||||
>
|
New Variant
|
||||||
<BsPlus size={24} />
|
</Button>
|
||||||
New Variant
|
|
||||||
</Button>
|
|
||||||
</Tooltip>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,11 +94,8 @@ export default function VariantConfigEditor(props: { variant: PromptVariant }) {
|
|||||||
wordWrap: "on",
|
wordWrap: "on",
|
||||||
folding: false,
|
folding: false,
|
||||||
scrollbar: {
|
scrollbar: {
|
||||||
vertical: "hidden",
|
|
||||||
alwaysConsumeMouseWheel: false,
|
alwaysConsumeMouseWheel: false,
|
||||||
verticalScrollbarSize: 0,
|
verticalScrollbarSize: 0,
|
||||||
|
|
||||||
// Don't let you scroll to an empty line
|
|
||||||
},
|
},
|
||||||
wordWrapBreakAfterCharacters: "",
|
wordWrapBreakAfterCharacters: "",
|
||||||
wordWrapBreakBeforeCharacters: "",
|
wordWrapBreakBeforeCharacters: "",
|
||||||
@@ -145,7 +142,7 @@ export default function VariantConfigEditor(props: { variant: PromptVariant }) {
|
|||||||
<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 && (
|
||||||
<HStack pos="absolute" bottom={0} right={0}>
|
<HStack pos="absolute" bottom={2} right={2}>
|
||||||
<Button
|
<Button
|
||||||
colorScheme="gray"
|
colorScheme="gray"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
import { useRef } from "react";
|
import { useRef, useState } from "react";
|
||||||
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 { Button, HStack, Heading, Tooltip } from "@chakra-ui/react";
|
import { Button, HStack, Input, Icon, Tooltip } from "@chakra-ui/react"; // Changed here
|
||||||
import { BsX } from "react-icons/bs";
|
import { BsX } from "react-icons/bs";
|
||||||
|
import { RiDraggable } from "react-icons/ri";
|
||||||
import { cellPadding, headerMinHeight } from "../constants";
|
import { cellPadding, headerMinHeight } from "../constants";
|
||||||
|
|
||||||
export default function VariantHeader(props: { variant: PromptVariant }) {
|
export default function VariantHeader(props: { variant: PromptVariant }) {
|
||||||
const utils = api.useContext();
|
const utils = api.useContext();
|
||||||
|
const [isDragTarget, setIsDragTarget] = useState(false);
|
||||||
|
const [label, setLabel] = useState(props.variant.label);
|
||||||
|
|
||||||
const labelRef = useRef<HTMLHeadingElement | null>(null);
|
|
||||||
const updateMutation = api.promptVariants.update.useMutation();
|
const updateMutation = api.promptVariants.update.useMutation();
|
||||||
const [onSaveLabel] = useHandledAsyncCallback(async () => {
|
const [onSaveLabel] = useHandledAsyncCallback(async () => {
|
||||||
const newLabel = labelRef.current?.innerText;
|
if (label && label !== props.variant.label) {
|
||||||
if (newLabel && newLabel !== props.variant.label) {
|
|
||||||
await updateMutation.mutateAsync({
|
await updateMutation.mutateAsync({
|
||||||
id: props.variant.id,
|
id: props.variant.id,
|
||||||
updates: { label: newLabel },
|
updates: { label: label },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [updateMutation, props.variant.id, props.variant.label]);
|
}, [updateMutation, props.variant.id, props.variant.label, label]);
|
||||||
|
|
||||||
const hideMutation = api.promptVariants.hide.useMutation();
|
const hideMutation = api.promptVariants.hide.useMutation();
|
||||||
const [onHide] = useHandledAsyncCallback(async () => {
|
const [onHide] = useHandledAsyncCallback(async () => {
|
||||||
@@ -29,36 +30,69 @@ export default function VariantHeader(props: { variant: PromptVariant }) {
|
|||||||
await utils.promptVariants.list.invalidate();
|
await utils.promptVariants.list.invalidate();
|
||||||
}, [hideMutation, props.variant.id]);
|
}, [hideMutation, props.variant.id]);
|
||||||
|
|
||||||
|
const reorderMutation = api.promptVariants.reorder.useMutation();
|
||||||
|
const [onReorder] = useHandledAsyncCallback(
|
||||||
|
async (e: DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("onDrop");
|
||||||
|
const draggedId = e.dataTransfer.getData("text/plain");
|
||||||
|
const droppedId = props.variant.id;
|
||||||
|
if (!draggedId || !droppedId || draggedId === droppedId) return;
|
||||||
|
await reorderMutation.mutateAsync({
|
||||||
|
draggedId,
|
||||||
|
droppedId,
|
||||||
|
});
|
||||||
|
await utils.promptVariants.list.invalidate();
|
||||||
|
},
|
||||||
|
[reorderMutation, props.variant.id]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack spacing={4} alignItems="center" minH={headerMinHeight}>
|
<HStack
|
||||||
<Heading
|
spacing={4}
|
||||||
fontWeight="bold"
|
alignItems="center"
|
||||||
size="md"
|
minH={headerMinHeight}
|
||||||
ref={labelRef}
|
draggable
|
||||||
contentEditable
|
onDragStart={(e) => {
|
||||||
suppressContentEditableWarning
|
e.dataTransfer.setData("text/plain", props.variant.id);
|
||||||
|
e.currentTarget.style.opacity = "0.4";
|
||||||
|
}}
|
||||||
|
onDragEnd={(e) => {
|
||||||
|
e.currentTarget.style.opacity = "1";
|
||||||
|
}}
|
||||||
|
onDragOver={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setIsDragTarget(true);
|
||||||
|
}}
|
||||||
|
onDragLeave={() => {
|
||||||
|
setIsDragTarget(false);
|
||||||
|
}}
|
||||||
|
onDrop={onReorder}
|
||||||
|
backgroundColor={isDragTarget ? "gray.100" : "transparent"}
|
||||||
|
>
|
||||||
|
<Icon
|
||||||
|
as={RiDraggable}
|
||||||
|
boxSize={6}
|
||||||
|
color="gray.400"
|
||||||
|
_hover={{ color: "gray.800", cursor: "pointer" }}
|
||||||
|
/>
|
||||||
|
<Input // Changed to Input
|
||||||
|
size="sm"
|
||||||
|
value={label}
|
||||||
|
onChange={(e) => setLabel(e.target.value)}
|
||||||
|
onBlur={onSaveLabel}
|
||||||
borderWidth={1}
|
borderWidth={1}
|
||||||
borderColor="transparent"
|
borderColor="transparent"
|
||||||
|
fontWeight="bold"
|
||||||
|
fontSize={16}
|
||||||
_hover={{ borderColor: "gray.300" }}
|
_hover={{ borderColor: "gray.300" }}
|
||||||
_focus={{ borderColor: "blue.500", outline: "none" }}
|
_focus={{ borderColor: "blue.500", outline: "none" }}
|
||||||
onBlur={onSaveLabel}
|
|
||||||
flex={1}
|
flex={1}
|
||||||
px={cellPadding.x}
|
px={cellPadding.x}
|
||||||
// py={cellPadding.y}
|
/>
|
||||||
>
|
|
||||||
{props.variant.label}
|
|
||||||
</Heading>
|
|
||||||
<Tooltip label="Hide Variant" hasArrow>
|
<Tooltip label="Hide Variant" hasArrow>
|
||||||
<Button
|
<Button variant="ghost" colorScheme="gray" size="sm" onClick={onHide} borderRadius={0}>
|
||||||
variant="ghost"
|
<Icon as={BsX} boxSize={6} />
|
||||||
colorScheme="gray"
|
|
||||||
size="sm"
|
|
||||||
onClick={onHide}
|
|
||||||
borderRadius={0}
|
|
||||||
// px={cellPadding.x}
|
|
||||||
// py={cellPadding.y}
|
|
||||||
>
|
|
||||||
<BsX size={24} />
|
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import { RouterOutputs, api } from "~/utils/api";
|
import { Box, Grid, GridItem, Heading, type SystemStyleObject } from "@chakra-ui/react";
|
||||||
import { Scenario, type PromptVariant } from "./types";
|
|
||||||
import OutputCell from "./OutputCell";
|
|
||||||
import ScenarioEditor from "./ScenarioEditor";
|
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Box, Grid, GridItem, Heading, SystemStyleObject } from "@chakra-ui/react";
|
import { api } from "~/utils/api";
|
||||||
import NewScenarioButton from "./NewScenarioButton";
|
import NewScenarioButton from "./NewScenarioButton";
|
||||||
import NewVariantButton from "./NewVariantButton";
|
import NewVariantButton from "./NewVariantButton";
|
||||||
import VariantHeader from "./VariantHeader";
|
import OutputCell from "./OutputCell";
|
||||||
|
import ScenarioEditor from "./ScenarioEditor";
|
||||||
import VariantConfigEditor from "./VariantConfigEditor";
|
import VariantConfigEditor from "./VariantConfigEditor";
|
||||||
|
import VariantHeader from "./VariantHeader";
|
||||||
|
import type { Scenario, PromptVariant } from "./types";
|
||||||
|
import { cellPadding } from "../constants";
|
||||||
|
|
||||||
const stickyHeaderStyle: SystemStyleObject = {
|
const stickyHeaderStyle: SystemStyleObject = {
|
||||||
position: "sticky",
|
position: "sticky",
|
||||||
@@ -76,8 +77,8 @@ export default function OutputsTable({ experimentId }: { experimentId: string |
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<GridItem display="flex" alignItems="flex-end" rowSpan={2}>
|
<GridItem display="flex" alignItems="flex-end" rowSpan={2}>
|
||||||
<Box sx={stickyHeaderStyle} flex={1}>
|
<Box sx={stickyHeaderStyle} flex={1} px={cellPadding.x} py={cellPadding.y}>
|
||||||
<Heading size="md" fontWeight="bold">
|
<Heading size="sm" fontWeight="bold">
|
||||||
Scenario
|
Scenario
|
||||||
</Heading>
|
</Heading>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -143,4 +143,73 @@ export const promptVariantsRouter = createTRPCRouter({
|
|||||||
|
|
||||||
return newVariant;
|
return newVariant;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
reorder: publicProcedure
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
draggedId: z.string(),
|
||||||
|
droppedId: z.string(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.mutation(async ({ input }) => {
|
||||||
|
const dragged = await prisma.promptVariant.findUnique({
|
||||||
|
where: {
|
||||||
|
id: input.draggedId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const dropped = await prisma.promptVariant.findUnique({
|
||||||
|
where: {
|
||||||
|
id: input.droppedId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!dragged || !dropped || dragged.experimentId !== dropped.experimentId) {
|
||||||
|
throw new Error(
|
||||||
|
`Prompt Variant with id ${input.draggedId} or ${input.droppedId} does not exist`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const visibleItems = await prisma.promptVariant.findMany({
|
||||||
|
where: {
|
||||||
|
experimentId: dragged.experimentId,
|
||||||
|
visible: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
sortIndex: "asc",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove the dragged item from its current position
|
||||||
|
const orderedItems = visibleItems.filter((item) => item.id !== dragged.id);
|
||||||
|
|
||||||
|
// Find the index of the dragged item and the dropped item
|
||||||
|
const dragIndex = visibleItems.findIndex((item) => item.id === dragged.id);
|
||||||
|
const dropIndex = orderedItems.findIndex((item) => item.id === dropped.id);
|
||||||
|
|
||||||
|
// Determine the new index for the dragged item
|
||||||
|
let newIndex;
|
||||||
|
if (dragIndex < dropIndex) {
|
||||||
|
newIndex = dropIndex + 1; // Insert after the dropped item
|
||||||
|
} else {
|
||||||
|
newIndex = dropIndex; // Insert before the dropped item
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert the dragged item at the new position
|
||||||
|
orderedItems.splice(newIndex, 0, dragged);
|
||||||
|
|
||||||
|
// Now, we need to update all the items with their new sortIndex
|
||||||
|
await prisma.$transaction(
|
||||||
|
orderedItems.map((item, index) => {
|
||||||
|
return prisma.promptVariant.update({
|
||||||
|
where: {
|
||||||
|
id: item.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
sortIndex: index,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user