refactor: delete template logic

This commit is contained in:
2024-12-03 18:50:12 +00:00
parent e5bbd15837
commit 1a220547bd
3 changed files with 148 additions and 123 deletions

View File

@@ -1,5 +1,6 @@
import { type ApiError, fetchApi } from "@/api";
import { promiseOrThrow } from "@/lib/errors";
import type { QueryStatus } from "@/lib/query";
import { useCallback, useState } from "react";
import useSWR, { useSWRConfig } from "swr";
import type {
@@ -8,7 +9,7 @@ import type {
TemplateImage,
TemplateMeta,
} from "./types";
import { QueryStatus } from "@/lib/query";
import { setDefaultAutoSelectFamilyAttemptTimeout } from "net";
function useTemplates() {
return useSWR(
@@ -27,22 +28,32 @@ function useTemplate(name: string) {
}
function useDeleteTemplate() {
const [status, setStatus] = useState<QueryStatus<ApiError>>({ type: "idle" });
const { mutate } = useSWRConfig();
const deleteTemplate = useCallback(
async (templateName: string) => {
mutate(
"/templates",
fetchApi(`/templates/${templateName}`, { method: "DELETE" }),
{
populateCache: (_, templates) =>
templates?.filter((it: TemplateMeta) => it.name !== templateName),
revalidate: false,
},
);
setStatus({ type: "loading" });
try {
await mutate(
"/templates",
fetchApi(`/templates/${templateName}`, { method: "DELETE" }),
{
populateCache: (_, templates) =>
templates?.filter((it: TemplateMeta) => it.name !== templateName),
revalidate: false,
throwOnError: true,
},
);
setStatus({ type: "ok" });
} catch (error: unknown) {
setStatus({ type: "error", error: error as ApiError });
}
},
[mutate],
);
return deleteTemplate;
return { deleteTemplate, status };
}
function useCreateTemplate() {

View File

@@ -4,24 +4,10 @@ import { Dialog, DialogTrigger } from "@/components/ui/dialog";
import { PageHeader } from "@/components/ui/page-header.tsx";
import { Page } from "@/components/ui/page.tsx";
import { SidebarProvider } from "@/components/ui/sidebar.tsx";
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Toaster } from "@/components/ui/toaster";
import { useToast } from "@/hooks/use-toast";
import { ToastAction } from "@radix-ui/react-toast";
import { Link } from "@tanstack/react-router";
import dayjs from "dayjs";
import { Pencil, Plus, Trash2 } from "lucide-react";
import React from "react";
import { useDeleteTemplate, useTemplates } from "./api";
import { Plus } from "lucide-react";
import { NewTemplateDialog } from "./new-template-dialog";
import { TemplateTable } from "./template-table";
function TemplatesDashboard() {
return (
@@ -56,100 +42,4 @@ function Main() {
);
}
const TemplateTable = React.memo(_TemplateTable, () => true);
function _TemplateTable() {
const { data: templates, isLoading } = useTemplates();
const deleteTemplate = useDeleteTemplate();
const { toast } = useToast();
function placeholder() {
if (isLoading) {
return (
<div className="w-full py-2 space-y-2">
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
</div>
);
}
if (templates?.length === 0) {
return <p className="text-center py-2 opacity-80">No templates found.</p>;
}
return null;
}
async function _deleteTemplate(templateName: string) {
try {
await deleteTemplate(templateName);
toast({
title: "Template deleted!",
});
} catch (error) {
toast({
variant: "destructive",
title: "Failed to delete template.",
action: (
<ToastAction
altText="Try again"
onClick={() => _deleteTemplate(templateName)}
>
Try again
</ToastAction>
),
});
}
}
return (
<>
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Description</TableHead>
<TableHead>Created at</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
{templates ? (
<TableBody>
{templates.map((template) => (
<TableRow key={template.name}>
<TableCell>{template.name}</TableCell>
<TableCell>
{template.description || "No description"}
</TableCell>
<TableCell>
{dayjs(template.createdOn).format("YYYY/MM/DD")}
</TableCell>
<TableCell className="flex justify-end space-x-1">
<Button variant="outline" size="icon" asChild>
<Link to={`/templates/${template.name}`} preload="intent">
<div>
<Pencil />
<span className="sr-only">Edit template</span>
</div>
</Link>
</Button>
<Button
variant="outline"
size="icon"
onClick={() => _deleteTemplate(template.name)}
>
<Trash2 className="text-destructive" />
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
) : null}
</Table>
{placeholder()}
</>
);
}
export { TemplatesDashboard };

View File

@@ -0,0 +1,124 @@
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
TableHeader,
TableRow,
TableHead,
TableBody,
TableCell,
} from "@/components/ui/table";
import { useToast } from "@/hooks/use-toast";
import { ToastAction } from "@radix-ui/react-toast";
import dayjs from "dayjs";
import { Pencil, Trash2 } from "lucide-react";
import React, { useEffect } from "react";
import { useTemplates, useDeleteTemplate } from "./api";
import { Link } from "@tanstack/react-router";
import type { TemplateMeta } from "./types";
const TemplateTable = React.memo(_TemplateTable, () => true);
function _TemplateTable() {
const { data: templates, isLoading } = useTemplates();
function placeholder() {
if (isLoading) {
return (
<div className="w-full py-2 space-y-2">
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
<Skeleton className="w-full h-10" />
</div>
);
}
if (templates?.length === 0) {
return <p className="text-center py-2 opacity-80">No templates found.</p>;
}
return null;
}
return (
<>
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Description</TableHead>
<TableHead>Created at</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
</TableHeader>
{templates ? (
<TableBody>
{templates.map((template) => (
<TemplateTableRow key={template.name} template={template} />
))}
</TableBody>
) : null}
</Table>
{placeholder()}
</>
);
}
function TemplateTableRow({ template }: { template: TemplateMeta }) {
return (
<TableRow>
<TableCell>{template.name}</TableCell>
<TableCell>{template.description || "No description"}</TableCell>
<TableCell>{dayjs(template.createdOn).format("YYYY/MM/DD")}</TableCell>
<TableCell className="flex justify-end space-x-1">
<Button variant="outline" size="icon" asChild>
<Link to={`/templates/${template.name}`} preload="intent">
<div>
<Pencil />
<span className="sr-only">Edit template</span>
</div>
</Link>
</Button>
<DeleteTemplateButton template={template} />
</TableCell>
</TableRow>
);
}
function DeleteTemplateButton({ template }: { template: TemplateMeta }) {
const { deleteTemplate, status } = useDeleteTemplate();
const { toast } = useToast();
useEffect(() => {
if (status.type === "error") {
let toastDescription: string;
switch (status.error.type) {
case "NETWORK":
toastDescription = "Network error";
break;
default:
toastDescription = "Unexpected error";
break;
}
toast({
variant: "destructive",
title: "Failed to delete template",
description: toastDescription,
});
}
}, [status, toast]);
async function _deleteTemplate() {
await deleteTemplate(template.name);
toast({ title: "Template deleted!" });
}
return (
<Button variant="outline" size="icon" onClick={_deleteTemplate}>
<Trash2 className="text-destructive" />
</Button>
);
}
export { TemplateTable };