From aa8f46dffb1d26f1c73b7e63826336ae05b06289 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Yusuf=20Akg=C3=BCl?= <s86116@bht-berlin.de>
Date: Tue, 6 Jun 2023 04:35:32 +0200
Subject: [PATCH] header done

---
 app/(content)/(gaming)/games/page.tsx         |  4 --
 app/(content)/{ => (user)}/followers/page.tsx |  0
 .../{ => (user)}/notifications/page.tsx       |  0
 app/(content)/layout.tsx                      | 25 +++++++--
 app/globals.css                               |  2 +-
 components/filter-sort-games.tsx              | 12 ++--
 components/header.tsx                         | 15 +++++
 components/nav.tsx                            |  8 +--
 components/search-input.tsx                   | 20 +++++--
 components/ui/avatar.tsx                      | 50 +++++++++++++++++
 components/user-avatar.tsx                    | 24 ++++++++
 components/user-nav.tsx                       | 56 +++++++++++++++++++
 package-lock.json                             | 27 +++++++++
 package.json                                  |  1 +
 14 files changed, 215 insertions(+), 29 deletions(-)
 rename app/(content)/{ => (user)}/followers/page.tsx (100%)
 rename app/(content)/{ => (user)}/notifications/page.tsx (100%)
 create mode 100644 components/header.tsx
 create mode 100644 components/ui/avatar.tsx
 create mode 100644 components/user-avatar.tsx
 create mode 100644 components/user-nav.tsx

diff --git a/app/(content)/(gaming)/games/page.tsx b/app/(content)/(gaming)/games/page.tsx
index 4ff601b..81b9172 100644
--- a/app/(content)/(gaming)/games/page.tsx
+++ b/app/(content)/(gaming)/games/page.tsx
@@ -1,7 +1,6 @@
 import Sort from "@/components/filter-sort-games";
 import { InfiniteScrollGames } from "@/components/infinity-scroll";
 import ScrollToTop from "@/components/scroll-to-top";
