wip cool nav stuff

This commit is contained in:
Derock 2024-01-14 21:46:57 -05:00
parent d06a82bf2a
commit d943a9419b
No known key found for this signature in database
9 changed files with 172 additions and 32 deletions

View file

@ -52,6 +52,7 @@
"ipaddr.js": "^2.1.0",
"lucide-react": "^0.298.0",
"next": "14.0.4",
"next-nprogress-bar": "^2.1.2",
"next-themes": "^0.2.1",
"node-os-utils": "^1.3.7",
"react": "18.2.0",

View file

@ -113,6 +113,9 @@ dependencies:
next:
specifier: 14.0.4
version: 14.0.4(react-dom@18.2.0)(react@18.2.0)
next-nprogress-bar:
specifier: ^2.1.2
version: 2.1.2
next-themes:
specifier: ^0.2.1
version: 0.2.1(next@14.0.4)(react-dom@18.2.0)(react@18.2.0)
@ -6047,6 +6050,12 @@ packages:
engines: {node: '>= 0.6'}
dev: false
/next-nprogress-bar@2.1.2:
resolution: {integrity: sha512-2Df5d7fr6uPx+BX8MkoWCfl+RjG+uWI5mA399e5sEe8mbT3q/GIUvCXLzBgJBIISpKuMmdLAOYEzqpjlsRVOWw==}
dependencies:
nprogress: 0.2.0
dev: false
/next-themes@0.2.1(next@14.0.4)(react-dom@18.2.0)(react@18.2.0):
resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
peerDependencies:
@ -6227,6 +6236,10 @@ packages:
set-blocking: 2.0.0
dev: false
/nprogress@0.2.0:
resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==}
dev: false
/object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}

View file

@ -6,7 +6,17 @@ import { type RouterOutputs } from "~/trpc/shared";
export type BasicServiceDetails =
RouterOutputs["projects"]["get"]["services"][number];
export type ProjectContextType = RouterOutputs["projects"]["get"] & {
/**
* Base project path
* Example: `/project/123`
*/
path: string;
/**
* Base service path
* Example: `/project/123/service/456`
*/
servicePath: string;
selectedService?: BasicServiceDetails;
};

View file

@ -0,0 +1,13 @@
import { DeleteButton } from "../_components/DeleteButton";
export default function ServicePage({
params: { serviceId },
}: {
params: { serviceId: string };
}) {
return (
<div>
Hello world from {serviceId} <DeleteButton serviceId={serviceId} />
</div>
);
}

View file

@ -0,0 +1,65 @@
"use client";
import { BoxesIcon, CloudyIcon, CodeIcon, HomeIcon } from "lucide-react";
import { SidebarNav, type SidebarNavProps } from "~/components/SidebarNav";
import { useProject } from "../../_context/ProjectContext";
const sidebarNavItems = [
{
title: "Home",
description: "Quick overview of all containers for this project.",
href: "/home",
icon: HomeIcon,
},
{
type: "divider",
title: "Deployment",
},
{
title: "Containers",
description: "Lists all containers deployed for this service.",
href: "/containers",
icon: BoxesIcon,
},
{
title: "Deployments",
description: "All deployments for this service.",
href: "/deployments",
icon: CloudyIcon,
},
{
title: "Source",
description: "Source settings",
href: "/source",
icon: CodeIcon,
},
] as const;
export default function ProjectHomeLayout({
children,
}: {
children: React.ReactNode;
}) {
const project = useProject();
const items = sidebarNavItems.map((item) => ({
...item,
href: "href" in item ? `${project.path}${item.href}` : undefined,
})) as SidebarNavProps["items"];
return (
<div className="hidden space-y-6 py-10 md:block">
<div className="flex flex-col space-y-8 lg:flex-row lg:space-x-12 lg:space-y-0">
<aside className="lg:w-1/5">
<SidebarNav items={items} />
</aside>
<div className="flex-1 space-y-6 lg:max-w-2xl">
{/* <SettingsHeader items={items} /> */}
{/* <Separator /> */}
{children}
</div>
</div>
</div>
);
}

View file

@ -1,13 +1,13 @@
import { DeleteButton } from "./_components/DeleteButton";
"use client";
export default function ServicePage({
params: { serviceId },
}: {
params: { serviceId: string };
}) {
return (
<div>
Hello world from {serviceId} <DeleteButton serviceId={serviceId} />
</div>
);
import { usePathname, useRouter } from "next/navigation";
export default function ServicePage() {
const router = useRouter();
const pathname = usePathname();
// redirect to ./home
router.push(pathname + "/home");
return <div>Redirecting you...</div>;
}

View file

@ -5,6 +5,7 @@ import { cookies } from "next/headers";
import { ThemeProvider } from "~/components/contexts/ThemeProvider";
import { ToastProvider } from "~/components/contexts/ToastProvider";
import { TRPCReactProvider } from "~/trpc/react";
import { AppProgressBar } from "./providers";
const outfit = Outfit({
subsets: ["latin"],
@ -28,7 +29,14 @@ export default function RootLayout({
<body className={`font-sans ${outfit.variable} min-h-screen min-w-full`}>
<TRPCReactProvider cookies={cookies().toString()}>
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
<ToastProvider>{children}</ToastProvider>
<ToastProvider>
<AppProgressBar
color="hsl(var(--primary))"
delay={200}
options={{ showSpinner: false }}
/>
{children}
</ToastProvider>
</ThemeProvider>
</TRPCReactProvider>
</body>

4
src/app/providers.tsx Normal file
View file

@ -0,0 +1,4 @@
"use client";
// reexport AppProgressBar as the original source is missing a "use client" directive
export { AppProgressBar } from "next-nprogress-bar";

View file

@ -1,15 +1,27 @@
"use client";
import { type LucideIcon } from "lucide-react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import React from "react";
import { buttonVariants } from "~/components/ui/button";
import { cn } from "~/utils/utils";
interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {
items: {
href: string;
title: string;
}[];
type SidebarNavEntry = {
type?: "entry";
href: string;
title: string;
icon?: LucideIcon;
};
type SidebarNavDivider = {
type: "divider";
title: string;
icon?: LucideIcon;
};
export interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {
items: (SidebarNavEntry | SidebarNavDivider)[];
}
export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
@ -23,21 +35,35 @@ export function SidebarNav({ className, items, ...props }: SidebarNavProps) {
)}
{...props}
>
{items.map((item) => (
<Link
key={item.href}
href={item.href}
className={cn(
buttonVariants({ variant: "ghost" }),
pathname === item.href.replace(/\/$/, "")
? "bg-muted hover:bg-muted"
: "hover:bg-transparent hover:underline",
"justify-start",
)}
>
{item.title}
</Link>
))}
{items.map((item, i) =>
item.type === "divider" ? (
<p
className="pb-2 pt-4 text-xs tracking-wide text-muted-foreground"
key={i}
>
{item.title}
</p>
) : (
<Link
key={item.href}
href={item.href}
className={cn(
buttonVariants({ variant: "ghost" }),
pathname === item.href.replace(/\/$/, "")
? "bg-muted hover:bg-muted"
: "hover:bg-transparent hover:underline",
"justify-start",
)}
>
{item.icon && (
<div className="mr-2 rounded-md bg-card p-1.5">
<item.icon size={16} strokeWidth={1.5} />
</div>
)}
{item.title}
</Link>
),
)}
</nav>
);
}