Skip to content

Commit

Permalink
init sidebar refactor (#218)
Browse files Browse the repository at this point in the history
  • Loading branch information
jsbroks authored Nov 18, 2024
1 parent 815959f commit e71a202
Show file tree
Hide file tree
Showing 219 changed files with 1,849 additions and 761 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IconTopologyComplex } from "@tabler/icons-react";

import { Button, buttonVariants } from "@ctrlplane/ui/button";

import { CreateTargetDialog } from "../../_components/CreateTarget";
import { CreateTargetDialog } from "../../../_components/CreateTarget";

export const TargetGettingStarted: React.FC<{ workspace: Workspace }> = ({
workspace,
Expand Down
38 changes: 38 additions & 0 deletions apps/webservice/src/app/[workspaceSlug]/(app)/AppSidebar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Workspace } from "@ctrlplane/db/schema";
import React from "react";

import { SidebarContent, SidebarHeader } from "@ctrlplane/ui/sidebar";

import { api } from "~/trpc/server";
import { AppSidebarSystem, AppSidebarWorkspace } from "./AppSidebarContent";
import { AppSidebarHeader } from "./AppSidebarHeader";
import { AppSidebarPopover } from "./AppSidebarPopover";
import { SidebarWithPopover } from "./AppSidebarPopoverContext";

export const AppSidebar: React.FC<{ workspace: Workspace }> = async ({
workspace,
}) => {
const [workspaces, viewer, systems] = await Promise.all([
api.workspace.list(),
api.user.viewer(),
api.system.list({ workspaceId: workspace.id }),
]);

return (
<SidebarWithPopover>
<AppSidebarPopover />
<SidebarHeader>
<AppSidebarHeader
workspace={workspace}
workspaces={workspaces}
viewer={viewer}
systems={systems.items}
/>
</SidebarHeader>
<SidebarContent>
<AppSidebarWorkspace workspaceSlug={workspace.slug} />
<AppSidebarSystem systems={systems.items} />
</SidebarContent>
</SidebarWithPopover>
);
};
111 changes: 111 additions & 0 deletions apps/webservice/src/app/[workspaceSlug]/(app)/AppSidebarContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"use client";

import type { System } from "@ctrlplane/db/schema";
import { useMemo } from "react";
import {
IconCategory,
IconObjectScan,
IconPlant,
IconRocket,
IconRun,
IconShip,
IconVariable,
} from "@tabler/icons-react";

import { SidebarNavMain } from "../SidebarNavMain";

const navMain = (prefix: string) => [
{
title: "Systems",
icon: IconCategory,
isActive: true,
items: [
{
title: "Dependencies",
url: `${prefix}/dependencies`,
isActive: true,
},
],
},
{
title: "Resources",
popoverId: "targets",
icon: IconObjectScan,
isActive: true,
items: [
{
title: "List",
url: `${prefix}/targets`,
},
{
title: "Providers",
url: `${prefix}/target-providers`,
},
{
title: "Groups",
url: `${prefix}/target-metadata-groups`,
},
{
title: "Views",
url: `${prefix}/target-views`,
},
],
},
{
title: "Jobs",
icon: IconRocket,
items: [
{
title: "Agents",
url: "#",
},
{
title: "Runs",
url: "#",
},
],
},
];

export const AppSidebarWorkspace: React.FC<{ workspaceSlug: string }> = ({
workspaceSlug,
}) => {
const items = useMemo(() => navMain(`/${workspaceSlug}`), [workspaceSlug]);
return <SidebarNavMain title="Workspace" items={items} />;
};

export const AppSidebarSystem: React.FC<{
systems: System[];
}> = ({ systems }) => {
const items = useMemo(
() =>
systems.map((s) => ({
title: s.name,
isActive: true,
items: [
{
title: "Environments",
icon: IconPlant,
url: "#",
},
{
title: "Deployements",
icon: IconShip,
url: "#",
},
{
title: "Runbooks",
icon: IconRun,
url: "#",
},
{
title: "Variable Sets",
icon: IconVariable,
url: "#",
},
],
})),
[systems],
);
return <SidebarNavMain title="Systems" items={items} />;
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
"use client";

import type { Workspace } from "@ctrlplane/db/schema";
import { useState } from "react";
import { useParams } from "next/navigation";
import { IconPlus } from "@tabler/icons-react";

import { Button } from "@ctrlplane/ui/button";
Expand All @@ -12,17 +15,35 @@ import {
DropdownMenuTrigger,
} from "@ctrlplane/ui/dropdown-menu";

import { CreateDeploymentDialog } from "./_components/CreateDeployment";
import { CreateReleaseDialog } from "./_components/CreateRelease";
import { CreateSystemDialog } from "./_components/CreateSystem";
import { CreateTargetDialog } from "./_components/CreateTarget";
import { CreateSessionDialog } from "./_components/terminal/CreateDialogSession";
import { api } from "~/trpc/react";
import { CreateDeploymentDialog } from "../_components/CreateDeployment";
import { CreateReleaseDialog } from "../_components/CreateRelease";
import { CreateSystemDialog } from "../_components/CreateSystem";
import { CreateTargetDialog } from "../_components/CreateTarget";
import { CreateSessionDialog } from "../_components/terminal/CreateDialogSession";

export const SidebarCreateMenu: React.FC<{
export const AppSidebarCreateMenu: React.FC<{
workspace: Workspace;
deploymentId?: string;
systemId?: string;
}> = (props) => {
}> = ({ workspace }) => {
const { deploymentSlug, workspaceSlug, systemSlug } = useParams<{
workspaceSlug: string;
systemSlug?: string;
deploymentSlug?: string;
}>();

const system = api.system.bySlug.useQuery(
{ workspaceSlug, systemSlug: systemSlug ?? "" },
{ enabled: systemSlug != null },
);
const deployment = api.deployment.bySlug.useQuery(
{
workspaceSlug,
systemSlug: systemSlug ?? "",
deploymentSlug: deploymentSlug ?? "",
},
{ enabled: deploymentSlug != null && systemSlug != null },
);

const [open, setOpen] = useState(false);
return (
<DropdownMenu open={open} onOpenChange={setOpen}>
Expand All @@ -42,19 +63,26 @@ export const SidebarCreateMenu: React.FC<{
>
<DropdownMenuGroup>
<CreateSystemDialog
workspace={props.workspace}
workspace={workspace}
onSuccess={() => setOpen(false)}
>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
New System
</DropdownMenuItem>
</CreateSystemDialog>
<CreateDeploymentDialog {...props} onSuccess={() => setOpen(false)}>
<CreateDeploymentDialog
systemId={system.data?.id}
onSuccess={() => setOpen(false)}
>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
New Deployment
</DropdownMenuItem>
</CreateDeploymentDialog>
<CreateReleaseDialog {...props} onClose={() => setOpen(false)}>
<CreateReleaseDialog
systemId={system.data?.id}
deploymentId={deployment.data?.id}
onClose={() => setOpen(false)}
>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
New Release
</DropdownMenuItem>
Expand All @@ -75,7 +103,10 @@ export const SidebarCreateMenu: React.FC<{
<DropdownMenuSeparator />

<DropdownMenuGroup>
<CreateTargetDialog {...props} onSuccess={() => setOpen(false)}>
<CreateTargetDialog
workspace={workspace}
onSuccess={() => setOpen(false)}
>
<DropdownMenuItem onSelect={(e) => e.preventDefault()}>
Bootstrap Target
</DropdownMenuItem>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
"use client";

import type { Workspace } from "@ctrlplane/db/schema";
import type { System, Workspace } from "@ctrlplane/db/schema";
import Link from "next/link";
import { IconCheck, IconChevronDown } from "@tabler/icons-react";
import { signOut, useSession } from "next-auth/react";
import { IconCheck, IconChevronDown, IconSearch } from "@tabler/icons-react";
import { signOut } from "next-auth/react";

import { Button } from "@ctrlplane/ui/button";
import {
Expand All @@ -19,14 +19,17 @@ import {
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from "@ctrlplane/ui/dropdown-menu";
import { SidebarMenu, SidebarMenuItem } from "@ctrlplane/ui/sidebar";

import { api } from "~/trpc/react";
import { SearchDialog } from "../_components/SearchDialog";
import { AppSidebarCreateMenu } from "./AppSidebarCreateMenu";

export const SidebarWorkspaceDropdown: React.FC<{ workspace: Workspace }> = ({
workspace,
}) => {
const { data } = useSession();
const workspaces = api.workspace.list.useQuery();
const WorkspaceDropdown: React.FC<{
workspace: Workspace;
workspaces: Workspace[];
viewer: { email: string };
}> = ({ workspace, workspaces, viewer }) => {
const update = api.profile.update.useMutation();
return (
<DropdownMenu>
Expand All @@ -53,9 +56,9 @@ export const SidebarWorkspaceDropdown: React.FC<{ workspace: Workspace }> = ({
<DropdownMenuPortal>
<DropdownMenuSubContent className="w-[200px] bg-neutral-900">
<DropdownMenuLabel className="font-normal text-muted-foreground">
{data?.user.email}
{viewer.email}
</DropdownMenuLabel>
{workspaces.data?.map((ws) => (
{workspaces.map((ws) => (
<Link
key={ws.id}
href={`/${ws.slug}`}
Expand Down Expand Up @@ -85,3 +88,30 @@ export const SidebarWorkspaceDropdown: React.FC<{ workspace: Workspace }> = ({
</DropdownMenu>
);
};

export const AppSidebarHeader: React.FC<{
systems: System[];
workspace: Workspace;
workspaces: Workspace[];
viewer: { email: string };
}> = ({ workspace, workspaces, viewer }) => {
return (
<SidebarMenu>
<SidebarMenuItem className="flex items-center gap-2">
<div className="flex-grow overflow-x-auto">
<WorkspaceDropdown
workspace={workspace}
workspaces={workspaces}
viewer={viewer}
/>
</div>
<SearchDialog>
<Button variant="ghost" size="icon" className="h-6 w-6 flex-shrink-0">
<IconSearch className="h-4 w-4" />
</Button>
</SearchDialog>
<AppSidebarCreateMenu workspace={workspace} />
</SidebarMenuItem>
</SidebarMenu>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"use client";

import React from "react";
import { useKey } from "react-use";

import { Popover, PopoverAnchor, PopoverContent } from "@ctrlplane/ui/popover";

import { useSidebarPopover } from "./AppSidebarPopoverContext";

export const AppSidebarPopover: React.FC = () => {
const { activeSidebarItem, setActiveSidebarItem } = useSidebarPopover();
useKey("Escape", () => setActiveSidebarItem(null));
return (
<Popover open={activeSidebarItem != null}>
<PopoverAnchor className="w-full" />
<PopoverContent
side="right"
sideOffset={1}
className="h-[100vh] w-[300px] border-y-0 border-l-0 bg-black"
>
{activeSidebarItem}
</PopoverContent>
</Popover>
);
};
Loading

0 comments on commit e71a202

Please sign in to comment.