Created
diff --git a/apps/dokploy/components/shared/advance-breadcrumb.tsx b/apps/dokploy/components/shared/advance-breadcrumb.tsx
new file mode 100644
index 000000000..bdb65aa36
--- /dev/null
+++ b/apps/dokploy/components/shared/advance-breadcrumb.tsx
@@ -0,0 +1,564 @@
+import type { ServiceType } from "@dokploy/server/db/schema";
+import {
+ Check,
+ ChevronDown,
+ ChevronRight,
+ CircuitBoard,
+ FolderInput,
+ GlobeIcon,
+ X,
+} from "lucide-react";
+import { useRouter } from "next/router";
+import { useEffect, useState } from "react";
+import {
+ MariadbIcon,
+ MongodbIcon,
+ MysqlIcon,
+ PostgresqlIcon,
+ RedisIcon,
+} from "@/components/icons/data-tools-icons";
+import { Button } from "@/components/ui/button";
+import {
+ Command,
+ CommandEmpty,
+ CommandGroup,
+ CommandInput,
+ CommandItem,
+ CommandList,
+} from "@/components/ui/command";
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from "@/components/ui/popover";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { Separator } from "@/components/ui/separator";
+import { SidebarTrigger } from "@/components/ui/sidebar";
+import { api } from "@/utils/api";
+
+interface AdvanceBreadcrumbProps {
+ projectId?: string;
+ environmentId?: string;
+ serviceId?: string;
+ serviceType?: ServiceType;
+}
+
+const getServiceIcon = (type: ServiceType, className = "size-4") => {
+ const icons: Record
= {
+ application: ,
+ compose: ,
+ postgres: ,
+ mysql: ,
+ mariadb: ,
+ redis: ,
+ mongo: ,
+ };
+
+ return icons[type];
+};
+
+interface ServiceItem {
+ id: string;
+ name: string;
+ type: ServiceType;
+ appName?: string;
+}
+
+export const AdvanceBreadcrumb = ({
+ projectId,
+ environmentId,
+ serviceId,
+}: AdvanceBreadcrumbProps) => {
+ const router = useRouter();
+ const [projectOpen, setProjectOpen] = useState(false);
+ const [serviceOpen, setServiceOpen] = useState(false);
+ const [environmentOpen, setEnvironmentOpen] = useState(false);
+ const [projectSearch, setProjectSearch] = useState("");
+ const [serviceSearch, setServiceSearch] = useState("");
+ const [expandedProjectId, setExpandedProjectId] = useState(
+ null,
+ );
+
+ // Fetch all projects
+ const { data: allProjects } = api.project.all.useQuery();
+
+ // Fetch current project data
+ const { data: currentProject } = api.project.one.useQuery(
+ { projectId: projectId || "" },
+ { enabled: !!projectId },
+ );
+
+ // Fetch current environment
+ const { data: currentEnvironment } = api.environment.one.useQuery(
+ { environmentId: environmentId || "" },
+ { enabled: !!environmentId },
+ );
+
+ // Fetch environments for current project
+ const { data: projectEnvironments } = api.environment.byProjectId.useQuery(
+ { projectId: projectId || "" },
+ { enabled: !!projectId },
+ );
+
+ // Close dropdowns on escape key
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.key === "Escape") {
+ setProjectOpen(false);
+ setServiceOpen(false);
+ setEnvironmentOpen(false);
+ }
+ };
+ window.addEventListener("keydown", handleKeyDown);
+ return () => window.removeEventListener("keydown", handleKeyDown);
+ }, []);
+
+ // Extract services from current environment
+ const services: ServiceItem[] = [];
+ if (currentEnvironment) {
+ currentEnvironment.applications?.forEach(
+ (app: { applicationId: string; name: string; appName: string }) => {
+ services.push({
+ id: app.applicationId,
+ name: app.name,
+ type: "application",
+ appName: app.appName,
+ });
+ },
+ );
+ currentEnvironment.compose?.forEach(
+ (comp: { composeId: string; name: string; appName: string }) => {
+ services.push({
+ id: comp.composeId,
+ name: comp.name,
+ type: "compose",
+ appName: comp.appName,
+ });
+ },
+ );
+ currentEnvironment.postgres?.forEach(
+ (pg: { postgresId: string; name: string; appName: string }) => {
+ services.push({
+ id: pg.postgresId,
+ name: pg.name,
+ type: "postgres",
+ appName: pg.appName,
+ });
+ },
+ );
+ currentEnvironment.mysql?.forEach(
+ (my: { mysqlId: string; name: string; appName: string }) => {
+ services.push({
+ id: my.mysqlId,
+ name: my.name,
+ type: "mysql",
+ appName: my.appName,
+ });
+ },
+ );
+ currentEnvironment.mariadb?.forEach(
+ (maria: { mariadbId: string; name: string; appName: string }) => {
+ services.push({
+ id: maria.mariadbId,
+ name: maria.name,
+ type: "mariadb",
+ appName: maria.appName,
+ });
+ },
+ );
+ currentEnvironment.redis?.forEach(
+ (red: { redisId: string; name: string; appName: string }) => {
+ services.push({
+ id: red.redisId,
+ name: red.name,
+ type: "redis",
+ appName: red.appName,
+ });
+ },
+ );
+ currentEnvironment.mongo?.forEach(
+ (mon: { mongoId: string; name: string; appName: string }) => {
+ services.push({
+ id: mon.mongoId,
+ name: mon.name,
+ type: "mongo",
+ appName: mon.appName,
+ });
+ },
+ );
+ }
+
+ // Get current service
+ const currentService = services.find((s) => s.id === serviceId);
+
+ // Navigate to project's default environment
+ const handleProjectSelect = (
+ selectedProjectId: string,
+ selectedEnvironmentId?: string,
+ ) => {
+ const project = allProjects?.find((p) => p.projectId === selectedProjectId);
+ if (project && project.environments.length > 0) {
+ // Use provided environment or find production environment or use the first one
+ const targetEnvId =
+ selectedEnvironmentId ||
+ project.environments.find((e) => e.name === "production")
+ ?.environmentId ||
+ project.environments[0]?.environmentId;
+
+ router.push(
+ `/dashboard/project/${selectedProjectId}/environment/${targetEnvId}`,
+ );
+ }
+ setProjectOpen(false);
+ setExpandedProjectId(null);
+ };
+
+ // Navigate to environment
+ const handleEnvironmentSelect = (envId: string) => {
+ router.push(`/dashboard/project/${projectId}/environment/${envId}`);
+ setEnvironmentOpen(false);
+ };
+
+ // Navigate to service
+ const handleServiceSelect = (service: ServiceItem) => {
+ const serviceTypePath =
+ service.type === "application" ? "application" : service.type;
+ router.push(
+ `/dashboard/project/${projectId}/environment/${environmentId}/services/${serviceTypePath}/${service.id}`,
+ );
+ setServiceOpen(false);
+ };
+
+ // Filter projects based on search
+ const filteredProjects =
+ allProjects?.filter(
+ (p) =>
+ p.name.toLowerCase().includes(projectSearch.toLowerCase()) ||
+ p.description?.toLowerCase().includes(projectSearch.toLowerCase()),
+ ) || [];
+
+ // Filter services based on search
+ const filteredServices = services.filter(
+ (s) =>
+ s.name.toLowerCase().includes(serviceSearch.toLowerCase()) ||
+ s.appName?.toLowerCase().includes(serviceSearch.toLowerCase()),
+ );
+
+ // If we're just on the projects page, show simple breadcrumb
+ if (!projectId) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+ {/* Project Selector */}
+
+
+
+
+
+
+
+
+
+ Esc
+
+
+
+ No projects found.
+
+
+ {filteredProjects.map((project) => {
+ const totalServices = project.environments.reduce(
+ (total, env) =>
+ total +
+ (env.applications?.length || 0) +
+ (env.compose?.length || 0) +
+ (env.postgres?.length || 0) +
+ (env.mysql?.length || 0) +
+ (env.mariadb?.length || 0) +
+ (env.redis?.length || 0) +
+ (env.mongo?.length || 0),
+ 0,
+ );
+ const isSelected = project.projectId === projectId;
+ const isExpanded =
+ expandedProjectId === project.projectId;
+
+ return (
+
+
{
+ if (project.environments.length > 1) {
+ setExpandedProjectId(
+ isExpanded ? null : project.projectId,
+ );
+ } else {
+ handleProjectSelect(project.projectId);
+ }
+ }}
+ className="flex items-center justify-between py-3 px-2 cursor-pointer"
+ >
+
+
+ {project.name.slice(0, 2)}
+
+
+
+ {project.name}
+
+
+ {project.environments.length} env
+ {project.environments.length !== 1
+ ? "s"
+ : ""}{" "}
+ ยท {totalServices} service
+ {totalServices !== 1 ? "s" : ""}
+
+
+
+
+ {isSelected && (
+
+ )}
+ {project.environments.length > 1 && (
+
+ )}
+
+
+
+ {/* Expanded environments */}
+ {isExpanded && (
+
+ {project.environments.map((env) => {
+ const envServices =
+ (env.applications?.length || 0) +
+ (env.compose?.length || 0) +
+ (env.postgres?.length || 0) +
+ (env.mysql?.length || 0) +
+ (env.mariadb?.length || 0) +
+ (env.redis?.length || 0) +
+ (env.mongo?.length || 0);
+ const isEnvSelected =
+ env.environmentId === environmentId;
+
+ return (
+
+ handleProjectSelect(
+ project.projectId,
+ env.environmentId,
+ )
+ }
+ className="flex items-center justify-between py-2 px-2 cursor-pointer text-sm"
+ >
+
+
{env.name}
+
+ {envServices} service
+ {envServices !== 1 ? "s" : ""}
+
+
+ {isEnvSelected && (
+
+ )}
+
+ );
+ })}
+
+ )}
+
+ );
+ })}
+
+
+
+
+
+
+
+ {/* Environment Selector */}
+ {projectEnvironments && projectEnvironments.length > 1 && (
+
+
+
+
+
+
+ {projectEnvironments.map((env) => {
+ const isSelected = env.environmentId === environmentId;
+ return (
+
+ );
+ })}
+
+
+
+ )}
+
+ {projectEnvironments && projectEnvironments.length === 1 && (
+
+ {currentEnvironment?.name || "production"}
+
+ )}
+
+ {/* Service Selector - only show when viewing a service */}
+ {serviceId && currentService && (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ Esc
+
+
+
+ No services found.
+
+
+ {filteredServices.map((service) => {
+ const isSelected = service.id === serviceId;
+ return (
+ handleServiceSelect(service)}
+ className="flex items-center justify-between py-3 px-2 cursor-pointer"
+ >
+
+
+ {getServiceIcon(service.type)}
+
+
+
+ {service.name}
+
+
+ {service.type}
+
+
+
+ {isSelected && (
+
+ )}
+
+ );
+ })}
+
+
+
+
+
+
+
+ {/* Close button to go back to environment */}
+
+ >
+ )}
+
+
+
+ );
+};
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx
index af901311e..3fe6b7845 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId].tsx
@@ -44,8 +44,8 @@ import {
RedisIcon,
} from "@/components/icons/data-tools-icons";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
+import { AdvanceBreadcrumb } from "@/components/shared/advance-breadcrumb";
import { AlertBlock } from "@/components/shared/alert-block";
-import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
import { DateTooltip } from "@/components/shared/date-tooltip";
import { DialogAction } from "@/components/shared/dialog-action";
import { FocusShortcutInput } from "@/components/shared/focus-shortcut-input";
@@ -865,18 +865,7 @@ const EnvironmentPage = (
return (
-
+
Environment: {currentEnvironment.name} | {projectData?.name} | Dokploy
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx
index 7917bd97c..2aa399474 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/application/[applicationId].tsx
@@ -34,7 +34,7 @@ import { DeleteService } from "@/components/dashboard/compose/delete-service";
import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring";
import { ContainerPaidMonitoring } from "@/components/dashboard/monitoring/paid/container/show-paid-container-monitoring";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
-import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
+import { AdvanceBreadcrumb } from "@/components/shared/advance-breadcrumb";
import { StatusTooltip } from "@/components/shared/status-tooltip";
import { Badge } from "@/components/ui/badge";
import {
@@ -103,21 +103,11 @@ const Service = (
return (
-
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx
index 1d6902c59..9b479773a 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/compose/[composeId].tsx
@@ -30,7 +30,7 @@ import { ShowBackups } from "@/components/dashboard/database/backups/show-backup
import { ComposeFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-compose-monitoring";
import { ComposePaidMonitoring } from "@/components/dashboard/monitoring/paid/container/show-paid-compose-monitoring";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
-import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
+import { AdvanceBreadcrumb } from "@/components/shared/advance-breadcrumb";
import { StatusTooltip } from "@/components/shared/status-tooltip";
import { Badge } from "@/components/ui/badge";
import {
@@ -92,21 +92,11 @@ const Service = (
return (
-
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx
index 0a1e8501d..2f45ad0cf 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mariadb/[mariadbId].tsx
@@ -23,7 +23,7 @@ import { ContainerPaidMonitoring } from "@/components/dashboard/monitoring/paid/
import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings";
import { MariadbIcon } from "@/components/icons/data-tools-icons";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
-import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
+import { AdvanceBreadcrumb } from "@/components/shared/advance-breadcrumb";
import { StatusTooltip } from "@/components/shared/status-tooltip";
import { Badge } from "@/components/ui/badge";
import {
@@ -74,21 +74,11 @@ const Mariadb = (
return (
-
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx
index bae83cb2b..a810ca607 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mongo/[mongoId].tsx
@@ -23,7 +23,7 @@ import { ContainerPaidMonitoring } from "@/components/dashboard/monitoring/paid/
import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings";
import { MongodbIcon } from "@/components/icons/data-tools-icons";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
-import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
+import { AdvanceBreadcrumb } from "@/components/shared/advance-breadcrumb";
import { StatusTooltip } from "@/components/shared/status-tooltip";
import { Badge } from "@/components/ui/badge";
import {
@@ -73,21 +73,11 @@ const Mongo = (
return (
-
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx
index ba2b9d8a0..bf7dae0e6 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/mysql/[mysqlId].tsx
@@ -23,7 +23,7 @@ import { UpdateMysql } from "@/components/dashboard/mysql/update-mysql";
import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings";
import { MysqlIcon } from "@/components/icons/data-tools-icons";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
-import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
+import { AdvanceBreadcrumb } from "@/components/shared/advance-breadcrumb";
import { StatusTooltip } from "@/components/shared/status-tooltip";
import { Badge } from "@/components/ui/badge";
import {
@@ -72,21 +72,11 @@ const MySql = (
return (
-
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx
index 1d90e3e13..68356c5df 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/postgres/[postgresId].tsx
@@ -23,7 +23,7 @@ import { UpdatePostgres } from "@/components/dashboard/postgres/update-postgres"
import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings";
import { PostgresqlIcon } from "@/components/icons/data-tools-icons";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
-import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
+import { AdvanceBreadcrumb } from "@/components/shared/advance-breadcrumb";
import { StatusTooltip } from "@/components/shared/status-tooltip";
import { Badge } from "@/components/ui/badge";
import {
@@ -72,21 +72,11 @@ const Postgresql = (
return (
-
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx
index 47eb82a74..c40e76bd3 100644
--- a/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx
+++ b/apps/dokploy/pages/dashboard/project/[projectId]/environment/[environmentId]/services/redis/[redisId].tsx
@@ -22,7 +22,7 @@ import { UpdateRedis } from "@/components/dashboard/redis/update-redis";
import { ShowDatabaseAdvancedSettings } from "@/components/dashboard/shared/show-database-advanced-settings";
import { RedisIcon } from "@/components/icons/data-tools-icons";
import { DashboardLayout } from "@/components/layouts/dashboard-layout";
-import { BreadcrumbSidebar } from "@/components/shared/breadcrumb-sidebar";
+import { AdvanceBreadcrumb } from "@/components/shared/advance-breadcrumb";
import { StatusTooltip } from "@/components/shared/status-tooltip";
import { Badge } from "@/components/ui/badge";
import {
@@ -72,21 +72,11 @@ const Redis = (
return (
-