Milo Schwartz 9 meses atrás
pai
commit
408f42daad

+ 87 - 0
src/app/[orgId]/components/Header.tsx

@@ -0,0 +1,87 @@
+"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";
+import Link from "next/link";
+
+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 (
+        <>
+            <div className="flex items-center justify-between">
+                <Badge variant="outline" className="text-md font-bold">
+                    {orgName}
+                </Badge>
+
+                <div className="flex items-center">
+                    <div className="flex items-center gap-3">
+                        <span className="text-lg font-medium">
+                            {name || email}
+                        </span>
+                        <DropdownMenu>
+                            <DropdownMenuTrigger asChild>
+                                <Button
+                                    variant="ghost"
+                                    className="relative h-10 w-10 rounded-full"
+                                >
+                                    <Avatar className="h-10 w-10">
+                                        <AvatarFallback>
+                                            {getInitials()}
+                                        </AvatarFallback>
+                                    </Avatar>
+                                </Button>
+                            </DropdownMenuTrigger>
+                            <DropdownMenuContent
+                                className="w-56"
+                                align="end"
+                                forceMount
+                            >
+                                <DropdownMenuLabel className="font-normal">
+                                    <div className="flex flex-col space-y-1">
+                                        {name && (
+                                            <p className="text-sm font-medium leading-none">
+                                                {name}
+                                            </p>
+                                        )}
+                                        <p className="text-xs leading-none text-muted-foreground">
+                                            {email}
+                                        </p>
+                                    </div>
+                                </DropdownMenuLabel>
+                                <DropdownMenuSeparator />
+                                <DropdownMenuGroup>
+                                    <DropdownMenuItem>Profile</DropdownMenuItem>
+                                    <DropdownMenuItem>Log out</DropdownMenuItem>
+                                </DropdownMenuGroup>
+                            </DropdownMenuContent>
+                        </DropdownMenu>
+                    </div>
+                </div>
+            </div>
+        </>
+    );
+}

+ 4 - 2
src/app/configuration/components/TopbarNav.tsx → src/app/[orgId]/components/TopbarNav.tsx

@@ -12,12 +12,14 @@ interface TopbarNavProps extends React.HTMLAttributes<HTMLElement> {
         icon: React.ReactNode;
     }[];
     disabled?: boolean;
