Skip to content

Commit

Permalink
fix: Collapse folders in system deployments view
Browse files Browse the repository at this point in the history
  • Loading branch information
adityachoudhari26 committed Mar 3, 2025
1 parent c1bda4f commit 6ac83aa
Show file tree
Hide file tree
Showing 12 changed files with 5,330 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type * as SCHEMA from "@ctrlplane/db/schema";
import React, { version } from "react";
import Link from "next/link";
import { useParams } from "next/navigation";
import { format } from "date-fns";
import { useInView } from "react-intersection-observer";

import { Skeleton } from "@ctrlplane/ui/skeleton";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@ctrlplane/ui/tooltip";

import { StatusIcon } from "~/app/[workspaceSlug]/(appv2)/(deploy)/_components/deployments/environment-cell/StatusIcon";
import { urls } from "~/app/urls";
import { api } from "~/trpc/react";

type DeploymentDirectoryCellProps = {
directory: {
path: string;
environments: SCHEMA.Environment[];
};
deployment: SCHEMA.Deployment;
systemSlug: string;
};

export const DeploymentDirectoryCell: React.FC<
DeploymentDirectoryCellProps
> = ({ directory, deployment, systemSlug }) => {
const { workspaceSlug } = useParams<{ workspaceSlug: string }>();
const { ref, inView } = useInView();

const { data: releaseResult, isLoading: isReleaseLoading } =
api.release.list.useQuery(
{ deploymentId: deployment.id, limit: 1 },
{ enabled: inView && directory.environments.length > 0 },
);

const { data: statusesResult, isLoading: isStatusesLoading } =
api.release.status.bySystemDirectory.useQuery(
{ systemId: deployment.systemId, directory: directory.path },
{ enabled: inView && releaseResult != null },
);
const isLoading = isReleaseLoading || isStatusesLoading;

const release = releaseResult?.items[0];
const statuses = statusesResult ?? [];

const getReleaseUrl = urls
.workspace(workspaceSlug)
.system(systemSlug)
.deployment(deployment.slug).release;

return (
<div className="flex w-full items-center justify-center" ref={ref}>
{(!inView || isLoading) && (
<div className="flex h-full w-full items-center gap-2">
<Skeleton className="h-6 w-6 rounded-full" />
<div className="flex flex-col gap-2">
<Skeleton className="h-[16px] w-20 rounded-full" />
<Skeleton className="h-3 w-20 rounded-full" />
</div>
</div>
)}

{inView && !isLoading && release == null && (
<p className="text-xs text-muted-foreground/70">No versions released</p>
)}

{inView && !isLoading && release != null && (
<div className="flex w-full items-center justify-between rounded-md p-2 hover:bg-secondary/50">
<Link
href={getReleaseUrl(release.id).baseUrl()}
className="flex w-full items-center gap-2"
>
<StatusIcon statuses={statuses.map((s) => s.job.status)} />
<div className="w-full">
<div className="flex items-center gap-2">
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<div className="max-w-36 truncate font-semibold">
<span className="whitespace-nowrap">
{release.version}
</span>
</div>
</TooltipTrigger>
<TooltipContent className="max-w-[200px]">
{version}
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<div className="text-xs text-muted-foreground">
{format(release.createdAt, "MMM d, hh:mm aa")}
</div>
</div>
</Link>
</div>
)}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
IconShip,
IconTrash,
} from "@tabler/icons-react";
import _ from "lodash";

import { Badge } from "@ctrlplane/ui/badge";
import { Button } from "@ctrlplane/ui/button";
Expand All @@ -27,17 +28,32 @@ import {

import { CreateDeploymentDialog } from "~/app/[workspaceSlug]/(appv2)/_components/deployments/CreateDeployment";
import { DeleteSystemDialog } from "~/app/[workspaceSlug]/(appv2)/_components/system/DeleteSystemDialog";
import { api } from "~/trpc/react";
import { SystemDeploymentSkeleton } from "./SystemDeploymentSkeleton";
import DeploymentTable from "./TableDeployments";

type System = SCHEMA.System & {
deployments: SCHEMA.Deployment[];
environments: SCHEMA.Environment[];
};

export const SystemDeploymentTable: React.FC<{
workspace: SCHEMA.Workspace;
system: System;
}> = ({ workspace, system }) => {
const { data: rootDirsResult, isLoading } =
api.system.directory.listRoots.useQuery(system.id);
if (isLoading) return <SystemDeploymentSkeleton />;

const rootDirs = rootDirsResult?.directories ?? [];
const rootEnvironments = rootDirsResult?.rootEnvironments ?? [];

const numNestedEnvironments = _.sumBy(
rootDirs,
(dir) => dir.environments.length,
);

const numEnvironments = numNestedEnvironments + rootEnvironments.length;

return (
<div key={system.id} className="space-y-4">
<div className="flex w-full items-center justify-between">
Expand Down Expand Up @@ -70,7 +86,7 @@ export const SystemDeploymentTable: React.FC<{
variant="outline"
className="flex items-center gap-1 rounded-full font-normal text-muted-foreground"
>
<IconPlant className="size-3" /> {system.environments.length}
<IconPlant className="size-3" /> {numEnvironments}
</Badge>
</TooltipTrigger>
<TooltipContent>
Expand Down Expand Up @@ -114,8 +130,9 @@ export const SystemDeploymentTable: React.FC<{
<DeploymentTable
workspace={workspace}
systemSlug={system.slug}
environments={system.environments}
environments={rootEnvironments}
deployments={system.deployments}
directories={rootDirs}
/>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"use client";

import type * as SCHEMA from "@ctrlplane/db/schema";
import type { ResourceCondition } from "@ctrlplane/validators/resources";
import Link from "next/link";
import { IconLoader2 } from "@tabler/icons-react";
import { IconFolder, IconLoader2 } from "@tabler/icons-react";
import { isPresent } from "ts-is-present";

import { cn } from "@ctrlplane/ui";
import { Badge } from "@ctrlplane/ui/badge";
Expand All @@ -14,30 +16,28 @@ import {
TableHeader,
TableRow,
} from "@ctrlplane/ui/table";
import {
ComparisonOperator,
FilterType,
} from "@ctrlplane/validators/conditions";

import { LazyDeploymentEnvironmentCell } from "~/app/[workspaceSlug]/(appv2)/(deploy)/_components/deployments/environment-cell/DeploymentEnvironmentCell";
import { urls } from "~/app/urls";
import { api } from "~/trpc/react";
import { DeploymentDirectoryCell } from "./DeploymentDirectoryCell";

const EnvHeader: React.FC<{
environment: SCHEMA.Environment;
workspaceSlug: string;
}> = ({ environment: env, workspaceSlug }) => {
const { data: workspace, isLoading: isWorkspaceLoading } =
api.workspace.bySlug.useQuery(workspaceSlug);
const workspaceId = workspace?.id ?? "";

workspace: SCHEMA.Workspace;
}> = ({ environment: env, workspace }) => {
const filter = env.resourceFilter ?? undefined;
const { data: resourcesResult, isLoading: isResourcesLoading } =
api.resource.byWorkspaceId.list.useQuery(
{ workspaceId, filter, limit: 0 },
{ enabled: workspaceId !== "" && filter != null },
);
const total = resourcesResult?.total ?? 0;

const isLoading = isWorkspaceLoading || isResourcesLoading;
const { data, isLoading } = api.resource.byWorkspaceId.list.useQuery(
{ workspaceId: workspace.id, filter, limit: 0 },
{ enabled: filter != null },
);
const total = data?.total ?? 0;

const envUrl = `/${workspaceSlug}/systems?environment_id=${env.id}`;
const envUrl = `/${workspace.slug}/systems?environment_id=${env.id}`;
return (
<TableHead className="w-[220px] p-2" key={env.id}>
<Link
Expand All @@ -57,12 +57,63 @@ const EnvHeader: React.FC<{
);
};

const DirectoryHeader: React.FC<{
directory: {
path: string;
environments: SCHEMA.Environment[];
};
workspace: SCHEMA.Workspace;
}> = ({ directory, workspace }) => {
const resourceFilters = directory.environments
.map((env) => env.resourceFilter)
.filter(isPresent);
const filter: ResourceCondition | undefined =
resourceFilters.length > 0
? {
type: FilterType.Comparison,
operator: ComparisonOperator.Or,
conditions: resourceFilters,
}
: undefined;

const { data: resourcesResult, isLoading } =
api.resource.byWorkspaceId.list.useQuery(
{ workspaceId: workspace.id, filter, limit: 0 },
{ enabled: filter != null },
);

const total = resourcesResult?.total ?? 0;

return (
<TableHead className="w-[220px] p-2" key={directory.path}>
<div className="flex w-fit items-center gap-2 px-2 py-1 text-white">
<span className=" max-w-32 truncate">{directory.path}</span>

<Badge variant="outline" className="rounded-full text-muted-foreground">
{isLoading && (
<IconLoader2 className="h-3 w-3 animate-spin text-muted-foreground" />
)}
{!isLoading && total}
</Badge>

<Badge variant="outline" className="rounded-full text-muted-foreground">
<IconFolder className="h-4 w-4" strokeWidth={1.5} />
</Badge>
</div>
</TableHead>
);
};

const DeploymentTable: React.FC<{
workspace: SCHEMA.Workspace;
systemSlug: string;
environments: SCHEMA.Environment[];
deployments: SCHEMA.Deployment[];
}> = ({ systemSlug, deployments, environments, workspace }) => {
directories: {
path: string;
environments: SCHEMA.Environment[];
}[];
}> = ({ systemSlug, deployments, environments, workspace, directories }) => {
return (
<div className="scrollbar-thin scrollbar-thumb-neutral-700 scrollbar-track-neutral-800 w-full overflow-x-auto">
<Table className="w-full min-w-max bg-background">
Expand All @@ -72,10 +123,13 @@ const DeploymentTable: React.FC<{
Deployments
</TableHead>
{environments.map((env) => (
<EnvHeader
key={env.id}
environment={env}
workspaceSlug={workspace.slug}
<EnvHeader key={env.id} environment={env} workspace={workspace} />
))}
{directories.map((dir) => (
<DirectoryHeader
key={dir.path}
directory={dir}
workspace={workspace}
/>
))}
<TableCell className="flex-grow" />
Expand Down Expand Up @@ -119,6 +173,17 @@ const DeploymentTable: React.FC<{
</div>
</TableCell>
))}
{directories.map((dir) => (
<TableCell key={dir.path} className="h-[70px] w-[220px]">
<div className="flex h-full w-full justify-center">
<DeploymentDirectoryCell
directory={dir}
deployment={r}
systemSlug={systemSlug}
/>
</div>
</TableCell>
))}
<TableCell className="flex-grow" />
</TableRow>
))}
Expand Down
Loading

0 comments on commit 6ac83aa

Please sign in to comment.