refactor: improve sidebar

This commit is contained in:
Mauricio Siu
2025-01-19 01:29:29 -06:00
parent 43b7db00f9
commit 5310a559b0
16 changed files with 78 additions and 109 deletions

View File

@@ -1,98 +1,98 @@
import { fs, vol } from "memfs"; import { fs, vol } from "memfs";
vi.mock("node:fs", () => ({ vi.mock("node:fs", () => ({
...fs, ...fs,
default: fs, default: fs,
})); }));
import type { Admin, FileConfig } from "@dokploy/server"; import type { Admin, FileConfig } from "@dokploy/server";
import { import {
createDefaultServerTraefikConfig, createDefaultServerTraefikConfig,
loadOrCreateConfig, loadOrCreateConfig,
updateServerTraefik, updateServerTraefik,
} from "@dokploy/server"; } from "@dokploy/server";
import { beforeEach, expect, test, vi } from "vitest"; import { beforeEach, expect, test, vi } from "vitest";
const baseAdmin: Admin = { const baseAdmin: Admin = {
cleanupCacheApplications: false, cleanupCacheApplications: false,
cleanupCacheOnCompose: false, cleanupCacheOnCompose: false,
cleanupCacheOnPreviews: false, cleanupCacheOnPreviews: false,
createdAt: "", createdAt: "",
authId: "", authId: "",
adminId: "string", adminId: "string",
serverIp: null, serverIp: null,
certificateType: "none", certificateType: "none",
host: null, host: null,
letsEncryptEmail: null, letsEncryptEmail: null,
sshPrivateKey: null, sshPrivateKey: null,
enableDockerCleanup: false, enableDockerCleanup: false,
enableLogRotation: false, enableLogRotation: false,
serversQuantity: 0, serversQuantity: 0,
stripeCustomerId: "", stripeCustomerId: "",
stripeSubscriptionId: "", stripeSubscriptionId: "",
}; };
beforeEach(() => { beforeEach(() => {
vol.reset(); vol.reset();
createDefaultServerTraefikConfig(); createDefaultServerTraefikConfig();
}); });
test("Should read the configuration file", () => { test("Should read the configuration file", () => {
const config: FileConfig = loadOrCreateConfig("dokploy"); const config: FileConfig = loadOrCreateConfig("dokploy");
expect(config.http?.routers?.["dokploy-router-app"]?.service).toBe( expect(config.http?.routers?.["dokploy-router-app"]?.service).toBe(
"dokploy-service-app" "dokploy-service-app",
); );
}); });
test("Should apply redirect-to-https", () => { test("Should apply redirect-to-https", () => {
updateServerTraefik( updateServerTraefik(
{ {
...baseAdmin, ...baseAdmin,
certificateType: "letsencrypt", certificateType: "letsencrypt",
}, },
"example.com" "example.com",
); );
const config: FileConfig = loadOrCreateConfig("dokploy"); const config: FileConfig = loadOrCreateConfig("dokploy");
expect(config.http?.routers?.["dokploy-router-app"]?.middlewares).toContain( expect(config.http?.routers?.["dokploy-router-app"]?.middlewares).toContain(
"redirect-to-https" "redirect-to-https",
); );
}); });
test("Should change only host when no certificate", () => { test("Should change only host when no certificate", () => {
updateServerTraefik(baseAdmin, "example.com"); updateServerTraefik(baseAdmin, "example.com");
const config: FileConfig = loadOrCreateConfig("dokploy"); const config: FileConfig = loadOrCreateConfig("dokploy");
expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined();
}); });
test("Should not touch config without host", () => { test("Should not touch config without host", () => {
const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); const originalConfig: FileConfig = loadOrCreateConfig("dokploy");
updateServerTraefik(baseAdmin, null); updateServerTraefik(baseAdmin, null);
const config: FileConfig = loadOrCreateConfig("dokploy"); const config: FileConfig = loadOrCreateConfig("dokploy");
expect(originalConfig).toEqual(config); expect(originalConfig).toEqual(config);
}); });
test("Should remove websecure if https rollback to http", () => { test("Should remove websecure if https rollback to http", () => {
const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); const originalConfig: FileConfig = loadOrCreateConfig("dokploy");
updateServerTraefik( updateServerTraefik(
{ ...baseAdmin, certificateType: "letsencrypt" }, { ...baseAdmin, certificateType: "letsencrypt" },
"example.com" "example.com",
); );
updateServerTraefik({ ...baseAdmin, certificateType: "none" }, "example.com"); updateServerTraefik({ ...baseAdmin, certificateType: "none" }, "example.com");
const config: FileConfig = loadOrCreateConfig("dokploy"); const config: FileConfig = loadOrCreateConfig("dokploy");
expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined(); expect(config.http?.routers?.["dokploy-router-app-secure"]).toBeUndefined();
expect( expect(
config.http?.routers?.["dokploy-router-app"]?.middlewares config.http?.routers?.["dokploy-router-app"]?.middlewares,
).not.toContain("redirect-to-https"); ).not.toContain("redirect-to-https");
}); });