+    orgId: string;
 }
 
 export function TopbarNav({
     className,
     items,
     disabled = false,
+    orgId,
     ...props
 }: TopbarNavProps) {
     const pathname = usePathname();
@@ -34,10 +36,10 @@ export function TopbarNav({
             {items.map((item) => (
                 <Link
                     key={item.href}
-                    href={item.href}
+                    href={item.href.replace("{orgId}", orgId)}
                     className={cn(
                         "px-2 py-3 text-md",
-                        pathname === item.href
+                        pathname === item.href.replace("{orgId}", orgId)
                             ? "border-b-2 border-stone-600 text-stone-600"
                             : "hover:text-gray-600 text-stone-400",
                         "whitespace-nowrap",

+ 27 - 10
src/app/[orgId]/layout.tsx

@@ -1,7 +1,9 @@
 import { Metadata } from "next";
 import { TopbarNav } from "./components/TopbarNav";
-import { LayoutGrid, Tent } from "lucide-react";
+import { Cog, LayoutGrid, Tent, Users } from "lucide-react";
 import Header from "./components/Header";
+import { verifySession } from "@app/lib/auth/verifySession";
+import { redirect } from "next/navigation";
 
 export const metadata: Metadata = {
     title: "Configuration",
@@ -11,39 +13,54 @@ export const metadata: Metadata = {
 const topNavItems = [
     {
         title: "Sites",
-        href: "/configuration/sites",
+        href: "/{orgId}/sites",
         icon: <Tent />,
     },
     {
         title: "Resources",
-        href: "/configuration/resources",
+        href: "/{orgId}/resources",
         icon: <LayoutGrid />,
     },
+    {
+        title: "Users",
+        href: "/{orgId}/users",
+        icon: <Users />,
+    },
+    {
+        title: "General",
+        href: "/{orgId}/general",
+        icon: <Cog />,
+    },
 ];
 
 interface ConfigurationLaytoutProps {
     children: React.ReactNode;
-    params: { siteId: string };
+    params: { orgId: string };
 }
 
 export default async function ConfigurationLaytout({
     children,
     params,
 }: ConfigurationLaytoutProps) {
+    const user = await verifySession();
+
+    if (!user) {
+        redirect("/auth/login");
+    }
+
     return (
         <>
-            <div className="w-full bg-stone-200 border-b border-stone-300 mb-5 select-none px-3">
+            <div className="w-full bg-stone-200 border-b border-stone-300 mb-5 select-none sm:px-0 px-3">
                 <div className="container mx-auto flex flex-col content-between gap-3 pt-2">
                     <Header
-                        email="mschwartz10612@gmail.com"
-                        orgName="Home Lab 1"
-                        name="Milo Schwartz"
+                        email={user.email}
+                        orgName={params.orgId}
                     />
-                    <TopbarNav items={topNavItems} />
+                    <TopbarNav items={topNavItems} orgId={params.orgId}/>
                 </div>
             </div>
 
-            <div className="container mx-auto px-3">{children}</div>
+            <div className="container mx-auto sm:px-0 px-3">{children}</div>
         </>
     );
 }

+ 25 - 21
src/app/[orgId]/sites/[siteId]/page.tsx

@@ -1,26 +1,30 @@
-import React from 'react';
+import React from "react";
 import { Separator } from "@/components/ui/separator";
 import { ProfileForm } from "@app/components/profile-form";
 import { CreateSiteForm } from "./components/create-site";
 
-export default function SettingsProfilePage({ params }: { params: { siteId: string } }) {
-  const isCreateForm = params.siteId === "create";
+export default function SettingsProfilePage({
+    params,
+}: {
+    params: { siteId: string };
+}) {
+    const isCreateForm = params.siteId === "create";
 
-  return (
-    <div className="space-y-6">
-      <div>
-        <h3 className="text-lg font-medium">
-          {isCreateForm ? "Create Site" : "Profile"}
-        </h3>
-        <p className="text-sm text-muted-foreground">
-          {isCreateForm 
-            ? "Create a new site for your profile." 
-            : "This is how others will see you on the site."}
-        </p>
-      </div>
-      <Separator />
-      
-      {isCreateForm ? <CreateSiteForm /> : <ProfileForm />}
-    </div>
-  );
-}
+    return (
+        <div className="space-y-6">
+            <div>
+                <h3 className="text-lg font-medium">
+                    {isCreateForm ? "Create Site" : "Profile"}
+                </h3>
+                <p className="text-sm text-muted-foreground">
+                    {isCreateForm
+                        ? "Create a new site for your profile."
+                        : "This is how others will see you on the site."}
+                </p>
+            </div>
+            <Separator />
+
+            {isCreateForm ? <CreateSiteForm /> : <ProfileForm />}
+        </div>
+    );
+}

+ 0 - 80
src/app/configuration/components/Header.tsx

@@ -1,80 +0,0 @@
-"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 (
-        <>
-            <div className="flex items-center justify-between">
-                <Badge variant="outline" className="text-md font-bold">{orgName}</Badge>
-
-                <div className="flex items-center gap-3">
-                    <span className="text-lg font-medium">{name || email}</span>
-                    <DropdownMenu>
-                        <DropdownMenuTrigger asChild>
-                            <Button
-                                variant="ghost"
-                                className="relative h-10 w-10 rounded-full"
-                            >
-                                <Avatar className="h-10 w-10">
-                                    <AvatarFallback>
-                                        {getInitials()}
-                                    </AvatarFallback>
-                                </Avatar>
-                            </Button>
-                        </DropdownMenuTrigger>
-                        <DropdownMenuContent
-                            className="w-56"
-                            align="end"
-                            forceMount
-                        >
-                            <DropdownMenuLabel className="font-normal">
-                                <div className="flex flex-col space-y-1">
-                                    {name && (
-                                        <p className="text-sm font-medium leading-none">
-                                            {name}
-                                        </p>
-                                    )}
-                                    <p className="text-xs leading-none text-muted-foreground">
-                                        {email}
-                                    </p>
-                                </div>
-                            </DropdownMenuLabel>
-                            <DropdownMenuSeparator />
-                            <DropdownMenuGroup>
-                                <DropdownMenuItem>Profile</DropdownMenuItem>
-                                <DropdownMenuItem>Log out</DropdownMenuItem>
-                            </DropdownMenuGroup>
-                        </DropdownMenuContent>
-                    </DropdownMenu>
-                </div>
-            </div>
-        </>
-    );
-}