diff --git a/apps/web/src/references/components/CreateReference.tsx b/apps/web/src/references/components/CreateReference.tsx index bb821f733..cc23341ae 100644 --- a/apps/web/src/references/components/CreateReference.tsx +++ b/apps/web/src/references/components/CreateReference.tsx @@ -1,52 +1,54 @@ import { Dialog, DialogContent, DialogTitle } from "@base/Dialog"; -import NavTab from "@base/NavTab"; -import NavTabs from "@base/NavTabs"; -import EmptyReference from "./EmptyReference"; -import ImportReference from "./ImportReference"; +import { SelectBox, SelectBoxItem } from "@base/SelectBox"; +import { useState } from "react"; +import { CreateReferenceForm } from "./CreateReferenceForm"; type CreateReferenceProps = { - createReferenceType?: string; - setCreateReferenceType: (type?: string) => void; + open: boolean; + onOpenChange: (open: boolean) => void; }; /** * The create reference view with options to create an empty reference or import a reference */ -export function CreateReference({ - createReferenceType, - setCreateReferenceType, -}: CreateReferenceProps) { +export function CreateReference({ open, onOpenChange }: CreateReferenceProps) { + const [mode, setMode] = useState<"empty" | "import">("empty"); + + function handleOpenChange(open: boolean) { + onOpenChange(open); + if (!open) { + setMode("empty"); + } + } + + function handleSuccess() { + onOpenChange(false); + setMode("empty"); + } + return ( - { - setCreateReferenceType(undefined); - }} - > + Create Reference - - - Empty - - - Import - - - - {createReferenceType === "import" ? ( - - ) : ( - - )} + setMode(value as "empty" | "import")} + value={mode} + > + +
Empty
+ Start from a blank reference. +
+ +
Import
+ + Create a reference from a file previously exported from another + Virtool reference. + +
+
+
); diff --git a/apps/web/src/references/components/CreateReferenceForm.tsx b/apps/web/src/references/components/CreateReferenceForm.tsx new file mode 100644 index 000000000..01fb5dce4 --- /dev/null +++ b/apps/web/src/references/components/CreateReferenceForm.tsx @@ -0,0 +1,173 @@ +import { DialogFooter } from "@base/Dialog"; +import InputError from "@base/InputError"; +import InputGroup from "@base/InputGroup"; +import InputLabel from "@base/InputLabel"; +import InputSimple from "@base/InputSimple"; +import ProgressBarAffixed from "@base/ProgressBarAffixed"; +import SaveButton from "@base/SaveButton"; +import TextArea from "@base/TextArea"; +import { useNavigate } from "@tanstack/react-router"; +import { useId } from "react"; +import { Controller, useForm } from "react-hook-form"; +import { UploadBar } from "@/uploads/components/UploadBar"; +import { + useCreateReference, + useImportReference, + useUploadReference, +} from "../queries"; + +type FormValues = { + name: string; + description: string; + organism: string; + upload: string; +}; + +type CreateReferenceFormProps = { + mode: "empty" | "import"; + onSuccess: () => void; +}; + +/** + * A form for creating a reference from scratch or by importing a file, sharing + * name and description between the two modes + */ +export function CreateReferenceForm({ + mode, + onSuccess, +}: CreateReferenceFormProps) { + const navigate = useNavigate(); + const createMutation = useCreateReference(); + const importMutation = useImportReference(); + const { uploadMutation, fileName, uploadId, progress } = useUploadReference(); + + const nameId = useId(); + const organismId = useId(); + const descriptionId = useId(); + + const { + control, + formState: { errors }, + handleSubmit, + register, + setError, + } = useForm({ + defaultValues: { + name: "", + description: "", + organism: "", + upload: "", + }, + }); + + function handleDrop(acceptedFiles: File[]) { + const file = acceptedFiles[0]; + if (file === undefined) { + return; + } + + uploadMutation.mutate(file); + } + + function onSubmit(values: FormValues) { + if (mode === "import") { + if (uploadId === null) { + setError("upload", { + message: "Please wait for the upload to finish", + }); + return; + } + + importMutation.mutate( + { + name: values.name, + description: values.description, + importFrom: uploadId, + }, + { + onSuccess: () => { + navigate({ to: "/refs", replace: true }); + onSuccess(); + }, + }, + ); + + return; + } + + createMutation.mutate( + { + name: values.name, + description: values.description, + organism: values.organism, + }, + { + onSuccess: () => { + navigate({ to: "/refs" }); + onSuccess(); + }, + }, + ); + } + + const uploadBarMessage = + fileName || (progress === 0 ? "Drag file here to upload" : "Uploading..."); + + return ( +
+ {mode === "import" && ( + ( +
+ + { + handleDrop(acceptedFiles); + const file = acceptedFiles[0]; + if (file) { + onChange(file.name); + } + }} + multiple={false} + /> + + {errors.upload?.message} +
+ )} + /> + )} + + + Name + + {errors.name?.message} + + + {mode === "empty" && ( + + Organism + + + )} + + + Description +