diff --git a/apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/environments/[environmentId]/_components/EnvironmentTabs.tsx b/apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/environments/[environmentId]/_components/EnvironmentTabs.tsx
new file mode 100644
index 000000000..029729cec
--- /dev/null
+++ b/apps/webservice/src/app/[workspaceSlug]/(app)/(deploy)/(raw)/systems/[systemSlug]/(raw)/environments/[environmentId]/_components/EnvironmentTabs.tsx
@@ -0,0 +1,59 @@
+"use client";
+
+import type React from "react";
+import { useState } from "react";
+import { useParams, usePathname, useRouter } from "next/navigation";
+
+import { Tabs, TabsList, TabsTrigger } from "@ctrlplane/ui/tabs";
+
+import { urls } from "~/app/urls";
+
+export const EnvironmentTabs: React.FC = () => {
+ const { workspaceSlug, systemSlug, environmentId } = useParams<{
+ workspaceSlug: string;
+ systemSlug: string;
+ environmentId: string;
+ }>();
+
+ const environmentUrls = urls
+ .workspace(workspaceSlug)
+ .system(systemSlug)
+ .environment(environmentId);
+ const baseUrl = environmentUrls.baseUrl();
+ const overviewUrl = environmentUrls.overview();
+ const deploymentsUrl = environmentUrls.deployments();
+ const resourcesUrl = environmentUrls.resources();
+ const policiesUrl = environmentUrls.policies();
+
+ const pathname = usePathname();
+ const getInitialTab = () => {
+ if (pathname === policiesUrl) return "policies";
+ if (pathname === resourcesUrl) return "resources";
+ if (pathname === deploymentsUrl) return "deployments";
+ if (pathname === baseUrl) return "overview";
+ return "overview";
+ };
+
+ const [activeTab, setActiveTab] = useState(getInitialTab());
+
+ const router = useRouter();
+
+ const onTabChange = (value: string) => {
+ if (value === "overview") router.push(overviewUrl);
+ if (value === "deployments") router.push(deploymentsUrl);
+ if (value === "resources") router.push(resourcesUrl);
+ if (value === "policies") router.push(policiesUrl);
+ setActiveTab(value);
+ };
+
+ return (
+
+ Deployed {formatTimeAgo(deployment.deployedAt)} +
++ {deployment.deployedAt.toLocaleString()} +
++ {formatDuration(deployment.duration)} +
+{deployment.initiatedBy}
+{deployment.resources}
++ Failure occurred during resource configuration step. See + logs for more details. +
+ )} +Validation
++ Configuration validated successfully +
++ Resource Preparation +
++ Resources prepared for deployment +
++ Deployment Execution +
++ {deployment.status === "success" + ? "Completed successfully" + : deployment.status === "failed" + ? "Failed with errors" + : deployment.status === "pending" + ? "Waiting to start" + : "In progress..."} +
+Health Check
++ {deployment.status === "success" + ? "All resources healthy" + : "Pending completion"} +
++ [ + {new Date( + deployment.deployedAt.getTime(), + ).toLocaleTimeString()} + ] Starting deployment of {deployment.name} version{" "} + {deployment.version}... +
++ [ + {new Date( + deployment.deployedAt.getTime() + 15000, + ).toLocaleTimeString()} + ] Connecting to resource cluster... +
++ [ + {new Date( + deployment.deployedAt.getTime() + 32000, + ).toLocaleTimeString()} + ] Validation checks passed. +
++ [ + {new Date( + deployment.deployedAt.getTime() + 48000, + ).toLocaleTimeString()} + ] Creating deployment plan for {deployment.resources}{" "} + resources... +
++ [ + {new Date( + deployment.deployedAt.getTime() + 62000, + ).toLocaleTimeString()} + ] Updating configuration... +
++ [ + {new Date( + deployment.deployedAt.getTime() + 95000, + ).toLocaleTimeString()} + ] Applying changes to resources... +
+ {deployment.status === "success" ? ( + <> ++ [ + {new Date( + deployment.deployedAt.getTime() + 145000, + ).toLocaleTimeString()} + ] Running post-deployment verification... +
++ [ + {new Date( + deployment.deployedAt.getTime() + 180000, + ).toLocaleTimeString()} + ] All health checks passed. +
++ [ + {new Date( + deployment.deployedAt.getTime() + 185000, + ).toLocaleTimeString()} + ] Deployment completed successfully! +
+ > + ) : deployment.status === "failed" ? ( + <> ++ [ + {new Date( + deployment.deployedAt.getTime() + 110000, + ).toLocaleTimeString()} + ] Updating resource '{deployment.name}-1'... +
++ [ + {new Date( + deployment.deployedAt.getTime() + 125000, + ).toLocaleTimeString()} + ] Error: Failed to update resource '{deployment.name} + -1'. +
++ [ + {new Date( + deployment.deployedAt.getTime() + 126000, + ).toLocaleTimeString()} + ] Error details: Configuration validation failed - + insufficient permissions. +
++ [ + {new Date( + deployment.deployedAt.getTime() + 127000, + ).toLocaleTimeString()} + ] Rolling back changes... +
++ [ + {new Date( + deployment.deployedAt.getTime() + 135000, + ).toLocaleTimeString()} + ] Deployment failed. See detailed logs for more + information. +
+ > + ) : ( + <> ++ [ + {new Date( + deployment.deployedAt.getTime() + 105000, + ).toLocaleTimeString()} + ] Currently updating resource {deployment.name}-3... +
++ [{new Date().toLocaleTimeString()}] Deployment in + progress (2/{deployment.resources} resources + completed)... +
+ > + )} ++ {environment.description || "No description provided"} +
+
+ {environment.directory === "" ? "/" : environment.directory}
+
+
+ {key}
+ {value}
+ + These policies are inherited from a parent configuration. + You can override specific settings at the environment + level while maintaining the parent policy structure. +
++ Controls who can approve deployments and what + validation criteria must be met before a deployment + can proceed to this environment. +
++ Approval required for deployments to this + environment.{" "} + + Learn more + +
++ Defines the success requirements for deployments. + Can be set to require all resources to succeed, a + minimum number of resources, or no validation. +
++ Settings that control how deployments are executed + and managed in this environment, including + concurrency and resource limits. +
++ Controls how releases are managed, including how new + versions are handled and how deployments are + sequenced in this environment. +
++ Controls what happens to pending jobs when a new + version is created. You can either keep pending jobs + in the queue or cancel them in favor of the new + version. +
++ Manages which version channels are available and how + versions flow through different stages in this + environment. +
++ Deployment version channels let you establish a + consistent flow of versions. For example, versions + might flow from beta → stable. +
++ Channels assigned to this environment control + which versions can be deployed. Only versions + published to these channels will be deployed + here. +
++ Controls the timing aspects of deployments, + including rollout duration, release intervals, and + deployment windows. +
++ The time over which deployments will be gradually + rolled out to this environment. A longer duration + provides more time to monitor and catch issues + during deployment. +
++ Minimum time that must pass between active releases + to this environment. This "cooling period" helps + ensure stability between deployments. +
+No resource filter set for this environment
+