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 { useEffect } from "react"; import { toast } from "sonner"; import { DetailCard, FormCard, PageSection, QueryStateCard, SelectField, 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; }; const serviceTypeOptions = [ { value: "web_app", label: "Web app" }, { value: "mail", label: "Mail" }, { value: "database", label: "Database" }, { value: "api", label: "API" }, { value: "proxy", label: "Proxy" }, { value: "other", label: "Other" }, ] as const; const serviceProtocolOptions = [ { value: "http", label: "HTTP" }, { value: "https", label: "HTTPS" }, { value: "tcp", label: "TCP" }, { value: "udp", label: "UDP" }, { value: "other", label: "Other" }, ] as const; const serviceStatusOptions = [ { value: "active", label: "Active" }, { value: "down", label: "Down" }, { value: "maintenance", label: "Maintenance" }, ] as const; 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), }); const defaultValues: ServiceFormValues = { serverId: "", name: "", type: "web_app", internalPort: "", externalPort: "", protocol: "http", status: "active", description: "", }; const form = useForm({ defaultValues, onSubmit: async ({ value }) => { await updateService.mutateAsync(value); }, }); useEffect(() => { if (!service.data) { return; } form.reset({ 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 ?? "", }); }, [service.data, form]); if (service.isLoading || servers.isLoading) { return ; } if (service.isError || servers.isError || !service.data || !servers.data) { return ; } const serverOptions = servers.data.map((item) => ({ value: item.id, label: `${item.name} (${item.id})`, })); 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) => field.handleChange(e.target.value)} />} {(field) => field.handleChange(value as ServiceFormValues["type"])} placeholder="Select service type" options={[...serviceTypeOptions]} />} {(field) => field.handleChange(e.target.value)} />} {(field) => field.handleChange(e.target.value)} />} {(field) => field.handleChange(value as ServiceFormValues["protocol"])} placeholder="Select protocol" options={[...serviceProtocolOptions]} />} {(field) => field.handleChange(value as ServiceFormValues["status"])} placeholder="Select service status" options={[...serviceStatusOptions]} />}
{(field) => field.handleChange(e.target.value)} />}
); }