Add FileUploadsCard in bottom right corner

This commit is contained in:
David Corbitt
2023-09-07 09:03:32 -07:00
parent c9a53c2b94
commit ed25dcce39
6 changed files with 174 additions and 106 deletions

View File

@@ -1,68 +0,0 @@
import { VStack, HStack, Button, Text, Card, Progress, IconButton } from "@chakra-ui/react";
import { BsX } from "react-icons/bs";
import { type RouterOutputs, api } from "~/utils/api";
import { useHandledAsyncCallback } from "~/utils/hooks";
import { formatFileSize } from "~/utils/utils";
type FileUpload = RouterOutputs["datasets"]["listFileUploads"][0];
const FileUploadCard = ({ fileUpload }: { fileUpload: FileUpload }) => {
const { id, fileName, fileSize, progress, status, errorMessage } = fileUpload;
const utils = api.useContext();
const hideFileUploadMutation = api.datasets.hideFileUpload.useMutation();
const [hideFileUpload, hidingInProgress] = useHandledAsyncCallback(async () => {
await hideFileUploadMutation.mutateAsync({ fileUploadId: id });
await utils.datasets.listFileUploads.invalidate();
}, [id, hideFileUploadMutation, utils]);
const [refreshDatasetEntries] = useHandledAsyncCallback(async () => {
await utils.datasetEntries.list.invalidate();
}, [utils]);
return (
<Card w="full">
<VStack w="full" alignItems="flex-start" p={4}>
<HStack w="full" justifyContent="space-between">
<Text fontWeight="bold">
Uploading {fileName} ({formatFileSize(fileSize, 2)})
</Text>
<HStack spacing={0}>
{status === "COMPLETE" && (
<Button variant="ghost" onClick={refreshDatasetEntries} color="orange.400" size="xs">
Refresh Table
</Button>
)}
<IconButton
aria-label="Hide file upload"
as={BsX}
boxSize={6}
minW={0}
variant="ghost"
isLoading={hidingInProgress}
onClick={hideFileUpload}
cursor="pointer"
/>
</HStack>
</HStack>
{errorMessage ? (
<Text alignSelf="center" pt={2}>
{errorMessage}
</Text>
) : (
<>
<Text alignSelf="center" fontSize="xs">
{status} ({progress}%)
</Text>
<Progress w="full" value={progress} borderRadius={2} />
</>
)}
</VStack>
</Card>
);
};
export default FileUploadCard;

View File

