refactor(create-repository): from dialog to page

This commit is contained in:
Nicolas Meienberger
2025-11-14 21:10:40 +01:00
parent c179a16d15
commit c898e1ce07
4 changed files with 101 additions and 71 deletions

View File

@@ -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 { CreateRepositoryForm } from "./create-repository-form";
import { Button } from "./ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog";
import { ScrollArea } from "./ui/scroll-area";
import { createRepositoryMutation } from "../api-client/@tanstack/react-query.gen";
type Props = {
open: boolean;
setOpen: (open: boolean) => void;
};
export const CreateRepositoryDialog = ({ open, setOpen }: Props) => {
const formId = useId();
const create = useMutation({
...createRepositoryMutation(),
onSuccess: () => {
toast.success("Repository created successfully");
setOpen(false);
},
onError: (error) => {
toast.error("Failed to create repository", {
description: parseError(error)?.message,
});
},
});
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>
<Plus size={16} className="mr-2" />
Create Repository
</Button>
</DialogTrigger>
<DialogContent>
<ScrollArea className="h-[500px] p-4">
<DialogHeader>
<DialogTitle>Create repository</DialogTitle>
</DialogHeader>
<CreateRepositoryForm
className="mt-4"
mode="create"
formId={formId}
onSubmit={(values) => {
create.mutate({ body: { config: values, name: values.name, compressionMode: values.compressionMode } });
}}
/>
<DialogFooter className="mt-4">
<Button type="button" variant="secondary" onClick={() => setOpen(false)}>
Cancel
</Button>
<Button type="submit" form={formId} disabled={create.isPending}>
Create
</Button>
</DialogFooter>
</ScrollArea>
</DialogContent>
</Dialog>
);
};

View File

@@ -0,0 +1,89 @@
import { useMutation } from "@tanstack/react-query";
import { Database } from "lucide-react";
import { useId } from "react";
import { useNavigate } from "react-router";
import { toast } from "sonner";
import { createRepositoryMutation } from "~/client/api-client/@tanstack/react-query.gen";
import { CreateRepositoryForm, type RepositoryFormValues } from "~/client/components/create-repository-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-repository";
import { Alert, AlertDescription } from "~/client/components/ui/alert";
export const handle = {
breadcrumb: () => [{ label: "Repositories", href: "/repositories" }, { label: "Create" }],
};
export function meta(_: Route.MetaArgs) {
return [
{ title: "Create Repository" },
{
name: "description",
content: "Create a new backup repository with encryption and compression.",
},
];
}
export default function CreateRepository() {
const navigate = useNavigate();
const formId = useId();
const createRepository = useMutation({
...createRepositoryMutation(),
onSuccess: (data) => {
toast.success("Repository created successfully");
navigate(`/repositories/${data.repository.name}`);
},
});
const handleSubmit = (values: RepositoryFormValues) => {
createRepository.mutate({
body: {
config: values,
name: values.name,
compressionMode: values.compressionMode,
},
});
};
return (
<div className="container mx-auto space-y-6">
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<div className="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
<Database className="w-5 h-5 text-primary" />
</div>
<CardTitle>Create Repository</CardTitle>
</div>
</CardHeader>
<CardContent className="space-y-6">
{createRepository.isError && (
<Alert variant="destructive">
<AlertDescription>
<strong>Failed to create repository:</strong>
<br />
{parseError(createRepository.error)?.message}
</AlertDescription>
</Alert>
)}
<CreateRepositoryForm
mode="create"
formId={formId}
onSubmit={handleSubmit}
loading={createRepository.isPending}
/>
<div className="flex justify-end gap-2 pt-4 border-t">
<Button type="button" variant="secondary" onClick={() => navigate("/repositories")}>
Cancel
</Button>
<Button type="submit" form={formId} loading={createRepository.isPending}>
Create Repository
</Button>
</div>
</CardContent>
</Card>
</div>
);
}

View File

@@ -1,10 +1,9 @@
import { useQuery } from "@tanstack/react-query";
import { Database, RotateCcw } from "lucide-react";
import { Database, Plus, RotateCcw } from "lucide-react";
import { useState } from "react";
import { useNavigate } from "react-router";
import { listRepositories } from "~/client/api-client/sdk.gen";
import { listRepositoriesOptions } from "~/client/api-client/@tanstack/react-query.gen";
import { CreateRepositoryDialog } from "~/client/components/create-repository-dialog";
import { RepositoryIcon } from "~/client/components/repository-icon";
import { Button } from "~/client/components/ui/button";
import { Card } from "~/client/components/ui/card";
@@ -39,7 +38,6 @@ export default function Repositories({ loaderData }: Route.ComponentProps) {
const [searchQuery, setSearchQuery] = useState("");
const [statusFilter, setStatusFilter] = useState("");
const [backendFilter, setBackendFilter] = useState("");
const [createRepositoryOpen, setCreateRepositoryOpen] = useState(false);
const clearFilters = () => {
setSearchQuery("");
@@ -73,7 +71,12 @@ export default function Repositories({ loaderData }: Route.ComponentProps) {
icon={Database}
title="No repository"
description="Repositories are remote storage locations where you can backup your volumes securely. Encrypted and optimized for storage efficiency."
button={<CreateRepositoryDialog open={createRepositoryOpen} setOpen={setCreateRepositoryOpen} />}
button={
<Button onClick={() => navigate("/repositories/create")}>
<Plus size={16} className="mr-2" />
Create Repository
</Button>
}
/>
);
}
@@ -116,7 +119,10 @@ export default function Repositories({ loaderData }: Route.ComponentProps) {
</Button>
)}
</span>
<CreateRepositoryDialog open={createRepositoryOpen} setOpen={setCreateRepositoryOpen} />
<Button onClick={() => navigate("/repositories/create")}>
<Plus size={16} className="mr-2" />
Create Repository
</Button>
</div>
<div className="overflow-x-auto">
<Table className="border-t">

View File

@@ -12,6 +12,7 @@ export default [
route("backups/create", "./client/modules/backups/routes/create-backup.tsx"),
route("backups/:id", "./client/modules/backups/routes/backup-details.tsx"),
route("repositories", "./client/modules/repositories/routes/repositories.tsx"),
route("repositories/create", "./client/modules/repositories/routes/create-repository.tsx"),
route("repositories/:name", "./client/modules/repositories/routes/repository-details.tsx"),
route("repositories/:name/:snapshotId", "./client/modules/repositories/routes/snapshot-details.tsx"),
route("settings", "./client/modules/settings/routes/settings.tsx"),