View File

@@ -75,7 +75,7 @@ export const ShowBackups = ({ id, type }: Props) => {
{data?.length === 0 ? ( {data?.length === 0 ? (
<div className="flex flex-col items-center gap-3"> <div className="flex flex-col items-center gap-3">
<DatabaseBackup className="size-8 text-muted-foreground" /> <DatabaseBackup className="size-8 text-muted-foreground" />
<span className="text-base text-muted-foreground"> <span className="text-base text-muted-foreground text-center">
To create a backup it is required to set at least 1 provider. To create a backup it is required to set at least 1 provider.
Please, go to{" "} Please, go to{" "}
<Link <Link

View File

@@ -200,7 +200,7 @@ export const DockerMonitoring = ({
</div> </div>
</header> </header>
<div className="grid gap-6 md:grid-cols-2"> <div className="grid gap-6 lg:grid-cols-2">
<Card className="bg-background"> <Card className="bg-background">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">CPU Usage</CardTitle> <CardTitle className="text-sm font-medium">CPU Usage</CardTitle>

View File

@@ -77,8 +77,8 @@ export const ShowProjects = () => {
<div className="w-full"> <div className="w-full">
<Card className="h-full bg-sidebar p-2.5 rounded-xl "> <Card className="h-full bg-sidebar p-2.5 rounded-xl ">
<div className="rounded-xl bg-background shadow-md "> <div className="rounded-xl bg-background shadow-md ">
<div className="flex justify-between gap-4 w-full items-center"> <div className="flex justify-between gap-4 w-full items-center flex-wrap p-6">
<CardHeader className=""> <CardHeader className="p-0">
<CardTitle className="text-xl flex flex-row gap-2"> <CardTitle className="text-xl flex flex-row gap-2">
<FolderInput className="size-6 text-muted-foreground self-center" /> <FolderInput className="size-6 text-muted-foreground self-center" />
Projects Projects
@@ -87,7 +87,7 @@ export const ShowProjects = () => {
Create and manage your projects Create and manage your projects
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<div className=" px-4 "> <div className="">
<HandleProject /> <HandleProject />
</div> </div>
</div> </div>

View File

@@ -50,7 +50,7 @@ export const ShowCertificates = () => {
{data?.length === 0 ? ( {data?.length === 0 ? (
<div className="flex flex-col items-center gap-3 min-h-[25vh] justify-center"> <div className="flex flex-col items-center gap-3 min-h-[25vh] justify-center">
<ShieldCheck className="size-8 self-center text-muted-foreground" /> <ShieldCheck className="size-8 self-center text-muted-foreground" />
<span className="text-base text-muted-foreground"> <span className="text-base text-muted-foreground text-center">
You don't have any certificates created You don't have any certificates created
</span> </span>
<AddCertificate /> <AddCertificate />

View File

@@ -47,7 +47,7 @@ export const ShowNotifications = () => {
{data?.length === 0 ? ( {data?.length === 0 ? (
<div className="flex flex-col items-center gap-3 min-h-[25vh] justify-center"> <div className="flex flex-col items-center gap-3 min-h-[25vh] justify-center">
<Bell /> <Bell />
<span className="text-base text-muted-foreground"> <span className="text-base text-muted-foreground text-center">
To send notifications it is required to set at least 1 To send notifications it is required to set at least 1
provider. provider.
</span> </span>

View File

@@ -60,7 +60,7 @@ export function NodeCard({ node, serverId }: Props) {
<div className="font-medium">{node.Hostname}</div> <div className="font-medium">{node.Hostname}</div>
<Badge variant="green">{node.ManagerStatus || "Worker"}</Badge> <Badge variant="green">{node.ManagerStatus || "Worker"}</Badge>
</div> </div>
<div className="flex flex-wrap items-center space-x-4"> <div className="flex flex-wrap items-center gap-4">
<Badge variant="green">TLS Status: {node.TLSStatus}</Badge> <Badge variant="green">TLS Status: {node.TLSStatus}</Badge>
<Badge variant="blue">Availability: {node.Availability}</Badge> <Badge variant="blue">Availability: {node.Availability}</Badge>
</div> </div>

View File

@@ -72,7 +72,7 @@ export default function SwarmMonitorCard({ serverId }: Props) {
return ( return (
<Card className="h-full bg-sidebar p-2.5 rounded-xl mx-auto w-full"> <Card className="h-full bg-sidebar p-2.5 rounded-xl mx-auto w-full">
<div className="rounded-xl bg-background shadow-md p-6 flex flex-col gap-4"> <div className="rounded-xl bg-background shadow-md p-6 flex flex-col gap-4">
<header className="flex items-center justify-between"> <header className="flex items-center flex-wrap gap-4 justify-between">
<div className="space-y-1"> <div className="space-y-1">
<CardTitle className="text-xl flex flex-row gap-2"> <CardTitle className="text-xl flex flex-row gap-2">
<WorkflowIcon className="size-6 text-muted-foreground self-center" /> <WorkflowIcon className="size-6 text-muted-foreground self-center" />

View File

@@ -5,9 +5,5 @@ interface Props {
} }
export const DashboardLayout = ({ children }: Props) => { export const DashboardLayout = ({ children }: Props) => {
return ( return <Page>{children}</Page>;
<Page>
<div>{children}</div>
</Page>
);
}; };

View File

@@ -11,7 +11,7 @@ interface Props {
export const OnboardingLayout = ({ children }: Props) => { export const OnboardingLayout = ({ children }: Props) => {
return ( return (
<div className="container relative min-h-svh flex-col items-center justify-center flex lg:max-w-none lg:grid lg:grid-cols-2 lg:px-0 w-full"> <div className="container relative min-h-svh flex-col items-center justify-center flex lg:max-w-none lg:grid lg:grid-cols-2 lg:px-0 w-full">
<div className="relative hidden h-full flex-col p-10 text-white dark:border-r lg:flex"> <div className="relative hidden h-full flex-col p-10 text-primary dark:border-r lg:flex">
<div className="absolute inset-0 bg-muted" /> <div className="absolute inset-0 bg-muted" />
<Link <Link
href="https://dokploy.com" href="https://dokploy.com"
@@ -22,35 +22,16 @@ export const OnboardingLayout = ({ children }: Props) => {
</Link> </Link>
<div className="relative z-20 mt-auto"> <div className="relative z-20 mt-auto">
<blockquote className="space-y-2"> <blockquote className="space-y-2">
<p className="text-lg"> <p className="text-lg text-primary">
&ldquo;The Open Source alternative to Netlify, Vercel, &ldquo;The Open Source alternative to Netlify, Vercel,
Heroku.&rdquo; Heroku.&rdquo;
</p> </p>
{/* <footer className="text-sm">Sofia Davis</footer> */}
</blockquote> </blockquote>
</div> </div>
</div> </div>
<div className="w-full"> <div className="w-full">
<div className="flex w-full flex-col justify-center space-y-6 max-w-lg mx-auto"> <div className="flex w-full flex-col justify-center space-y-6 max-w-lg mx-auto">
{children} {children}
{/* <p className="px-8 text-center text-sm text-muted-foreground">
By clicking continue, you agree to our{" "}
<Link
href="/terms"
className="underline underline-offset-4 hover:text-primary"
>
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="underline underline-offset-4 hover:text-primary"
>
Privacy Policy
</Link>
.
</p> */}
</div> </div>
<div className="flex items-center gap-4 justify-center absolute bottom-4 right-4 text-muted-foreground"> <div className="flex items-center gap-4 justify-center absolute bottom-4 right-4 text-muted-foreground">
<Button variant="ghost" size="icon"> <Button variant="ghost" size="icon">

View File

@@ -5,9 +5,5 @@ interface Props {
} }
export const ProjectLayout = ({ children }: Props) => { export const ProjectLayout = ({ children }: Props) => {
return ( return <Page>{children}</Page>;
<div>
<Page>{children}</Page>
</div>
);
}; };

View File

@@ -676,7 +676,7 @@ export default function Page({ children }: Props) {
<Separator orientation="vertical" className="mr-2 h-4" /> <Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb> <Breadcrumb>
<BreadcrumbList> <BreadcrumbList>
<BreadcrumbItem className="hidden md:block"> <BreadcrumbItem className="block">
<BreadcrumbLink asChild> <BreadcrumbLink asChild>
<Link <Link
href={activeItem?.url || "/"} href={activeItem?.url || "/"}
@@ -686,7 +686,7 @@ export default function Page({ children }: Props) {
</Link> </Link>
</BreadcrumbLink> </BreadcrumbLink>
</BreadcrumbItem> </BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" /> <BreadcrumbSeparator className="block" />
<BreadcrumbItem> <BreadcrumbItem>
<BreadcrumbPage>{activeItem?.title}</BreadcrumbPage> <BreadcrumbPage>{activeItem?.title}</BreadcrumbPage>
</BreadcrumbItem> </BreadcrumbItem>

View File

@@ -28,7 +28,7 @@ export const BreadcrumbSidebar = ({ list }: Props) => {
<BreadcrumbList> <BreadcrumbList>
{list.map((item, index) => ( {list.map((item, index) => (
<Fragment key={item.name}> <Fragment key={item.name}>
<BreadcrumbItem className="hidden md:block"> <BreadcrumbItem className="block">
<BreadcrumbLink href={item.href} asChild={!!item.href}> <BreadcrumbLink href={item.href} asChild={!!item.href}>
{item.href ? ( {item.href ? (
<Link href={item.href}>{item.name}</Link> <Link href={item.href}>{item.name}</Link>
@@ -37,7 +37,7 @@ export const BreadcrumbSidebar = ({ list }: Props) => {
)} )}
</BreadcrumbLink> </BreadcrumbLink>
</BreadcrumbItem> </BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" /> <BreadcrumbSeparator className="block" />
</Fragment> </Fragment>
))} ))}
</BreadcrumbList> </BreadcrumbList>

View File

@@ -329,7 +329,7 @@ const SidebarInset = React.forwardRef<
<main <main
ref={ref} ref={ref}
className={cn( className={cn(
"relative flex min-h-svh flex-1 flex-col bg-background", "relative flex min-h-svh overflow-auto w-full flex-col bg-background",
"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow", "peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
className, className,
)} )}

View File

@@ -239,8 +239,8 @@ const Project = (
<div className="w-full"> <div className="w-full">
<Card className="h-full bg-sidebar p-2.5 rounded-xl "> <Card className="h-full bg-sidebar p-2.5 rounded-xl ">
<div className="rounded-xl bg-background shadow-md "> <div className="rounded-xl bg-background shadow-md ">
<div className="flex justify-between gap-4 w-full items-center"> <div className="flex justify-between gap-4 w-full items-center flex-wrap p-6">
<CardHeader className=""> <CardHeader className="p-0">
<CardTitle className="text-xl flex flex-row gap-2"> <CardTitle className="text-xl flex flex-row gap-2">
<FolderInput className="size-6 text-muted-foreground self-center" /> <FolderInput className="size-6 text-muted-foreground self-center" />
{data?.name} {data?.name}
@@ -248,7 +248,7 @@ const Project = (
<CardDescription>{data?.description}</CardDescription> <CardDescription>{data?.description}</CardDescription>
</CardHeader> </CardHeader>
{(auth?.rol === "admin" || user?.canCreateServices) && ( {(auth?.rol === "admin" || user?.canCreateServices) && (
<div className="flex flex-row gap-4 flex-wrap px-4"> <div className="flex flex-row gap-4 flex-wrap">
<ProjectEnvironment projectId={projectId}> <ProjectEnvironment projectId={projectId}>
<Button variant="outline">Project Environment</Button> <Button variant="outline">Project Environment</Button>
</ProjectEnvironment> </ProjectEnvironment>

View File

@@ -8,11 +8,7 @@ import type { ReactElement } from "react";
import superjson from "superjson"; import superjson from "superjson";
const Dashboard = () => { const Dashboard = () => {
return ( return <SwarmMonitorCard />;
<>
<SwarmMonitorCard />
</>
);
}; };
export default Dashboard; export default Dashboard;