diff --git a/README.md b/README.md index a6db383..adcc430 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@
- Demo + Demo

Backup management with scheduling and monitoring diff --git a/app/client/components/create-volume-dialog.tsx b/app/client/components/create-volume-dialog.tsx deleted file mode 100644 index 6ad92ca..0000000 --- a/app/client/components/create-volume-dialog.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { useMutation } from "@tanstack/react-query"; -import { Plus } from "lucide-react"; -import { useId } from "react"; -import { toast } from "sonner"; -import { parseError } from "~/client/lib/errors"; -import { CreateVolumeForm } from "./create-volume-form"; -import { Button } from "./ui/button"; -import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog"; -import { ScrollArea } from "./ui/scroll-area"; -import { createVolumeMutation } from "../api-client/@tanstack/react-query.gen"; - -type Props = { - open: boolean; - setOpen: (open: boolean) => void; -}; - -export const CreateVolumeDialog = ({ open, setOpen }: Props) => { - const formId = useId(); - - const create = useMutation({ - ...createVolumeMutation(), - onSuccess: () => { - toast.success("Volume created successfully"); - setOpen(false); - }, - onError: (error) => { - toast.error("Failed to create volume", { - description: parseError(error)?.message, - }); - }, - }); - - return ( -

- - - - - - - Create volume - - { - create.mutate({ body: { config: values, name: values.name } }); - }} - /> - - - - - - - - ); -}; diff --git a/app/client/modules/volumes/routes/create-volume.tsx b/app/client/modules/volumes/routes/create-volume.tsx new file mode 100644 index 0000000..9930807 --- /dev/null +++ b/app/client/modules/volumes/routes/create-volume.tsx @@ -0,0 +1,83 @@ +import { useMutation } from "@tanstack/react-query"; +import { HardDrive } from "lucide-react"; +import { useId } from "react"; +import { useNavigate } from "react-router"; +import { toast } from "sonner"; +import { createVolumeMutation } from "~/client/api-client/@tanstack/react-query.gen"; +import { CreateVolumeForm, type FormValues } from "~/client/components/create-volume-form"; +import { Button } from "~/client/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card"; +import { parseError } from "~/client/lib/errors"; +import type { Route } from "./+types/create-volume"; +import { Alert, AlertDescription } from "~/client/components/ui/alert"; + +export const handle = { + breadcrumb: () => [{ label: "Volumes", href: "/volumes" }, { label: "Create" }], +}; + +export function meta(_: Route.MetaArgs) { + return [ + { title: "Create Volume" }, + { + name: "description", + content: "Create a new storage volume with automatic mounting and health checks.", + }, + ]; +} + +export default function CreateVolume() { + const navigate = useNavigate(); + const formId = useId(); + + const createVolume = useMutation({ + ...createVolumeMutation(), + onSuccess: (data) => { + toast.success("Volume created successfully"); + navigate(`/volumes/${data.name}`); + }, + }); + + const handleSubmit = (values: FormValues) => { + createVolume.mutate({ + body: { + config: values, + name: values.name, + }, + }); + }; + + return ( +
+ + +
+
+ +
+ Create Volume +
+
+ + {createVolume.isError && ( + + + Failed to create volume: +
+ {parseError(createVolume.error)?.message} +
+
+ )} + +
+ + +
+
+
+
+ ); +} diff --git a/app/client/modules/volumes/routes/volumes.tsx b/app/client/modules/volumes/routes/volumes.tsx index 04cb2d8..eb9969f 100644 --- a/app/client/modules/volumes/routes/volumes.tsx +++ b/app/client/modules/volumes/routes/volumes.tsx @@ -1,8 +1,7 @@ import { useQuery } from "@tanstack/react-query"; -import { HardDrive, RotateCcw } from "lucide-react"; +import { HardDrive, Plus, RotateCcw } from "lucide-react"; import { useState } from "react"; import { useNavigate } from "react-router"; -import { CreateVolumeDialog } from "~/client/components/create-volume-dialog"; import { EmptyState } from "~/client/components/empty-state"; import { StatusDot } from "~/client/components/status-dot"; import { Button } from "~/client/components/ui/button"; @@ -36,7 +35,6 @@ export const clientLoader = async () => { }; export default function Volumes({ loaderData }: Route.ComponentProps) { - const [createVolumeOpen, setCreateVolumeOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const [statusFilter, setStatusFilter] = useState(""); const [backendFilter, setBackendFilter] = useState(""); @@ -73,7 +71,12 @@ export default function Volumes({ loaderData }: Route.ComponentProps) { icon={HardDrive} title="No volume" description="Manage and monitor all your storage backends in one place with advanced features like automatic mounting and health checks." - button={} + button={ + + } /> ); } @@ -115,7 +118,10 @@ export default function Volumes({ loaderData }: Route.ComponentProps) { )} - +
diff --git a/app/routes.ts b/app/routes.ts index f126a35..3beb7c3 100644 --- a/app/routes.ts +++ b/app/routes.ts @@ -7,6 +7,7 @@ export default [ layout("./client/components/layout.tsx", [ route("/", "./client/routes/root.tsx"), route("volumes", "./client/modules/volumes/routes/volumes.tsx"), + route("volumes/create", "./client/modules/volumes/routes/create-volume.tsx"), route("volumes/:name", "./client/modules/volumes/routes/volume-details.tsx"), route("backups", "./client/modules/backups/routes/backups.tsx"), route("backups/create", "./client/modules/backups/routes/create-backup.tsx"), diff --git a/screenshots/backup-details.png b/screenshots/backup-details.png new file mode 100644 index 0000000..f37a6ca Binary files /dev/null and b/screenshots/backup-details.png differ