@@ -0,0 +1,139 @@
import { useState, useEffect } from "react";
import {
VStack,
HStack,
Button,
Text,
Progress,
IconButton,
Portal,
Spinner,
} from "@chakra-ui/react";
import { BsX } from "react-icons/bs";
import { type RouterOutputs, api } from "~/utils/api";
import { useDataset, useHandledAsyncCallback } from "~/utils/hooks";
import { formatFileSize } from "~/utils/utils";
type FileUpload = RouterOutputs["datasets"]["listFileUploads"][0];
const FileUploadsCard = () => {
const dataset = useDataset();
const [fileUploadsRefetchInterval, setFileUploadsRefetchInterval] = useState<number>(500);
const fileUploads = api.datasets.listFileUploads.useQuery(
{ datasetId: dataset.data?.id as string },
{ enabled: !!dataset.data?.id, refetchInterval: fileUploadsRefetchInterval },
);
useEffect(() => {
if (fileUploads?.data?.some((fu) => fu.status !== "COMPLETE" && fu.status !== "ERROR")) {
setFileUploadsRefetchInterval(500);
} else {
setFileUploadsRefetchInterval(15000);
}
}, [fileUploads]);
const utils = api.useContext();
const hideFileUploadsMutation = api.datasets.hideFileUploads.useMutation();
const [hideAllFileUploads, hidingInProgress] = useHandledAsyncCallback(async () => {
if (!fileUploads.data?.length) return;
await hideFileUploadsMutation.mutateAsync({
fileUploadIds: fileUploads.data.map((upload) => upload.id),
});
await utils.datasets.listFileUploads.invalidate();
}, [hideFileUploadsMutation, fileUploads.data, utils]);
if (!fileUploads.data?.length) return null;
return (
<Portal>
<VStack
w={72}
borderRadius={8}
position="fixed"
bottom={8}
right={8}
overflow="hidden"
borderWidth={1}
boxShadow="0 0 40px 4px rgba(0, 0, 0, 0.1);"
minW={0}
bgColor="white"
>
<HStack p={4} w="full" bgColor="gray.200" justifyContent="space-between">
<Text fontWeight="bold">Uploads</Text>
<IconButton
aria-label="Close uploads"
as={hidingInProgress ? Spinner : BsX}
boxSize={6}
minW={0}
variant="ghost"
onClick={hideAllFileUploads}
cursor="pointer"
/>
</HStack>
{fileUploads?.data?.map((upload) => <FileUploadRow key={upload.id} fileUpload={upload} />)}
</VStack>
</Portal>
);
};
export default FileUploadsCard;
const FileUploadRow = ({ fileUpload }: { fileUpload: FileUpload }) => {
const { id, fileName, fileSize, progress, status, errorMessage } = fileUpload;
const utils = api.useContext();
const hideFileUploadsMutation = api.datasets.hideFileUploads.useMutation();
const [hideFileUpload, hidingInProgress] = useHandledAsyncCallback(async () => {
await hideFileUploadsMutation.mutateAsync({ fileUploadIds: [id] });
}, [id, hideFileUploadsMutation, utils]);
const [refreshDatasetEntries] = useHandledAsyncCallback(async () => {
await hideFileUploadsMutation.mutateAsync({ fileUploadIds: [id] });
await utils.datasetEntries.list.invalidate();
}, [id, hideFileUploadsMutation, utils]);
return (
<VStack w="full" alignItems="flex-start" p={4} borderBottomWidth={1}>
<HStack w="full" justifyContent="space-between" alignItems="flex-start">
<VStack alignItems="flex-start" spacing={0}>
<Text fontWeight="bold">{fileName}</Text>
<Text fontSize="xs">({formatFileSize(fileSize, 2)})</Text>
</VStack>
<HStack spacing={0}>
{status === "COMPLETE" ? (
<Button variant="ghost" onClick={refreshDatasetEntries} color="orange.400" size="xs">
Refresh Table
</Button>
) : (
<IconButton
aria-label="Hide file upload"
as={BsX}
boxSize={6}
minW={0}
variant="ghost"
isLoading={hidingInProgress}
onClick={hideFileUpload}
cursor="pointer"
/>
)}
</HStack>
</HStack>
{errorMessage ? (
<Text alignSelf="center" pt={2}>
{errorMessage}
</Text>
) : (
<>
<Text alignSelf="center" fontSize="xs">
{status}
</Text>
<Progress w="full" value={progress} borderRadius={2} />
</>
)}
</VStack>
);
};

View File

