From 41fff32f6052ab938540d19af1bd0a1e56f765ef Mon Sep 17 00:00:00 2001 From: Syahdan Date: Wed, 29 Apr 2026 18:49:52 +0700 Subject: [PATCH 1/5] chore: simplify drizzle and effect import --- bun.lock | 8 +++++--- package.json | 2 ++ packages/api/package.json | 4 ++-- packages/db/package.json | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/bun.lock b/bun.lock index a1a5ee1..47dc957 100644 --- a/bun.lock +++ b/bun.lock @@ -100,8 +100,8 @@ "@trpc/client": "catalog:", "@trpc/server": "catalog:", "dotenv": "catalog:", - "drizzle-orm": "^0.45.1", - "effect": "^3.18.4", + "drizzle-orm": "catalog:", + "effect": "catalog:", "zod": "catalog:", }, "devDependencies": { @@ -134,7 +134,7 @@ "@minmon/env": "workspace:*", "better-auth": "catalog:", "dotenv": "catalog:", - "drizzle-orm": "^0.45.1", + "drizzle-orm": "catalog:", "pg": "^8.17.1", "zod": "catalog:", }, @@ -191,6 +191,8 @@ "@types/react-dom": "^19.2.3", "better-auth": "1.5.5", "dotenv": "^17.2.2", + "drizzle-orm": "^0.45.1", + "effect": "^3.18.4", "elysia": "^1.4.28", "next-themes": "^0.4.6", "typescript": "^6", diff --git a/package.json b/package.json index a545b6c..09cf3e1 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "elysia": "^1.4.28", "@trpc/server": "^11.16.0", "better-auth": "1.5.5", + "drizzle-orm": "^0.45.1", + "effect": "^3.18.4", "next-themes": "^0.4.6", "@trpc/client": "^11.16.0", "@types/react-dom": "^19.2.3" diff --git a/packages/api/package.json b/packages/api/package.json index 1223235..43a9162 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -17,8 +17,8 @@ "@trpc/client": "catalog:", "@trpc/server": "catalog:", "dotenv": "catalog:", - "drizzle-orm": "^0.45.1", - "effect": "^3.18.4", + "drizzle-orm": "catalog:", + "effect": "catalog:", "zod": "catalog:" }, "devDependencies": { diff --git a/packages/db/package.json b/packages/db/package.json index 0381152..2b29b35 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -24,7 +24,7 @@ "better-auth": "catalog:", "@minmon/env": "workspace:*", "dotenv": "catalog:", - "drizzle-orm": "^0.45.1", + "drizzle-orm": "catalog:", "pg": "^8.17.1", "zod": "catalog:" }, From 8e9acd500643848a4303bf280f543ef656f9a2f9 Mon Sep 17 00:00:00 2001 From: Syahdan Date: Wed, 29 Apr 2026 19:20:27 +0700 Subject: [PATCH 2/5] ui: unnest dashboard detail routes and stabilize form hooks --- apps/web/src/routeTree.gen.ts | 166 +++++++++--------- ...d.tsx => dashboard.domains_.$domainId.tsx} | 66 ++++--- ...oteId.tsx => dashboard.notes_.$noteId.tsx} | 40 +++-- ...d.tsx => dashboard.servers_.$serverId.tsx} | 52 ++++-- ...tsx => dashboard.services_.$serviceId.tsx} | 52 ++++-- 5 files changed, 219 insertions(+), 157 deletions(-) rename apps/web/src/routes/{dashboard.domains.$domainId.tsx => dashboard.domains_.$domainId.tsx} (87%) rename apps/web/src/routes/{dashboard.notes.$noteId.tsx => dashboard.notes_.$noteId.tsx} (93%) rename apps/web/src/routes/{dashboard.servers.$serverId.tsx => dashboard.servers_.$serverId.tsx} (93%) rename apps/web/src/routes/{dashboard.services.$serviceId.tsx => dashboard.services_.$serviceId.tsx} (94%) diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index 83f00c8..3f406e0 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -17,14 +17,14 @@ import { Route as DashboardServicesRouteImport } from './routes/dashboard.servic import { Route as DashboardServersRouteImport } from './routes/dashboard.servers' import { Route as DashboardNotesRouteImport } from './routes/dashboard.notes' import { Route as DashboardDomainsRouteImport } from './routes/dashboard.domains' +import { Route as DashboardServicesServiceIdRouteImport } from './routes/dashboard.services_.$serviceId' import { Route as DashboardServicesNewRouteImport } from './routes/dashboard.services.new' -import { Route as DashboardServicesServiceIdRouteImport } from './routes/dashboard.services.$serviceId' +import { Route as DashboardServersServerIdRouteImport } from './routes/dashboard.servers_.$serverId' import { Route as DashboardServersNewRouteImport } from './routes/dashboard.servers.new' -import { Route as DashboardServersServerIdRouteImport } from './routes/dashboard.servers.$serverId' +import { Route as DashboardNotesNoteIdRouteImport } from './routes/dashboard.notes_.$noteId' import { Route as DashboardNotesNewRouteImport } from './routes/dashboard.notes.new' -import { Route as DashboardNotesNoteIdRouteImport } from './routes/dashboard.notes.$noteId' +import { Route as DashboardDomainsDomainIdRouteImport } from './routes/dashboard.domains_.$domainId' import { Route as DashboardDomainsNewRouteImport } from './routes/dashboard.domains.new' -import { Route as DashboardDomainsDomainIdRouteImport } from './routes/dashboard.domains.$domainId' const LoginRoute = LoginRouteImport.update({ id: '/login', @@ -66,49 +66,49 @@ const DashboardDomainsRoute = DashboardDomainsRouteImport.update({ path: '/domains', getParentRoute: () => DashboardRoute, } as any) +const DashboardServicesServiceIdRoute = + DashboardServicesServiceIdRouteImport.update({ + id: '/services_/$serviceId', + path: '/services/$serviceId', + getParentRoute: () => DashboardRoute, + } as any) const DashboardServicesNewRoute = DashboardServicesNewRouteImport.update({ id: '/new', path: '/new', getParentRoute: () => DashboardServicesRoute, } as any) -const DashboardServicesServiceIdRoute = - DashboardServicesServiceIdRouteImport.update({ - id: '/$serviceId', - path: '/$serviceId', - getParentRoute: () => DashboardServicesRoute, +const DashboardServersServerIdRoute = + DashboardServersServerIdRouteImport.update({ + id: '/servers_/$serverId', + path: '/servers/$serverId', + getParentRoute: () => DashboardRoute, } as any) const DashboardServersNewRoute = DashboardServersNewRouteImport.update({ id: '/new', path: '/new', getParentRoute: () => DashboardServersRoute, } as any) -const DashboardServersServerIdRoute = - DashboardServersServerIdRouteImport.update({ - id: '/$serverId', - path: '/$serverId', - getParentRoute: () => DashboardServersRoute, - } as any) +const DashboardNotesNoteIdRoute = DashboardNotesNoteIdRouteImport.update({ + id: '/notes_/$noteId', + path: '/notes/$noteId', + getParentRoute: () => DashboardRoute, +} as any) const DashboardNotesNewRoute = DashboardNotesNewRouteImport.update({ id: '/new', path: '/new', getParentRoute: () => DashboardNotesRoute, } as any) -const DashboardNotesNoteIdRoute = DashboardNotesNoteIdRouteImport.update({ - id: '/$noteId', - path: '/$noteId', - getParentRoute: () => DashboardNotesRoute, -} as any) +const DashboardDomainsDomainIdRoute = + DashboardDomainsDomainIdRouteImport.update({ + id: '/domains_/$domainId', + path: '/domains/$domainId', + getParentRoute: () => DashboardRoute, + } as any) const DashboardDomainsNewRoute = DashboardDomainsNewRouteImport.update({ id: '/new', path: '/new', getParentRoute: () => DashboardDomainsRoute, } as any) -const DashboardDomainsDomainIdRoute = - DashboardDomainsDomainIdRouteImport.update({ - id: '/$domainId', - path: '/$domainId', - getParentRoute: () => DashboardDomainsRoute, - } as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute @@ -119,14 +119,14 @@ export interface FileRoutesByFullPath { '/dashboard/servers': typeof DashboardServersRouteWithChildren '/dashboard/services': typeof DashboardServicesRouteWithChildren '/dashboard/': typeof DashboardIndexRoute - '/dashboard/domains/$domainId': typeof DashboardDomainsDomainIdRoute '/dashboard/domains/new': typeof DashboardDomainsNewRoute - '/dashboard/notes/$noteId': typeof DashboardNotesNoteIdRoute + '/dashboard/domains/$domainId': typeof DashboardDomainsDomainIdRoute '/dashboard/notes/new': typeof DashboardNotesNewRoute - '/dashboard/servers/$serverId': typeof DashboardServersServerIdRoute + '/dashboard/notes/$noteId': typeof DashboardNotesNoteIdRoute '/dashboard/servers/new': typeof DashboardServersNewRoute - '/dashboard/services/$serviceId': typeof DashboardServicesServiceIdRoute + '/dashboard/servers/$serverId': typeof DashboardServersServerIdRoute '/dashboard/services/new': typeof DashboardServicesNewRoute + '/dashboard/services/$serviceId': typeof DashboardServicesServiceIdRoute } export interface FileRoutesByTo { '/': typeof IndexRoute @@ -136,14 +136,14 @@ export interface FileRoutesByTo { '/dashboard/servers': typeof DashboardServersRouteWithChildren '/dashboard/services': typeof DashboardServicesRouteWithChildren '/dashboard': typeof DashboardIndexRoute - '/dashboard/domains/$domainId': typeof DashboardDomainsDomainIdRoute '/dashboard/domains/new': typeof DashboardDomainsNewRoute - '/dashboard/notes/$noteId': typeof DashboardNotesNoteIdRoute + '/dashboard/domains/$domainId': typeof DashboardDomainsDomainIdRoute '/dashboard/notes/new': typeof DashboardNotesNewRoute - '/dashboard/servers/$serverId': typeof DashboardServersServerIdRoute + '/dashboard/notes/$noteId': typeof DashboardNotesNoteIdRoute '/dashboard/servers/new': typeof DashboardServersNewRoute - '/dashboard/services/$serviceId': typeof DashboardServicesServiceIdRoute + '/dashboard/servers/$serverId': typeof DashboardServersServerIdRoute '/dashboard/services/new': typeof DashboardServicesNewRoute + '/dashboard/services/$serviceId': typeof DashboardServicesServiceIdRoute } export interface FileRoutesById { __root__: typeof rootRouteImport @@ -155,14 +155,14 @@ export interface FileRoutesById { '/dashboard/servers': typeof DashboardServersRouteWithChildren '/dashboard/services': typeof DashboardServicesRouteWithChildren '/dashboard/': typeof DashboardIndexRoute - '/dashboard/domains/$domainId': typeof DashboardDomainsDomainIdRoute '/dashboard/domains/new': typeof DashboardDomainsNewRoute - '/dashboard/notes/$noteId': typeof DashboardNotesNoteIdRoute + '/dashboard/domains_/$domainId': typeof DashboardDomainsDomainIdRoute '/dashboard/notes/new': typeof DashboardNotesNewRoute - '/dashboard/servers/$serverId': typeof DashboardServersServerIdRoute + '/dashboard/notes_/$noteId': typeof DashboardNotesNoteIdRoute '/dashboard/servers/new': typeof DashboardServersNewRoute - '/dashboard/services/$serviceId': typeof DashboardServicesServiceIdRoute + '/dashboard/servers_/$serverId': typeof DashboardServersServerIdRoute '/dashboard/services/new': typeof DashboardServicesNewRoute + '/dashboard/services_/$serviceId': typeof DashboardServicesServiceIdRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath @@ -175,14 +175,14 @@ export interface FileRouteTypes { | '/dashboard/servers' | '/dashboard/services' | '/dashboard/' - | '/dashboard/domains/$domainId' | '/dashboard/domains/new' - | '/dashboard/notes/$noteId' + | '/dashboard/domains/$domainId' | '/dashboard/notes/new' - | '/dashboard/servers/$serverId' + | '/dashboard/notes/$noteId' | '/dashboard/servers/new' - | '/dashboard/services/$serviceId' + | '/dashboard/servers/$serverId' | '/dashboard/services/new' + | '/dashboard/services/$serviceId' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -192,14 +192,14 @@ export interface FileRouteTypes { | '/dashboard/servers' | '/dashboard/services' | '/dashboard' - | '/dashboard/domains/$domainId' | '/dashboard/domains/new' - | '/dashboard/notes/$noteId' + | '/dashboard/domains/$domainId' | '/dashboard/notes/new' - | '/dashboard/servers/$serverId' + | '/dashboard/notes/$noteId' | '/dashboard/servers/new' - | '/dashboard/services/$serviceId' + | '/dashboard/servers/$serverId' | '/dashboard/services/new' + | '/dashboard/services/$serviceId' id: | '__root__' | '/' @@ -210,14 +210,14 @@ export interface FileRouteTypes { | '/dashboard/servers' | '/dashboard/services' | '/dashboard/' - | '/dashboard/domains/$domainId' | '/dashboard/domains/new' - | '/dashboard/notes/$noteId' + | '/dashboard/domains_/$domainId' | '/dashboard/notes/new' - | '/dashboard/servers/$serverId' + | '/dashboard/notes_/$noteId' | '/dashboard/servers/new' - | '/dashboard/services/$serviceId' + | '/dashboard/servers_/$serverId' | '/dashboard/services/new' + | '/dashboard/services_/$serviceId' fileRoutesById: FileRoutesById } export interface RootRouteChildren { @@ -284,6 +284,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardDomainsRouteImport parentRoute: typeof DashboardRoute } + '/dashboard/services_/$serviceId': { + id: '/dashboard/services_/$serviceId' + path: '/services/$serviceId' + fullPath: '/dashboard/services/$serviceId' + preLoaderRoute: typeof DashboardServicesServiceIdRouteImport + parentRoute: typeof DashboardRoute + } '/dashboard/services/new': { id: '/dashboard/services/new' path: '/new' @@ -291,12 +298,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardServicesNewRouteImport parentRoute: typeof DashboardServicesRoute } - '/dashboard/services/$serviceId': { - id: '/dashboard/services/$serviceId' - path: '/$serviceId' - fullPath: '/dashboard/services/$serviceId' - preLoaderRoute: typeof DashboardServicesServiceIdRouteImport - parentRoute: typeof DashboardServicesRoute + '/dashboard/servers_/$serverId': { + id: '/dashboard/servers_/$serverId' + path: '/servers/$serverId' + fullPath: '/dashboard/servers/$serverId' + preLoaderRoute: typeof DashboardServersServerIdRouteImport + parentRoute: typeof DashboardRoute } '/dashboard/servers/new': { id: '/dashboard/servers/new' @@ -305,12 +312,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardServersNewRouteImport parentRoute: typeof DashboardServersRoute } - '/dashboard/servers/$serverId': { - id: '/dashboard/servers/$serverId' - path: '/$serverId' - fullPath: '/dashboard/servers/$serverId' - preLoaderRoute: typeof DashboardServersServerIdRouteImport - parentRoute: typeof DashboardServersRoute + '/dashboard/notes_/$noteId': { + id: '/dashboard/notes_/$noteId' + path: '/notes/$noteId' + fullPath: '/dashboard/notes/$noteId' + preLoaderRoute: typeof DashboardNotesNoteIdRouteImport + parentRoute: typeof DashboardRoute } '/dashboard/notes/new': { id: '/dashboard/notes/new' @@ -319,12 +326,12 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardNotesNewRouteImport parentRoute: typeof DashboardNotesRoute } - '/dashboard/notes/$noteId': { - id: '/dashboard/notes/$noteId' - path: '/$noteId' - fullPath: '/dashboard/notes/$noteId' - preLoaderRoute: typeof DashboardNotesNoteIdRouteImport - parentRoute: typeof DashboardNotesRoute + '/dashboard/domains_/$domainId': { + id: '/dashboard/domains_/$domainId' + path: '/domains/$domainId' + fullPath: '/dashboard/domains/$domainId' + preLoaderRoute: typeof DashboardDomainsDomainIdRouteImport + parentRoute: typeof DashboardRoute } '/dashboard/domains/new': { id: '/dashboard/domains/new' @@ -333,23 +340,14 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardDomainsNewRouteImport parentRoute: typeof DashboardDomainsRoute } - '/dashboard/domains/$domainId': { - id: '/dashboard/domains/$domainId' - path: '/$domainId' - fullPath: '/dashboard/domains/$domainId' - preLoaderRoute: typeof DashboardDomainsDomainIdRouteImport - parentRoute: typeof DashboardDomainsRoute - } } } interface DashboardDomainsRouteChildren { - DashboardDomainsDomainIdRoute: typeof DashboardDomainsDomainIdRoute DashboardDomainsNewRoute: typeof DashboardDomainsNewRoute } const DashboardDomainsRouteChildren: DashboardDomainsRouteChildren = { - DashboardDomainsDomainIdRoute: DashboardDomainsDomainIdRoute, DashboardDomainsNewRoute: DashboardDomainsNewRoute, } @@ -357,12 +355,10 @@ const DashboardDomainsRouteWithChildren = DashboardDomainsRoute._addFileChildren(DashboardDomainsRouteChildren) interface DashboardNotesRouteChildren { - DashboardNotesNoteIdRoute: typeof DashboardNotesNoteIdRoute DashboardNotesNewRoute: typeof DashboardNotesNewRoute } const DashboardNotesRouteChildren: DashboardNotesRouteChildren = { - DashboardNotesNoteIdRoute: DashboardNotesNoteIdRoute, DashboardNotesNewRoute: DashboardNotesNewRoute, } @@ -371,12 +367,10 @@ const DashboardNotesRouteWithChildren = DashboardNotesRoute._addFileChildren( ) interface DashboardServersRouteChildren { - DashboardServersServerIdRoute: typeof DashboardServersServerIdRoute DashboardServersNewRoute: typeof DashboardServersNewRoute } const DashboardServersRouteChildren: DashboardServersRouteChildren = { - DashboardServersServerIdRoute: DashboardServersServerIdRoute, DashboardServersNewRoute: DashboardServersNewRoute, } @@ -384,12 +378,10 @@ const DashboardServersRouteWithChildren = DashboardServersRoute._addFileChildren(DashboardServersRouteChildren) interface DashboardServicesRouteChildren { - DashboardServicesServiceIdRoute: typeof DashboardServicesServiceIdRoute DashboardServicesNewRoute: typeof DashboardServicesNewRoute } const DashboardServicesRouteChildren: DashboardServicesRouteChildren = { - DashboardServicesServiceIdRoute: DashboardServicesServiceIdRoute, DashboardServicesNewRoute: DashboardServicesNewRoute, } @@ -402,6 +394,10 @@ interface DashboardRouteChildren { DashboardServersRoute: typeof DashboardServersRouteWithChildren DashboardServicesRoute: typeof DashboardServicesRouteWithChildren DashboardIndexRoute: typeof DashboardIndexRoute + DashboardDomainsDomainIdRoute: typeof DashboardDomainsDomainIdRoute + DashboardNotesNoteIdRoute: typeof DashboardNotesNoteIdRoute + DashboardServersServerIdRoute: typeof DashboardServersServerIdRoute + DashboardServicesServiceIdRoute: typeof DashboardServicesServiceIdRoute } const DashboardRouteChildren: DashboardRouteChildren = { @@ -410,6 +406,10 @@ const DashboardRouteChildren: DashboardRouteChildren = { DashboardServersRoute: DashboardServersRouteWithChildren, DashboardServicesRoute: DashboardServicesRouteWithChildren, DashboardIndexRoute: DashboardIndexRoute, + DashboardDomainsDomainIdRoute: DashboardDomainsDomainIdRoute, + DashboardNotesNoteIdRoute: DashboardNotesNoteIdRoute, + DashboardServersServerIdRoute: DashboardServersServerIdRoute, + DashboardServicesServiceIdRoute: DashboardServicesServiceIdRoute, } const DashboardRouteWithChildren = DashboardRoute._addFileChildren( diff --git a/apps/web/src/routes/dashboard.domains.$domainId.tsx b/apps/web/src/routes/dashboard.domains_.$domainId.tsx similarity index 87% rename from apps/web/src/routes/dashboard.domains.$domainId.tsx rename to apps/web/src/routes/dashboard.domains_.$domainId.tsx index 5f15dc4..8f2fa73 100644 --- a/apps/web/src/routes/dashboard.domains.$domainId.tsx +++ b/apps/web/src/routes/dashboard.domains_.$domainId.tsx @@ -1,13 +1,14 @@ +import { DetailCard, FormCard, PageSection, QueryStateCard, TextAreaField, TextField } from "@/components/dashboard-ui"; +import { queryClient, trpc, trpcClient } from "@/utils/trpc"; 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, TextAreaField, TextField } from "@/components/dashboard-ui"; -import { queryClient, trpc, trpcClient } from "@/utils/trpc"; -export const Route = createFileRoute("/dashboard/domains/$domainId")({ +export const Route = createFileRoute("/dashboard/domains_/$domainId")({ component: RouteComponent, }); @@ -54,6 +55,31 @@ function RouteComponent() { onError: (error) => toast.error(error.message), }); + const form = useForm({ + defaultValues: { + serviceId: "", + name: "", + expectedServerIp: "", + remarks: "", + } satisfies DomainFormValues, + onSubmit: async ({ value }) => { + await updateDomain.mutateAsync(value); + }, + }); + + useEffect(() => { + if (!domain.data) { + return; + } + + form.reset({ + serviceId: domain.data.serviceId, + name: domain.data.name, + expectedServerIp: domain.data.expectedServerIp, + remarks: domain.data.remarks ?? "", + }); + }, [domain.data, form]); + if (domain.isLoading || services.isLoading) { return ; } @@ -62,20 +88,6 @@ function RouteComponent() { return ; } - const defaultValues: DomainFormValues = { - serviceId: domain.data.serviceId, - name: domain.data.name, - expectedServerIp: domain.data.expectedServerIp, - remarks: domain.data.remarks ?? "", - }; - - const form = useForm({ - defaultValues, - onSubmit: async ({ value }) => { - await updateDomain.mutateAsync(value); - }, - }); - return (
@@ -122,16 +134,16 @@ function RouteComponent() { > {(field) => ( - field.handleChange(e.target.value)} - description={`Available: ${services.data.map((item) => `${item.name} (${item.id})`).join(", ")}`} - /> - )} - + field.handleChange(e.target.value)} + description={`Available: ${services.data.map((item) => `${item.name} (${item.id})`).join(", ")}`} + /> + )} +
{(field) => field.handleChange(e.target.value)} />} {(field) => field.handleChange(e.target.value)} />} diff --git a/apps/web/src/routes/dashboard.notes.$noteId.tsx b/apps/web/src/routes/dashboard.notes_.$noteId.tsx similarity index 93% rename from apps/web/src/routes/dashboard.notes.$noteId.tsx rename to apps/web/src/routes/dashboard.notes_.$noteId.tsx index c56fc07..9fe591e 100644 --- a/apps/web/src/routes/dashboard.notes.$noteId.tsx +++ b/apps/web/src/routes/dashboard.notes_.$noteId.tsx @@ -2,11 +2,12 @@ import { Button } from "@minmon/ui/components/button"; 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, TextAreaField, TextField } from "@/components/dashboard-ui"; import { queryClient, trpc, trpcClient } from "@/utils/trpc"; -export const Route = createFileRoute("/dashboard/notes/$noteId")({ +export const Route = createFileRoute("/dashboard/notes_/$noteId")({ component: RouteComponent, }); @@ -39,19 +40,11 @@ function RouteComponent() { onError: (error) => toast.error(error.message), }); - if (note.isLoading || servers.isLoading) { - return ; - } - - if (note.isError || servers.isError || !note.data || !servers.data) { - return ; - } - const defaultValues: NoteFormValues = { - serverId: note.data.serverId, - title: note.data.title, - category: note.data.category ?? "", - content: note.data.content, + serverId: "", + title: "", + category: "", + content: "", }; const form = useForm({ @@ -61,6 +54,27 @@ function RouteComponent() { }, }); + useEffect(() => { + if (!note.data) { + return; + } + + form.reset({ + serverId: note.data.serverId, + title: note.data.title, + category: note.data.category ?? "", + content: note.data.content, + }); + }, [note.data, form]); + + if (note.isLoading || servers.isLoading) { + return ; + } + + if (note.isError || servers.isError || !note.data || !servers.data) { + return ; + } + return (
diff --git a/apps/web/src/routes/dashboard.servers.$serverId.tsx b/apps/web/src/routes/dashboard.servers_.$serverId.tsx similarity index 93% rename from apps/web/src/routes/dashboard.servers.$serverId.tsx rename to apps/web/src/routes/dashboard.servers_.$serverId.tsx index a0253f7..625cc2b 100644 --- a/apps/web/src/routes/dashboard.servers.$serverId.tsx +++ b/apps/web/src/routes/dashboard.servers_.$serverId.tsx @@ -3,11 +3,12 @@ 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, TextAreaField, TextField } from "@/components/dashboard-ui"; import { queryClient, trpc, trpcClient } from "@/utils/trpc"; -export const Route = createFileRoute("/dashboard/servers/$serverId")({ +export const Route = createFileRoute("/dashboard/servers_/$serverId")({ component: RouteComponent, }); @@ -45,23 +46,15 @@ function RouteComponent() { onError: (error) => toast.error(error.message), }); - if (server.isLoading) { - return ; - } - - if (server.isError || !server.data) { - return ; - } - const defaultValues: ServerFormValues = { - name: server.data.name, - primaryIpAddress: server.data.primaryIpAddress, - secondaryIpAddress: server.data.secondaryIpAddress ?? "", - operatingSystem: server.data.operatingSystem ?? "", - location: server.data.location ?? "", - provider: server.data.provider ?? "", - description: server.data.description ?? "", - status: server.data.status, + name: "", + primaryIpAddress: "", + secondaryIpAddress: "", + operatingSystem: "", + location: "", + provider: "", + description: "", + status: "active", }; const form = useForm({ @@ -71,6 +64,31 @@ function RouteComponent() { }, }); + useEffect(() => { + if (!server.data) { + return; + } + + form.reset({ + name: server.data.name, + primaryIpAddress: server.data.primaryIpAddress, + secondaryIpAddress: server.data.secondaryIpAddress ?? "", + operatingSystem: server.data.operatingSystem ?? "", + location: server.data.location ?? "", + provider: server.data.provider ?? "", + description: server.data.description ?? "", + status: server.data.status, + }); + }, [server.data, form]); + + if (server.isLoading) { + return ; + } + + if (server.isError || !server.data) { + return ; + } + return (
diff --git a/apps/web/src/routes/dashboard.services.$serviceId.tsx b/apps/web/src/routes/dashboard.services_.$serviceId.tsx similarity index 94% rename from apps/web/src/routes/dashboard.services.$serviceId.tsx rename to apps/web/src/routes/dashboard.services_.$serviceId.tsx index 598ee1d..2587697 100644 --- a/apps/web/src/routes/dashboard.services.$serviceId.tsx +++ b/apps/web/src/routes/dashboard.services_.$serviceId.tsx @@ -3,11 +3,12 @@ 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, TextAreaField, TextField } from "@/components/dashboard-ui"; import { queryClient, trpc, trpcClient } from "@/utils/trpc"; -export const Route = createFileRoute("/dashboard/services/$serviceId")({ +export const Route = createFileRoute("/dashboard/services_/$serviceId")({ component: RouteComponent, }); @@ -48,23 +49,15 @@ function RouteComponent() { 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 ?? "", + serverId: "", + name: "", + type: "web_app", + internalPort: "", + externalPort: "", + protocol: "http", + status: "active", + description: "", }; const form = useForm({ @@ -74,6 +67,31 @@ function RouteComponent() { }, }); + 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 ; + } + return (
From 4e0cf0140f30f4b6c4fc11e47b7bec08fe67850c Mon Sep 17 00:00:00 2001 From: Syahdan Date: Wed, 29 Apr 2026 20:08:29 +0700 Subject: [PATCH 3/5] ui: redirect home route and simplify dashboard navigation --- apps/web/src/components/header.tsx | 20 +-------- apps/web/src/components/sign-in-form.tsx | 4 +- apps/web/src/routes/index.tsx | 57 +++++------------------- 3 files changed, 13 insertions(+), 68 deletions(-) diff --git a/apps/web/src/components/header.tsx b/apps/web/src/components/header.tsx index c809782..fe5d466 100644 --- a/apps/web/src/components/header.tsx +++ b/apps/web/src/components/header.tsx @@ -4,31 +4,13 @@ import { ModeToggle } from "./mode-toggle"; import UserMenu from "./user-menu"; export default function Header() { - const links = [ - { to: "/", label: "Home" }, - { to: "/dashboard", label: "Dashboard" }, - { to: "/dashboard/servers", label: "Servers" }, - { to: "/dashboard/services", label: "Services" }, - { to: "/dashboard/domains", label: "Domains" }, - { to: "/dashboard/notes", label: "Notes" }, - ] as const; - return (
- + minmon -
diff --git a/apps/web/src/components/sign-in-form.tsx b/apps/web/src/components/sign-in-form.tsx index 2c3060e..119a020 100644 --- a/apps/web/src/components/sign-in-form.tsx +++ b/apps/web/src/components/sign-in-form.tsx @@ -129,8 +129,8 @@ export default function SignInForm() {

Student demo flow uses administrator sign-in only.

- - Return to homepage + + Open dashboard after sign in
diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index 4ba9b8f..1ef3955 100644 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -1,51 +1,14 @@ -import { useQuery } from "@tanstack/react-query"; -import { createFileRoute } from "@tanstack/react-router"; +import { createFileRoute, redirect } from "@tanstack/react-router"; -import { trpc } from "@/utils/trpc"; +import { authClient } from "@/lib/auth-client"; export const Route = createFileRoute("/")({ - component: HomeComponent, + beforeLoad: async () => { + const session = await authClient.getSession(); + + throw redirect({ + to: session.data ? "/dashboard" : "/login", + }); + }, + component: () => null, }); - -const TITLE_TEXT = ` - ██████╗ ███████╗████████╗████████╗███████╗██████╗ - ██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗ - ██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝ - ██╔══██╗██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗ - ██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║ - ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ - - ████████╗ ███████╗████████╗ █████╗ ██████╗██╗ ██╗ - ╚══██╔══╝ ██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝ - ██║ ███████╗ ██║ ███████║██║ █████╔╝ - ██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗ - ██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗ - ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ - `; - -function HomeComponent() { - const healthCheck = useQuery(trpc.healthCheck.queryOptions()); - - return ( -
-
{TITLE_TEXT}
-
-
-

API Status

-
-
- - {healthCheck.isLoading - ? "Checking..." - : healthCheck.data - ? "Connected" - : "Disconnected"} - -
-
-
-
- ); -} From 441e70919338974a917d70c9a0b36da632190266 Mon Sep 17 00:00:00 2001 From: Syahdan Date: Wed, 29 Apr 2026 21:27:36 +0700 Subject: [PATCH 4/5] docs: add report draft and dashboard form refinements --- apps/web/src/components/dashboard-shell.tsx | 4 +- apps/web/src/components/dashboard-ui.tsx | 121 +++- apps/web/src/components/sign-in-form.tsx | 151 +++-- apps/web/src/routeTree.gen.ts | 237 ++++---- apps/web/src/routes/dashboard.domains.tsx | 48 +- .../routes/dashboard.domains_.$domainId.tsx | 29 +- ...ins.new.tsx => dashboard.domains_.new.tsx} | 19 +- apps/web/src/routes/dashboard.notes.tsx | 20 +- .../src/routes/dashboard.notes_.$noteId.tsx | 20 +- ...notes.new.tsx => dashboard.notes_.new.tsx} | 19 +- apps/web/src/routes/dashboard.servers.tsx | 20 +- .../routes/dashboard.servers_.$serverId.tsx | 61 +- ...ers.new.tsx => dashboard.servers_.new.tsx} | 17 +- apps/web/src/routes/dashboard.services.tsx | 20 +- .../routes/dashboard.services_.$serviceId.tsx | 46 +- ...es.new.tsx => dashboard.services_.new.tsx} | 48 +- docs/minmon-homelab-report.tex | 559 ++++++++++++++++++ docs/report-outline.md | 165 ++++-- docs/run-instructions.md | 101 +++- docs/uml-guidance.md | 88 ++- packages/api/src/routers/domains.ts | 4 +- packages/api/src/routers/notes.ts | 4 +- packages/api/src/routers/servers.ts | 2 +- packages/api/src/routers/services.ts | 4 +- packages/ui/src/components/select.tsx | 199 +++++++ packages/ui/src/styles/globals.css | 199 +++++-- 26 files changed, 1700 insertions(+), 505 deletions(-) rename apps/web/src/routes/{dashboard.domains.new.tsx => dashboard.domains_.new.tsx} (86%) rename apps/web/src/routes/{dashboard.notes.new.tsx => dashboard.notes_.new.tsx} (86%) rename apps/web/src/routes/{dashboard.servers.new.tsx => dashboard.servers_.new.tsx} (91%) rename apps/web/src/routes/{dashboard.services.new.tsx => dashboard.services_.new.tsx} (66%) create mode 100644 docs/minmon-homelab-report.tex create mode 100644 packages/ui/src/components/select.tsx diff --git a/apps/web/src/components/dashboard-shell.tsx b/apps/web/src/components/dashboard-shell.tsx index a52d923..72a2802 100644 --- a/apps/web/src/components/dashboard-shell.tsx +++ b/apps/web/src/components/dashboard-shell.tsx @@ -58,7 +58,7 @@ export function DashboardShell({

Operations overview for demos and day-to-day admin tasks

- Use this area to review monitored assets, inspect detail states, and stage CRUD forms that can connect to tRPC routers as they are added. + Use this area to review monitored assets, inspect detail states, currently used to monitor my homelab setup.

@@ -91,7 +91,7 @@ export function DashboardShell({