diff --git a/web/src/templates/api.ts b/web/src/templates/api.ts index 61f36d3..3fa85a6 100644 --- a/web/src/templates/api.ts +++ b/web/src/templates/api.ts @@ -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>({ 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() { diff --git a/web/src/templates/dashboard.tsx b/web/src/templates/dashboard.tsx index 4722e89..9a82997 100644 --- a/web/src/templates/dashboard.tsx +++ b/web/src/templates/dashboard.tsx @@ -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 ( -
- - - - - -
- ); - } - if (templates?.length === 0) { - return

No templates found.

; - } - 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: ( - _deleteTemplate(templateName)} - > - Try again - - ), - }); - } - } - - return ( - <> - - - - Name - Description - Created at - Actions - - - - {templates ? ( - - {templates.map((template) => ( - - {template.name} - - {template.description || "No description"} - - - {dayjs(template.createdOn).format("YYYY/MM/DD")} - - - - - - - ))} - - ) : null} -
- {placeholder()} - - ); -} - export { TemplatesDashboard }; diff --git a/web/src/templates/template-table.tsx b/web/src/templates/template-table.tsx new file mode 100644 index 0000000..7d17981 --- /dev/null +++ b/web/src/templates/template-table.tsx @@ -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 ( +
+ + + + + +
+ ); + } + if (templates?.length === 0) { + return

No templates found.

; + } + return null; + } + + return ( + <> + + + + Name + Description + Created at + Actions + + + + {templates ? ( + + {templates.map((template) => ( + + ))} + + ) : null} +
+ {placeholder()} + + ); +} + +function TemplateTableRow({ template }: { template: TemplateMeta }) { + return ( + + {template.name} + {template.description || "No description"} + {dayjs(template.createdOn).format("YYYY/MM/DD")} + + + + + + ); +} + +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 ( + + ); +} + +export { TemplateTable };