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 { toast } from "sonner"; import { DetailCard, FormCard, PageSection, QueryStateCard, TextAreaField, TextField } from "@/components/dashboard-ui"; import { queryClient, trpc, trpcClient } from "@/utils/trpc"; export const Route = createFileRoute("/dashboard/services/$serviceId")({ component: RouteComponent, }); type ServiceFormValues = { serverId: string; name: string; type: "web_app" | "mail" | "database" | "api" | "proxy" | "other"; internalPort: string; externalPort: string; protocol: "http" | "https" | "tcp" | "udp" | "other"; status: "active" | "down" | "maintenance"; description: string; }; function RouteComponent() { const { serviceId } = Route.useParams(); const navigate = useNavigate(); const service = useQuery(trpc.services.byId.queryOptions({ id: serviceId })); const servers = useQuery(trpc.servers.list.queryOptions()); const updateService = useMutation({ mutationFn: async (values: ServiceFormValues) => trpcClient.services.update.mutate({ id: serviceId, serverId: values.serverId, name: values.name, type: values.type, internalPort: values.internalPort ? Number(values.internalPort) : null, externalPort: values.externalPort ? Number(values.externalPort) : null, protocol: values.protocol, status: values.status, description: values.description || null, }), onSuccess: async () => { toast.success("Service updated"); await queryClient.invalidateQueries(); }, onError: (error) => toast.error(error.message), }); if (service.isLoading || servers.isLoading) { return ; } if (service.isError || servers.isError || !service.data || !servers.data) { return ; } const defaultValues: ServiceFormValues = { serverId: service.data.serverId, name: service.data.name, type: service.data.type, internalPort: service.data.internalPort?.toString() ?? "", externalPort: service.data.externalPort?.toString() ?? "", protocol: service.data.protocol, status: service.data.status, description: service.data.description ?? "", }; const form = useForm({ defaultValues, onSubmit: async ({ value }) => { await updateService.mutateAsync(value); }, }); return (
{service.data.status} }, { label: "Server ID", value: service.data.serverId }, { label: "Service type", value: service.data.type }, { label: "Protocol", value: service.data.protocol }, { label: "Internal port", value: service.data.internalPort ?? "-" }, { label: "External port", value: service.data.externalPort ?? "-" }, { label: "Related domains", value: String(service.data.summary.domains) }, ]} />
{ e.preventDefault(); e.stopPropagation(); form.handleSubmit(); }} > ({ isSubmitting: state.isSubmitting })}> {({ isSubmitting }) => } } > {(field) => ( field.handleChange(e.target.value)} description={`Available: ${servers.data.map((item) => `${item.name} (${item.id})`).join(", ")}`} /> )}
{(field) => field.handleChange(e.target.value)} />} {(field) => field.handleChange(e.target.value as ServiceFormValues["type"])} />} {(field) => field.handleChange(e.target.value)} />} {(field) => field.handleChange(e.target.value)} />} {(field) => field.handleChange(e.target.value as ServiceFormValues["protocol"])} />} {(field) => field.handleChange(e.target.value as ServiceFormValues["status"])} />}
{(field) => field.handleChange(e.target.value)} />}
); }