docs: add report draft and dashboard form refinements

This commit is contained in:
Syahdan 2026-04-29 21:27:36 +07:00
parent 4e0cf0140f
commit 441e709193
26 changed files with 1700 additions and 505 deletions

View file

@ -2,10 +2,10 @@ import { Button } from "@minmon/ui/components/button";
import { StatusBadge } from "@minmon/ui/components/status-badge";
import { useForm } from "@tanstack/react-form";
import { useMutation, useQuery } from "@tanstack/react-query";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { createFileRoute, Link, useNavigate } from "@tanstack/react-router";
import { useEffect } from "react";
import { toast } from "sonner";
import { DetailCard, FormCard, PageSection, QueryStateCard, TextAreaField, TextField } from "@/components/dashboard-ui";
import { DetailCard, FormCard, PageSection, QueryStateCard, ResourceListCard, SelectField, TextAreaField, TextField } from "@/components/dashboard-ui";
import { queryClient, trpc, trpcClient } from "@/utils/trpc";
export const Route = createFileRoute("/dashboard/servers_/$serverId")({
@ -23,10 +23,17 @@ type ServerFormValues = {
status: "active" | "maintenance" | "inactive";
};
const serverStatusOptions = [
{ value: "active", label: "Active" },
{ value: "maintenance", label: "Maintenance" },
{ value: "inactive", label: "Inactive" },
] as const;
function RouteComponent() {
const { serverId } = Route.useParams();
const navigate = useNavigate();
const server = useQuery(trpc.servers.byId.queryOptions({ id: serverId }));
const notes = useQuery(trpc.notes.list.queryOptions({ serverId }));
const updateServer = useMutation({
mutationFn: async (values: ServerFormValues) =>
@ -108,6 +115,54 @@ function RouteComponent() {
/>
</PageSection>
{notes.isLoading ? (
<QueryStateCard title="Loading server notes" description="Fetching notes attached to this server." />
) : notes.isError || !notes.data ? (
<QueryStateCard title="Server notes unavailable" description="The notes for this server could not be loaded." />
) : (
<PageSection
title="Server notes"
description="Notes and internal documentation attached to this server."
action={
<Link to="/dashboard/notes/new">
<Button variant="outline">New note</Button>
</Link>
}
>
<ResourceListCard
title="Attached notes"
description="Review server-specific documentation without leaving this detail page."
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: "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(),
},
]}
items={notes.data}
emptyTitle="No notes for this server"
emptyDescription="Add a note to document maintenance, incidents, or internal server context."
/>
</PageSection>
)}
<form
className="grid gap-4"
onSubmit={(e) => {
@ -137,7 +192,7 @@ function RouteComponent() {
<form.Field name="operatingSystem">{(field) => <TextField label="Operating system" name={field.name} value={field.state.value} onBlur={field.handleBlur} onChange={(e) => field.handleChange(e.target.value)} />}</form.Field>
<form.Field name="location">{(field) => <TextField label="Location" name={field.name} value={field.state.value} onBlur={field.handleBlur} onChange={(e) => field.handleChange(e.target.value)} />}</form.Field>
<form.Field name="provider">{(field) => <TextField label="Provider" name={field.name} value={field.state.value} onBlur={field.handleBlur} onChange={(e) => field.handleChange(e.target.value)} />}</form.Field>
<form.Field name="status">{(field) => <TextField label="Status" name={field.name} value={field.state.value} onBlur={field.handleBlur} onChange={(e) => field.handleChange(e.target.value as "active" | "maintenance" | "inactive")} />}</form.Field>
<form.Field name="status">{(field) => <SelectField label="Status" name={field.name} value={field.state.value} onBlur={field.handleBlur} onValueChange={(value) => field.handleChange(value as ServerFormValues["status"])} placeholder="Select server status" options={[...serverStatusOptions]} />}</form.Field>
</div>
<form.Field name="description">{(field) => <TextAreaField label="Description" name={field.name} value={field.state.value} onBlur={field.handleBlur} onChange={(e) => field.handleChange(e.target.value)} />}</form.Field>
</FormCard>