-import SearchInput from "@/components/search-input";
 
 // renders a list of games infinitely
 export default async function GamesPage() {
@@ -9,9 +8,6 @@ export default async function GamesPage() {
         <>
             <main className="relative lg:gap-10 xl:grid xl:grid-cols-[1fr_240px]">
                 <div className="grid">
-                    <div className="flex flex-col gap-10 items-center w-full">
-                        <SearchInput className="p-3 lg:w-2/3 2xl:w-1/3" />
-                    </div>
                     <InfiniteScrollGames />
                 </div>
                 <div className="hidden xl:block flex-col md:flex">
diff --git a/app/(content)/followers/page.tsx b/app/(content)/(user)/followers/page.tsx
similarity index 100%
rename from app/(content)/followers/page.tsx
rename to app/(content)/(user)/followers/page.tsx
diff --git a/app/(content)/notifications/page.tsx b/app/(content)/(user)/notifications/page.tsx
similarity index 100%
rename from app/(content)/notifications/page.tsx
rename to app/(content)/(user)/notifications/page.tsx
diff --git a/app/(content)/layout.tsx b/app/(content)/layout.tsx
index 8a44982..3c53c0a 100644
--- a/app/(content)/layout.tsx
+++ b/app/(content)/layout.tsx
@@ -1,5 +1,8 @@
+import { MainNav } from "@/components/header"
 import DashboardNav from "@/components/nav"
+import SearchInput from "@/components/search-input"
 import { SiteFooter } from "@/components/site-footer"
+import { UserAccountNav } from "@/components/user-nav"
 import { dashboardConfig } from "@/lib/config/dashboard"
 import { getCurrentUser } from "@/lib/session"
 
@@ -12,12 +15,24 @@ export default async function ContentLayout({
     const user = await getCurrentUser()
 
     return (
-        <div className="flex flex-col min-h-screen">
-            <div className="mx-32 my-6 flex-1 md:grid md:grid-cols-[220px_1fr] md:gap-6 lg:grid-cols-[240px_1fr] lg:gap-10">
+        <div className="flex min-h-screen flex-col space-y-6">
+            <header className="sticky top-0 z-40 border-b bg-background">
+                <div className="container flex h-16 items-center justify-between py-4">
+                    <MainNav />
+                    <SearchInput className="p-3 md:w-2/3 2xl:w-1/3" />
+                    {user && <UserAccountNav
+                        user={{
+                            name: user?.name,
+                            image: user?.image,
+                            username: user?.username,
+                        }}
+                    />}
+                    {!user && <p className="w-8"></p>}
+                </div>
+            </header>
+            <div className="container grid flex-1 gap-12 md:grid-cols-[200px_1fr]">
                 <aside className="hidden w-[200px] flex-col md:flex">
-                    <div className="sticky top-0">
-                        <DashboardNav items={dashboardConfig.sidebarNav} />
-                    </div>
+                    <DashboardNav items={dashboardConfig.sidebarNav} />
                 </aside>
                 <main className="flex w-full flex-1 flex-col overflow-hidden">
                     {children}
diff --git a/app/globals.css b/app/globals.css
index af41b8b..cb7efa4 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -31,7 +31,7 @@
     --primary-foreground: 210 40% 98%;
 
     /* Secondary colors for <Button /> */
-    --secondary: 210 40% 96.1%;
+    --secondary: 237 100% 96%;
     --secondary-foreground: 222.2 47.4% 11.2%;
 
     /* Used for accents such as hover effects on <DropdownMenuItem>, <SelectItem>...etc */
diff --git a/components/filter-sort-games.tsx b/components/filter-sort-games.tsx
index caa9c36..9b37f76 100644
--- a/components/filter-sort-games.tsx
+++ b/components/filter-sort-games.tsx
@@ -57,10 +57,10 @@ export default function Sort() {
   }
 
   return (
-    <Card className="p-6 grid items-start gap-2">
+    <Card className="p-6 grid items-start gap-2 bg-secondary">
       <h1>Filter</h1>
       <Select value={selectedCategory ? selectedCategory : undefined} key={selectedCategory[0]} onValueChange={(value) => setSelectedCategory(value)}>
-        <SelectTrigger className={`w-full ${selectedCategory[0] ? 'font-extrabold' : ''}`}>
+        <SelectTrigger className={`bg-background border-none w-full ${selectedCategory[0] ? 'font-extrabold' : ''}`}>
           <SelectValue placeholder="By category..." />
         </SelectTrigger>
         <SelectContent>
@@ -77,7 +77,7 @@ export default function Sort() {
       </Select>
 
       <Select value={selectedGenre ? selectedGenre : undefined} key={selectedGenre[0]} onValueChange={(value) => setSelectedGenre(value)}>
-        <SelectTrigger className={`w-full ${selectedGenre[0] ? 'font-extrabold' : ''}`}>
+        <SelectTrigger className={`bg-background border-none w-full ${selectedGenre[0] ? 'font-extrabold' : ''}`}>
           <SelectValue placeholder="By genre..." />
         </SelectTrigger>
         <SelectContent>
@@ -109,7 +109,7 @@ export default function Sort() {
       </Select>
 
       <Select value={selectedPlatform ? selectedPlatform : undefined} key={selectedPlatform[0]} onValueChange={(value) => setSelectedPlatform(value)}>
-        <SelectTrigger className={`w-full ${selectedPlatform[0] ? 'font-extrabold' : ''}`}>
+        <SelectTrigger className={`bg-background border-none w-full ${selectedPlatform[0] ? 'font-extrabold' : ''}`}>
           <SelectValue placeholder="By Platform..." />
         </SelectTrigger>
         <SelectContent>
@@ -126,7 +126,7 @@ export default function Sort() {
       <h1 className="pt-6">Sort by</h1>
       <div className="flex space-x-2 pb-1">
         <Select value={selectedSortMethod} onValueChange={(value) => setSelectedSortMethod(value)}>
-          <SelectTrigger className="w-full">
+          <SelectTrigger className="bg-background border-none w-full">
             <SelectValue placeholder="Rating" />
           </SelectTrigger>
           <SelectContent>
@@ -137,7 +137,7 @@ export default function Sort() {
             </SelectGroup>
           </SelectContent>
         </Select>
-        <Button variant="ghost" onClick={() => toggleSortOrder()}>
+        <Button variant="ghost" onClick={() => toggleSortOrder()} className="bg-background border-none">
           <Icons.arrowdown className={`h-4 w-4 transition-all transform ${selectedSortOrder === 'asc' ? 'rotate-180' : ''}`} />
         </Button>
       </div>
diff --git a/components/header.tsx b/components/header.tsx
new file mode 100644
index 0000000..cdf1c8c
--- /dev/null
+++ b/components/header.tsx
@@ -0,0 +1,15 @@
+"use client"
+
+import Link from "next/link"
+
+import { GameUnityLogo } from "./logo"
+
+export function MainNav() {
+    return (
+        <div className="flex gap-6 md:gap-10">
+            <Link href="/" className="items-center space-x-2 flex">
+                <GameUnityLogo className="h-8 w-8" />
+            </Link>
+        </div>
+    )
+}
\ No newline at end of file
diff --git a/components/nav.tsx b/components/nav.tsx
index 36b42a3..a6d6e24 100644
--- a/components/nav.tsx
+++ b/components/nav.tsx
@@ -7,7 +7,6 @@ import { SidebarNavItem } from "@/types";
 import { useSession } from "next-auth/react";
 import Link from "next/link";
 import { usePathname } from "next/navigation";
-import { GameUnityLogo } from "./logo";
 import { ModeToggle } from "./mode-toggle";
 
 interface DashboardNavProps {
@@ -26,11 +25,6 @@ export default function DashboardNav({ items }: DashboardNavProps) {
 
     return (
         <nav className="grid items-start gap-2">
-            <div className="flex items-center">
-                <Link href="/" className={cn("rounded-full p-3 hover:bg-accent")}>
-                    <GameUnityLogo className="h-8 w-8" />
-                </Link>
-            </div>
             {visibleItems.map((item, index) => {
                 const Icon = Icons[item.icon as keyof IconsType || "arrowRight"];
                 if (item.title === "My Profile") {
@@ -38,7 +32,7 @@ export default function DashboardNav({ items }: DashboardNavProps) {
                 }
                 return (
                     item.href && (
-                        <Link key={index} href={item.disabled ? "/" : item.href} className={index == 6 ? "mt-10" : ""}>
+                        <Link key={index} href={item.disabled ? "/" : item.href} className={item.title === "Settings" ? "mt-10" : ""}>
                             <span
                                 className={cn(
                                     "group flex items-center rounded-md px-3 py-2 font-medium hover:bg-accent hover:text-accent-foreground",
diff --git a/components/search-input.tsx b/components/search-input.tsx
index 30e96e4..1786c3c 100644
--- a/components/search-input.tsx
+++ b/components/search-input.tsx
@@ -6,6 +6,7 @@ import { useState } from 'react';
 import { Icons } from './icons';
 import { Button } from './ui/button';
 import { Input } from './ui/input';
+import { toast } from './ui/use-toast';
 
 interface DocsSearchProps extends React.HTMLAttributes<HTMLFormElement> { }
 
@@ -17,13 +18,20 @@ export default function SearchInput({ className, ...props }: DocsSearchProps) {
     function onSearch(event: React.FormEvent) {
         event.preventDefault()
 
-        if (!searchQuery) {
-            router.push(pathname)
-            return
-        }
+        if (pathname === "/games") {
+            if (!searchQuery) {
+                router.push(pathname)
+                return
+            }
 
-        const encoededQuery = encodeURIComponent(searchQuery)
-        router.push(`${pathname}?search=${encoededQuery}`) // add useSearchParams
+            const encoededQuery = encodeURIComponent(searchQuery)
+            router.push(`${pathname}?search=${encoededQuery}`)
+        } else {
+            return toast({
+                title: "Work in Progress!",
+                description: "Sorry, but global search is not available yet... ã…¤You can test it out in the games page though!",
+            })
+        }
     };
 
     return (
diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx
new file mode 100644
index 0000000..51e507b
--- /dev/null
+++ b/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<typeof AvatarPrimitive.Root>,
+  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
+>(({ className, ...props }, ref) => (
+  <AvatarPrimitive.Root
+    ref={ref}
+    className={cn(
+      "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
+      className
+    )}
+    {...props}
+  />
+))
+Avatar.displayName = AvatarPrimitive.Root.displayName
+
+const AvatarImage = React.forwardRef<
+  React.ElementRef<typeof AvatarPrimitive.Image>,
+  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
+>(({ className, ...props }, ref) => (
+  <AvatarPrimitive.Image
+    ref={ref}
+    className={cn("aspect-square h-full w-full", className)}
+    {...props}
+  />
+))
+AvatarImage.displayName = AvatarPrimitive.Image.displayName
+
+const AvatarFallback = React.forwardRef<
+  React.ElementRef<typeof AvatarPrimitive.Fallback>,
+  React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
+>(({ className, ...props }, ref) => (
+  <AvatarPrimitive.Fallback
+    ref={ref}
+    className={cn(
+      "flex h-full w-full items-center justify-center rounded-full bg-muted",
+      className
+    )}
+    {...props}
+  />
+))
+AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
+
+export { Avatar, AvatarImage, AvatarFallback }
diff --git a/components/user-avatar.tsx b/components/user-avatar.tsx
new file mode 100644
index 0000000..c627d49
--- /dev/null
+++ b/components/user-avatar.tsx
@@ -0,0 +1,24 @@
+import { User } from "@prisma/client"
+import { AvatarProps } from "@radix-ui/react-avatar"
+
+import { Icons } from "@/components/icons"
+import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
+
+interface UserAvatarProps extends AvatarProps {
+    user: Pick<User, "image" | "name">
+}
+
+export function UserAvatar({ user, ...props }: UserAvatarProps) {
+    return (
+        <Avatar {...props}>
+            {user.image ? (
+                <AvatarImage alt="Picture" src={user.image} />
+            ) : (
+                <AvatarFallback>
+                    <span className="sr-only">{user.name}</span>
+                    <Icons.user className="h-4 w-4" />
+                </AvatarFallback>
+            )}
+        </Avatar>
+    )
+}
\ No newline at end of file
diff --git a/components/user-nav.tsx b/components/user-nav.tsx
new file mode 100644
index 0000000..a6e1c0a
--- /dev/null
+++ b/components/user-nav.tsx
@@ -0,0 +1,56 @@
+"use client"
+
+import { User } from "next-auth"
+import { signOut } from "next-auth/react"
+
+import {
+    DropdownMenu,
+    DropdownMenuContent,
+    DropdownMenuItem,
+    DropdownMenuSeparator,
+    DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import { UserAvatar } from "@/components/user-avatar"
+
+type UserUsername = string | null | undefined
+
+interface UserAccountNavProps extends React.HTMLAttributes<HTMLDivElement> {
+    user: Pick<User & { username: UserUsername }, "name" | "image" | "username">
+}
+
+export function UserAccountNav({ user }: UserAccountNavProps) {
+    return (
+        <DropdownMenu>
+            <DropdownMenuTrigger className="rounded-full">
+                <UserAvatar
+                    user={{ name: user.name || null, image: user.image || null }}
+                    className="h-8 w-8"
+                />
+            </DropdownMenuTrigger>
+            <DropdownMenuContent align="end">
+                <div className="flex items-center justify-start gap-2 p-2">
+                    <div className="flex flex-col space-y-1 leading-none">
+                        {user.name && <p className="font-medium">{user.name}</p>}
+                        {user.username && (
+                            <p className="w-[200px] truncate text-sm text-muted-foreground">
+                                @{user.username}
+                            </p>
+                        )}
+                    </div>
+                </div>
+                <DropdownMenuSeparator />
+                <DropdownMenuItem
+                    className="cursor-pointer"
+                    onSelect={(event) => {
+                        event.preventDefault()
+                        signOut({
+                            callbackUrl: `${window.location.origin}/login`,
+                        })
+                    }}
+                >
+                    Sign out
+                </DropdownMenuItem>
+            </DropdownMenuContent>
+        </DropdownMenu>
+    )
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index fc05649..001892b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
         "@auth/prisma-adapter": "^1.0.0",
         "@hookform/resolvers": "^3.1.0",
         "@prisma/client": "^4.15.0",
+        "@radix-ui/react-avatar": "^1.0.3",
         "@radix-ui/react-dropdown-menu": "^2.0.5",
         "@radix-ui/react-label": "^2.0.2",
         "@radix-ui/react-scroll-area": "^1.0.4",
@@ -593,6 +594,32 @@
         }
       }
     },
+    "node_modules/@radix-ui/react-avatar": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.0.3.tgz",
+      "integrity": "sha512-9ToF7YNex3Ste45LrAeTlKtONI9yVRt/zOS158iilIkW5K/Apeyb/TUQlcEFTEFvWr8Kzdi2ZYrm1/suiXPajQ==",
+      "dependencies": {
+        "@babel/runtime": "^7.13.10",
+        "@radix-ui/react-context": "1.0.1",
+        "@radix-ui/react-primitive": "1.0.3",
+        "@radix-ui/react-use-callback-ref": "1.0.1",
+        "@radix-ui/react-use-layout-effect": "1.0.1"
+      },
+      "peerDependencies": {
+        "@types/react": "*",
+        "@types/react-dom": "*",
+        "react": "^16.8 || ^17.0 || ^18.0",
+        "react-dom": "^16.8 || ^17.0 || ^18.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "@types/react-dom": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/@radix-ui/react-collection": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz",
diff --git a/package.json b/package.json
index 5372fd5..ef91fe6 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
     "@auth/prisma-adapter": "^1.0.0",
     "@hookform/resolvers": "^3.1.0",
     "@prisma/client": "^4.15.0",
+    "@radix-ui/react-avatar": "^1.0.3",
     "@radix-ui/react-dropdown-menu": "^2.0.5",
     "@radix-ui/react-label": "^2.0.2",
     "@radix-ui/react-scroll-area": "^1.0.4",
-- 
GitLab