95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
import { Button } from "@minmon/ui/components/button";
|
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
import { createFileRoute, Link } from "@tanstack/react-router";
|
|
import { toast } from "sonner";
|
|
|
|
import { ConfirmDeleteIconButton, PageSection, QueryStateCard, ResourceListCard } from "@/components/dashboard-ui";
|
|
import { queryClient, trpc, trpcClient } from "@/utils/trpc";
|
|
|
|
export const Route = createFileRoute("/dashboard/notes")({
|
|
component: RouteComponent,
|
|
});
|
|
|
|
function RouteComponent() {
|
|
const notes = useQuery(trpc.notes.list.queryOptions());
|
|
const servers = useQuery(trpc.servers.list.queryOptions());
|
|
const removeNote = useMutation({
|
|
mutationFn: async (id: string) => trpcClient.notes.delete.mutate({ id }),
|
|
onSuccess: async () => {
|
|
toast.success("Note deleted");
|
|
await queryClient.invalidateQueries();
|
|
},
|
|
onError: (error) => toast.error(error.message),
|
|
});
|
|
|
|
if (notes.isLoading || servers.isLoading) {
|
|
return <QueryStateCard title="Loading notes" description="Fetching server documentation and internal notes." />;
|
|
}
|
|
|
|
if (notes.isError || servers.isError || !notes.data || !servers.data) {
|
|
return <QueryStateCard title="Notes unavailable" description="The note list could not be loaded." />;
|
|
}
|
|
|
|
const serverMap = new Map(servers.data.map((item) => [item.id, item.name]));
|
|
|
|
return (
|
|
<PageSection
|
|
title="Notes"
|
|
description="Manage documentation, incident notes, and internal reminders for each server."
|
|
action={
|
|
<Link to="/dashboard/notes/new">
|
|
<Button>New note</Button>
|
|
</Link>
|
|
}
|
|
>
|
|
<ResourceListCard
|
|
title="Server notes"
|
|
description="Each note belongs to one server and acts as lightweight internal documentation."
|
|
columns={[
|
|
{
|
|
key: "title",
|
|
header: "Title",
|
|
render: (item: (typeof notes.data)[number]) => (
|
|
<div className="grid gap-1">
|
|
<Link to="/dashboard/notes/$noteId" params={{ noteId: item.id }} className="font-medium hover:underline">
|
|
{item.title}
|
|
</Link>
|
|
<span className="text-xs text-muted-foreground line-clamp-2">{item.content}</span>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
key: "server",
|
|
header: "Server",
|
|
render: (item: (typeof notes.data)[number]) => serverMap.get(item.serverId) || item.serverId,
|
|
},
|
|
{
|
|
key: "category",
|
|
header: "Category",
|
|
render: (item: (typeof notes.data)[number]) => item.category || "-",
|
|
},
|
|
{
|
|
key: "updatedAt",
|
|
header: "Updated",
|
|
render: (item: (typeof notes.data)[number]) => new Date(item.updatedAt).toLocaleString(),
|
|
},
|
|
{
|
|
key: "actions",
|
|
header: "Actions",
|
|
className: "w-16",
|
|
render: (item: (typeof notes.data)[number]) => (
|
|
<ConfirmDeleteIconButton
|
|
itemLabel={`note ${item.title}`}
|
|
disabled={removeNote.isPending}
|
|
onConfirm={() => removeNote.mutate(item.id)}
|
|
/>
|
|
),
|
|
},
|
|
]}
|
|
items={notes.data}
|
|
emptyTitle="No notes yet"
|
|
emptyDescription="Add a note to document maintenance, incidents, or general server information."
|
|
/>
|
|
</PageSection>
|
|
);
|
|
}
|