diff --git a/apps/dokploy/components/dashboard/home/show-home.tsx b/apps/dokploy/components/dashboard/home/show-home.tsx new file mode 100644 index 000000000..6e40d794a --- /dev/null +++ b/apps/dokploy/components/dashboard/home/show-home.tsx @@ -0,0 +1,223 @@ +import { formatDistanceToNow } from "date-fns"; +import { Plus, Rocket } from "lucide-react"; +import Link from "next/link"; +import { useMemo } from "react"; +import { Button } from "@/components/ui/button"; +import { api } from "@/utils/api"; + +type DeploymentStatus = "idle" | "running" | "done" | "error"; + +const statusDotClass: Record = { + done: "bg-muted-foreground/60", + running: "bg-amber-500", + error: "bg-red-500", + idle: "bg-muted-foreground/30", +}; + +function getServiceInfo(d: any) { + const app = d.application; + const comp = d.compose; + if (app?.environment?.project && app.environment) { + return { + name: app.name as string, + environment: app.environment.name as string, + projectName: app.environment.project.name as string, + href: `/dashboard/project/${app.environment.project.projectId}/environment/${app.environment.environmentId}/services/application/${app.applicationId}`, + }; + } + if (comp?.environment?.project && comp.environment) { + return { + name: comp.name as string, + environment: comp.environment.name as string, + projectName: comp.environment.project.name as string, + href: `/dashboard/project/${comp.environment.project.projectId}/environment/${comp.environment.environmentId}/services/compose/${comp.composeId}`, + }; + } + return null; +} + +function StatCard({ + label, + value, + delta, +}: { + label: string; + value: string; + delta?: string; +}) { + return ( +
+ + {label} + +
+ {value} + {delta && ( + + {delta} + + )} +
+
+ ); +} + +export const ShowHome = () => { + const { data: auth } = api.user.get.useQuery(); + const { data: projects } = api.project.all.useQuery(); + const { data: servers } = api.server.all.useQuery(); + const { data: permissions } = api.user.getPermissions.useQuery(); + const canReadDeployments = !!permissions?.deployment.read; + const { data: deployments } = api.deployment.allCentralized.useQuery( + undefined, + { + enabled: canReadDeployments, + refetchInterval: 10000, + }, + ); + + const firstName = auth?.user?.firstName?.trim(); + + const { totals, serverCount } = useMemo(() => { + let applications = 0; + let compose = 0; + let databases = 0; + const dbKeys = [ + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "libsql", + ] as const; + for (const p of projects ?? []) { + for (const env of p.environments ?? []) { + applications += env.applications?.length ?? 0; + compose += env.compose?.length ?? 0; + for (const key of dbKeys) { + databases += (env as any)[key]?.length ?? 0; + } + } + } + return { + totals: { + projects: projects?.length ?? 0, + applications, + compose, + databases, + services: applications + compose + databases, + }, + serverCount: servers?.length ?? 0, + }; + }, [projects, servers]); + + const recentDeployments = useMemo(() => { + if (!deployments) return []; + return [...deployments] + .sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), + ) + .slice(0, 10); + }, [deployments]); + + return ( +
+
+
+

+ {firstName ? `Welcome back, ${firstName}` : "Welcome back"} +

+

+ {totals.services} services across {serverCount}{" "} + {serverCount === 1 ? "server" : "servers"} · {totals.projects}{" "} + {totals.projects === 1 ? "project" : "projects"} +

+
+ +
+ +
+ + + + +
+ +
+
+
+ +

Recent deployments

+
+ {canReadDeployments && ( + + view all → + + )} +
+ {!canReadDeployments ? ( +
+ You do not have permission to view deployments. +
+ ) : recentDeployments.length === 0 ? ( +
+ No deployments yet. +
+ ) : ( +
    + {recentDeployments.map((d) => { + const info = getServiceInfo(d); + if (!info) return null; + const status = (d.status ?? "idle") as DeploymentStatus; + return ( +
  • + + +
    + + {info.name} + + + {info.projectName} · {info.environment} + +
    + + {status} + + + {formatDistanceToNow(new Date(d.createdAt), { + addSuffix: true, + })} + + + logs → + + +
  • + ); + })} +
+ )} +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/search-command.tsx b/apps/dokploy/components/dashboard/search-command.tsx index 9946a54a9..841728dc1 100644 --- a/apps/dokploy/components/dashboard/search-command.tsx +++ b/apps/dokploy/components/dashboard/search-command.tsx @@ -167,7 +167,7 @@ export const SearchCommand = () => {