@@ -26,26 +26,26 @@ import { validateTrainingRows, type TrainingRow, parseJSONL } from "./validateTr
import { uploadDatasetEntryFile } from "~/utils/azure/website";
import { formatFileSize } from "~/utils/utils";
const ImportDataButton = () => {
const UploadDataButton = () => {
const disclosure = useDisclosure();
return (
<>
<ActionButton
onClick={disclosure.onOpen}
label="Import Data"
label="Upload Data"
icon={AiOutlineCloudUpload}
iconBoxSize={4}
requireBeta
/>
<ImportDataModal disclosure={disclosure} />
<UploadDataModal disclosure={disclosure} />
</>
);
};
export default ImportDataButton;
export default UploadDataButton;
const ImportDataModal = ({ disclosure }: { disclosure: UseDisclosureReturn }) => {
const UploadDataModal = ({ disclosure }: { disclosure: UseDisclosureReturn }) => {
const dataset = useDataset().data;
const [validationError, setValidationError] = useState<string | null>(null);

View File

@@ -25,10 +25,10 @@ import DatasetEntryPaginator from "~/components/datasets/DatasetEntryPaginator";
import { useAppStore } from "~/state/store";
import FineTuneButton from "~/components/datasets/FineTuneButton";
import ExperimentButton from "~/components/datasets/ExperimentButton";
import ImportDataButton from "~/components/datasets/ImportDataButton";
import UploadDataButton from "~/components/datasets/UploadDataButton";
// import DownloadButton from "~/components/datasets/DownloadButton";
import DeleteButton from "~/components/datasets/DeleteButton";
import FileUploadCard from "~/components/datasets/FileUploadCard";
import FileUploadsCard from "~/components/datasets/FileUploadsCard";
export default function Dataset() {
const utils = api.useContext();
@@ -41,19 +41,6 @@ export default function Dataset() {
setName(dataset.data?.name || "");
}, [dataset.data?.name]);
const [fileUploadsRefetchInterval, setFileUploadsRefetchInterval] = useState<number>(500);
const fileUploads = api.datasets.listFileUploads.useQuery(
{ datasetId: dataset.data?.id as string },
{ enabled: !!dataset.data?.id, refetchInterval: fileUploadsRefetchInterval },
);
useEffect(() => {
if (fileUploads?.data?.some((fu) => fu.status !== "COMPLETE" && fu.status !== "ERROR")) {
setFileUploadsRefetchInterval(500);
} else {
setFileUploadsRefetchInterval(0);
}
}, [fileUploads]);
useEffect(() => {
useAppStore.getState().sharedArgumentsEditor.loadMonaco().catch(console.error);
}, []);
@@ -115,16 +102,9 @@ export default function Dataset() {
<DatasetHeaderButtons openDrawer={drawerDisclosure.onOpen} />
</PageHeaderContainer>
<VStack px={8} py={8} alignItems="flex-start" spacing={4} w="full">
<HStack w="full">
<VStack w="full">
{fileUploads?.data?.map((upload) => (
<FileUploadCard key={upload.id} fileUpload={upload} />
))}
</VStack>
</HStack>
<HStack w="full" justifyContent="flex-end">
<FineTuneButton />
<ImportDataButton />
<UploadDataButton />
<ExperimentButton />
{/* <DownloadButton /> */}
<DeleteButton />
@@ -133,6 +113,7 @@ export default function Dataset() {
<DatasetEntryPaginator />
</VStack>
</VStack>
<FileUploadsCard />
</AppShell>
<DatasetConfigurationDrawer disclosure={drawerDisclosure} />
</>

View File

@@ -3,7 +3,7 @@ import { z } from "zod";
import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc";
import { prisma } from "~/server/db";
import { requireCanModifyProject, requireCanViewProject } from "~/utils/accessControl";
import { success } from "~/utils/errorHandling/standardResponses";
import { error, success } from "~/utils/errorHandling/standardResponses";
import { generateServiceClientUrl } from "~/utils/azure/server";
import { queueImportDatasetEntries } from "~/server/tasks/importDatasetEntries.task";
@@ -148,19 +148,33 @@ export const datasetsRouter = createTRPCRouter({
orderBy: { createdAt: "desc" },
});
}),
hideFileUpload: protectedProcedure
.input(z.object({ fileUploadId: z.string() }))
hideFileUploads: protectedProcedure
.input(z.object({ fileUploadIds: z.string().array() }))
.mutation(async ({ input, ctx }) => {
const { datasetId } = await prisma.datasetFileUpload.findUniqueOrThrow({
where: { id: input.fileUploadId },
});
const { projectId } = await prisma.dataset.findUniqueOrThrow({
where: { id: datasetId },
if (!input.fileUploadIds.length) return error("No file upload ids provided");
const {
dataset: { projectId, id: datasetId },
} = await prisma.datasetFileUpload.findUniqueOrThrow({
where: { id: input.fileUploadIds[0] },
select: {
dataset: {
select: {
id: true,
projectId: true,
},
},
},
});
await requireCanModifyProject(projectId, ctx);
await prisma.datasetFileUpload.update({
where: { id: input.fileUploadId },
await prisma.datasetFileUpload.updateMany({
where: {
id: {
in: input.fileUploadIds,
},
datasetId,
},
data: {
visible: false,
},

View File

@@ -104,6 +104,7 @@ export const importDatasetEntries = defineTask<ImportDatasetEntriesJob>(
data: {
errorMessage: `Error formatting rows: ${e.message as string}`,
status: "ERROR",
visible: true,
},
});
return;
@@ -128,6 +129,7 @@ export const importDatasetEntries = defineTask<ImportDatasetEntriesJob>(
data: {
status: "COMPLETE",
progress: 100,
visible: true,
},
});
},