diff --git a/package.json b/package.json
index 52c0f62a..451657ef 100644
--- a/package.json
+++ b/package.json
@@ -19,8 +19,10 @@
"@node-rs/argon2": "1.8.3",
"@oslojs/crypto": "1.0.1",
"@oslojs/encoding": "1.1.0",
+ "@radix-ui/react-avatar": "1.1.1",
"@radix-ui/react-checkbox": "1.1.2",
"@radix-ui/react-dialog": "1.1.2",
+ "@radix-ui/react-dropdown-menu": "2.1.2",
"@radix-ui/react-icons": "1.3.0",
"@radix-ui/react-label": "2.1.0",
"@radix-ui/react-popover": "1.1.2",
diff --git a/src/app/configuration/components/Header.tsx b/src/app/configuration/components/Header.tsx
new file mode 100644
index 00000000..5075a5b7
--- /dev/null
+++ b/src/app/configuration/components/Header.tsx
@@ -0,0 +1,80 @@
+"use client";
+
+import { Avatar, AvatarFallback } from "@app/components/ui/avatar";
+import { Badge } from "@app/components/ui/badge";
+import { Button } from "@app/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuGroup,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuTrigger,
+} from "@app/components/ui/dropdown-menu";
+
+type HeaderProps = {
+ name?: string;
+ email: string;
+ orgName: string;
+};
+
+export default function Header({ email, orgName, name }: HeaderProps) {
+ function getInitials() {
+ if (name) {
+ const [firstName, lastName] = name.split(" ");
+ return `${firstName[0]}${lastName[0]}`;
+ }
+ return email.substring(0, 2).toUpperCase();
+ }
+
+ return (
+ <>
+
+
{orgName}
+
+
+
{name || email}
+
+
+
+
+
+
+
+ {name && (
+
+ {name}
+
+ )}
+
+ {email}
+
+
+
+
+
+ Profile
+ Log out
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/configuration/components/TopbarNav.tsx b/src/app/configuration/components/TopbarNav.tsx
new file mode 100644
index 00000000..2ba25df5
--- /dev/null
+++ b/src/app/configuration/components/TopbarNav.tsx
@@ -0,0 +1,58 @@
+"use client";
+
+import React from "react";
+import Link from "next/link";
+import { usePathname } from "next/navigation";
+import { cn } from "@/lib/utils";
+
+interface TopbarNavProps extends React.HTMLAttributes {
+ items: {
+ href: string;
+ title: string;
+ icon: React.ReactNode;
+ }[];
+ disabled?: boolean;
+}
+
+export function TopbarNav({
+ className,
+ items,
+ disabled = false,
+ ...props
+}: TopbarNavProps) {
+ const pathname = usePathname();
+
+ return (
+
+ );
+}
diff --git a/src/app/configuration/layout.tsx b/src/app/configuration/layout.tsx
index e8d7c4d8..a57af186 100644
--- a/src/app/configuration/layout.tsx
+++ b/src/app/configuration/layout.tsx
@@ -1,64 +1,49 @@
-import { Metadata } from "next"
-import Image from "next/image"
-
-import { Separator } from "@/components/ui/separator"
-import { SidebarNav } from "@/components/sidebar-nav"
+import { Metadata } from "next";
+import { TopbarNav } from "./components/TopbarNav";
+import { LayoutGrid, Tent } from "lucide-react";
+import Header from "./components/Header";
export const metadata: Metadata = {
- title: "Forms",
- description: "Advanced form example using react-hook-form and Zod.",
-}
+ title: "Configuration",
+ description: "",
+};
-const sidebarNavItems = [
+const topNavItems = [
{
title: "Sites",
href: "/configuration/sites",
+ icon: ,
},
{
title: "Resources",
href: "/configuration/resources",
+ icon: ,
},
-]
+];
-interface SettingsLayoutProps {
- children: React.ReactNode,
- params: { siteId: string }
+interface ConfigurationLaytoutProps {
+ children: React.ReactNode;
+ params: { siteId: string };
}
-export default function SettingsLayout({ children, params }: SettingsLayoutProps) {
+export default async function ConfigurationLaytout({
+ children,
+ params,
+}: ConfigurationLaytoutProps) {
return (
<>
-
-
-
-
-
-
-
Settings
-
- { params.siteId == "create" ? "Create site..." : "Manage settings on site " + params.siteId }.
-
-
-
-
-
-
{children}
+
+
+
{children}
>
- )
+ );
}
diff --git a/src/app/configuration/resources/page.tsx b/src/app/configuration/resources/page.tsx
index f720649d..4b89334e 100644
--- a/src/app/configuration/resources/page.tsx
+++ b/src/app/configuration/resources/page.tsx
@@ -1,7 +1,14 @@
export default async function Page() {
return (
<>
-
This is where the table goes...
+
+
+ Manage Resources
+
+
+ Create secure proxies to your private resources.
+
+
>
);
}
diff --git a/src/app/configuration/sites/page.tsx b/src/app/configuration/sites/page.tsx
index ab6e57ba..a01c9866 100644
--- a/src/app/configuration/sites/page.tsx
+++ b/src/app/configuration/sites/page.tsx
@@ -3,8 +3,14 @@ import Link from "next/link";
export default async function Page() {
return (
<>
-
This is where the table goes...
-
Open up the site 123
+
+
+ Manage Sites
+
+
+ Manage your existing sites here or create a new one.
+
+
>
);
}
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx
new file mode 100644
index 00000000..51e507ba
--- /dev/null
+++ b/src/components/ui/avatar.tsx
@@ -0,0 +1,50 @@
+"use client"
+
+import * as React from "react"
+import * as AvatarPrimitive from "@radix-ui/react-avatar"
+
+import { cn } from "@/lib/utils"
+
+const Avatar = React.forwardRef<
+ React.ElementRef
,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+Avatar.displayName = AvatarPrimitive.Root.displayName
+
+const AvatarImage = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AvatarImage.displayName = AvatarPrimitive.Image.displayName
+
+const AvatarFallback = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
+
+export { Avatar, AvatarImage, AvatarFallback }
diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx
new file mode 100644
index 00000000..f000e3ef
--- /dev/null
+++ b/src/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx
new file mode 100644
index 00000000..f69a0d64
--- /dev/null
+++ b/src/components/ui/dropdown-menu.tsx
@@ -0,0 +1,200 @@
+"use client"
+
+import * as React from "react"
+import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
+import { Check, ChevronRight, Circle } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+const DropdownMenu = DropdownMenuPrimitive.Root
+
+const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
+
+const DropdownMenuGroup = DropdownMenuPrimitive.Group
+
+const DropdownMenuPortal = DropdownMenuPrimitive.Portal
+
+const DropdownMenuSub = DropdownMenuPrimitive.Sub
+
+const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
+
+const DropdownMenuSubTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, children, ...props }, ref) => (
+
+ {children}
+
+
+))
+DropdownMenuSubTrigger.displayName =
+ DropdownMenuPrimitive.SubTrigger.displayName
+
+const DropdownMenuSubContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DropdownMenuSubContent.displayName =
+ DropdownMenuPrimitive.SubContent.displayName
+
+const DropdownMenuContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+
+
+))
+DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
+
+const DropdownMenuItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName
+
+const DropdownMenuCheckboxItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, checked, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+DropdownMenuCheckboxItem.displayName =
+ DropdownMenuPrimitive.CheckboxItem.displayName
+
+const DropdownMenuRadioItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName
+
+const DropdownMenuLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ inset?: boolean
+ }
+>(({ className, inset, ...props }, ref) => (
+
+))
+DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName
+
+const DropdownMenuSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName
+
+const DropdownMenuShortcut = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => {
+ return (
+
+ )
+}
+DropdownMenuShortcut.displayName = "DropdownMenuShortcut"
+
+export {
+ DropdownMenu,
+ DropdownMenuTrigger,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuCheckboxItem,
+ DropdownMenuRadioItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuShortcut,
+ DropdownMenuGroup,
+ DropdownMenuPortal,
+ DropdownMenuSub,
+ DropdownMenuSubContent,
+ DropdownMenuSubTrigger,
+ DropdownMenuRadioGroup,
+}