diff options
| author | Md Saban <45597394+mdsaban@users.noreply.github.com> | 2024-10-12 18:37:20 +0530 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-12 14:07:20 +0100 |
| commit | 1f768be0485bbfa6b542dd24183fe8389acb9355 (patch) | |
| tree | b803eefba19cddfad988b3bd1979b693fbc0f694 | |
| parent | e2644ebc11e9521ece054a846f8c993c322a8332 (diff) | |
| download | karakeep-1f768be0485bbfa6b542dd24183fe8389acb9355.tar.zst | |
feature(web): Introduce a new sticky navbar. Fixes 520 (#515)
* ui: add global header
* fix: design fixes
* fix: tests
* fix navbar background, hide y scrollbar and change sidebar footer to show version
---------
Co-authored-by: MohamedBassem <me@mbassem.com>
| -rw-r--r-- | apps/web/app/dashboard/archive/page.tsx | 16 | ||||
| -rw-r--r-- | apps/web/app/dashboard/bookmarks/page.tsx | 10 | ||||
| -rw-r--r-- | apps/web/app/dashboard/favourites/page.tsx | 2 | ||||
| -rw-r--r-- | apps/web/app/dashboard/layout.tsx | 30 | ||||
| -rw-r--r-- | apps/web/app/dashboard/search/page.tsx | 11 | ||||
| -rw-r--r-- | apps/web/components/dashboard/GlobalActions.tsx | 2 | ||||
| -rw-r--r-- | apps/web/components/dashboard/header/Header.tsx | 71 | ||||
| -rw-r--r-- | apps/web/components/dashboard/header/ProfileOptions.tsx (renamed from apps/web/components/dashboard/sidebar/SidebarProfileOptions.tsx) | 15 | ||||
| -rw-r--r-- | apps/web/components/dashboard/lists/ListHeader.tsx | 2 | ||||
| -rw-r--r-- | apps/web/components/dashboard/sidebar/AllLists.tsx | 6 | ||||
| -rw-r--r-- | apps/web/components/dashboard/sidebar/ModileSidebar.tsx | 4 | ||||
| -rw-r--r-- | apps/web/components/dashboard/sidebar/Sidebar.tsx | 34 |
12 files changed, 116 insertions, 87 deletions
diff --git a/apps/web/app/dashboard/archive/page.tsx b/apps/web/app/dashboard/archive/page.tsx index 5c25d8cc..becb6a58 100644 --- a/apps/web/app/dashboard/archive/page.tsx +++ b/apps/web/app/dashboard/archive/page.tsx @@ -1,19 +1,13 @@ import Bookmarks from "@/components/dashboard/bookmarks/Bookmarks"; -import GlobalActions from "@/components/dashboard/GlobalActions"; import InfoTooltip from "@/components/ui/info-tooltip"; function header() { return ( - <div className="flex items-center justify-between"> - <div className="flex gap-2"> - <p className="text-2xl">🗄️ Archive</p> - <InfoTooltip size={17} className="my-auto" variant="explain"> - <p>Archived bookmarks won't appear in the homepage</p> - </InfoTooltip> - </div> - <div> - <GlobalActions /> - </div> + <div className="flex gap-2"> + <p className="text-2xl">🗄️ Archive</p> + <InfoTooltip size={17} className="my-auto" variant="explain"> + <p>Archived bookmarks won't appear in the homepage</p> + </InfoTooltip> </div> ); } diff --git a/apps/web/app/dashboard/bookmarks/page.tsx b/apps/web/app/dashboard/bookmarks/page.tsx index c02e6b85..a7b22fef 100644 --- a/apps/web/app/dashboard/bookmarks/page.tsx +++ b/apps/web/app/dashboard/bookmarks/page.tsx @@ -1,18 +1,10 @@ import React from "react"; import Bookmarks from "@/components/dashboard/bookmarks/Bookmarks"; -import GlobalActions from "@/components/dashboard/GlobalActions"; -import { SearchInput } from "@/components/dashboard/search/SearchInput"; export default async function BookmarksPage() { return ( <div> - <div className="flex gap-2"> - <SearchInput /> - <GlobalActions /> - </div> - <div className="my-4"> - <Bookmarks query={{ archived: false }} showEditorCard={true} /> - </div> + <Bookmarks query={{ archived: false }} showEditorCard={true} /> </div> ); } diff --git a/apps/web/app/dashboard/favourites/page.tsx b/apps/web/app/dashboard/favourites/page.tsx index e5959af3..be20bd2f 100644 --- a/apps/web/app/dashboard/favourites/page.tsx +++ b/apps/web/app/dashboard/favourites/page.tsx @@ -1,5 +1,4 @@ import Bookmarks from "@/components/dashboard/bookmarks/Bookmarks"; -import GlobalActions from "@/components/dashboard/GlobalActions"; export default async function FavouritesBookmarkPage() { return ( @@ -7,7 +6,6 @@ export default async function FavouritesBookmarkPage() { header={ <div className="flex items-center justify-between"> <p className="text-2xl">⭐️ Favourites</p> - <GlobalActions /> </div> } query={{ favourited: true }} diff --git a/apps/web/app/dashboard/layout.tsx b/apps/web/app/dashboard/layout.tsx index b0ac1f7d..cbd51245 100644 --- a/apps/web/app/dashboard/layout.tsx +++ b/apps/web/app/dashboard/layout.tsx @@ -1,3 +1,4 @@ +import Header from "@/components/dashboard/header/Header"; import MobileSidebar from "@/components/dashboard/sidebar/ModileSidebar"; import Sidebar from "@/components/dashboard/sidebar/Sidebar"; import DemoModeBanner from "@/components/DemoModeBanner"; @@ -14,20 +15,23 @@ export default async function Dashboard({ modal: React.ReactNode; }>) { return ( - <div className="flex min-h-screen w-screen flex-col sm:h-screen sm:flex-row"> - <ValidAccountCheck /> - <div className="hidden flex-none sm:flex"> - <Sidebar /> - </div> - <main className="flex-1 bg-muted sm:overflow-y-auto"> - {serverConfig.demoMode && <DemoModeBanner />} - <div className="block w-full sm:hidden"> - <MobileSidebar /> - <Separator /> + <div> + <Header /> + <div className="flex min-h-[calc(100vh-64px)] w-screen flex-col sm:h-[calc(100vh-64px)] sm:flex-row"> + <ValidAccountCheck /> + <div className="hidden flex-none sm:flex"> + <Sidebar /> </div> - {modal} - <div className="container min-h-screen p-4">{children}</div> - </main> + <main className="flex-1 bg-muted sm:overflow-y-auto"> + {serverConfig.demoMode && <DemoModeBanner />} + <div className="block w-full sm:hidden"> + <MobileSidebar /> + <Separator /> + </div> + {modal} + <div className="min-h-30 container p-4">{children}</div> + </main> + </div> </div> ); } diff --git a/apps/web/app/dashboard/search/page.tsx b/apps/web/app/dashboard/search/page.tsx index e7405c85..a239550c 100644 --- a/apps/web/app/dashboard/search/page.tsx +++ b/apps/web/app/dashboard/search/page.tsx @@ -1,24 +1,15 @@ "use client"; -import { Suspense, useRef } from "react"; +import { Suspense } from "react"; import BookmarksGrid from "@/components/dashboard/bookmarks/BookmarksGrid"; -import GlobalActions from "@/components/dashboard/GlobalActions"; -import { SearchInput } from "@/components/dashboard/search/SearchInput"; import { FullPageSpinner } from "@/components/ui/full-page-spinner"; import { useBookmarkSearch } from "@/lib/hooks/bookmark-search"; function SearchComp() { const { data } = useBookmarkSearch(); - const inputRef: React.MutableRefObject<HTMLInputElement | null> = - useRef<HTMLInputElement | null>(null); - return ( <div className="flex flex-col gap-3"> - <div className="flex gap-2"> - <SearchInput ref={inputRef} autoFocus={true} /> - <GlobalActions /> - </div> {data ? ( <BookmarksGrid bookmarks={data.bookmarks} /> ) : ( diff --git a/apps/web/components/dashboard/GlobalActions.tsx b/apps/web/components/dashboard/GlobalActions.tsx index e09f92a2..9c05dddf 100644 --- a/apps/web/components/dashboard/GlobalActions.tsx +++ b/apps/web/components/dashboard/GlobalActions.tsx @@ -5,7 +5,7 @@ import ChangeLayout from "@/components/dashboard/ChangeLayout"; export default function GlobalActions() { return ( - <div className="flex min-w-max flex-wrap overflow-hidden rounded-md border bg-background"> + <div className="flex min-w-max flex-wrap overflow-hidden"> <ChangeLayout /> <BulkBookmarksAction /> </div> diff --git a/apps/web/components/dashboard/header/Header.tsx b/apps/web/components/dashboard/header/Header.tsx new file mode 100644 index 00000000..11e3b3c3 --- /dev/null +++ b/apps/web/components/dashboard/header/Header.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import Link from "next/link"; +import { redirect } from "next/navigation"; +import GlobalActions from "@/components/dashboard/GlobalActions"; +import ProfileOptions from "@/components/dashboard/header/ProfileOptions"; +import { SearchInput } from "@/components/dashboard/search/SearchInput"; +import HoarderLogo from "@/components/HoarderIcon"; +import { Button } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { getServerAuthSession } from "@/server/auth"; +import { Settings, Shield } from "lucide-react"; + +export default async function Header() { + const session = await getServerAuthSession(); + if (!session) { + redirect("/"); + } + + const adminItem = + session.user.role == "admin" + ? [ + { + name: "Admin", + icon: <Shield size={18} />, + path: "/dashboard/admin", + }, + ] + : []; + + const headerItems = [ + ...adminItem, + { + name: "Settings", + icon: <Settings size={18} />, + path: "/dashboard/settings", + }, + ]; + + return ( + <header className="sticky left-0 right-0 top-0 z-50 flex h-16 items-center justify-between overflow-x-auto overflow-y-hidden bg-background p-4 shadow"> + <div className="hidden items-center sm:flex"> + <Link href={"/dashboard/bookmarks"} className="w-56"> + <HoarderLogo height={20} gap="8px" /> + </Link> + </div> + <div className="flex w-full gap-2"> + <SearchInput className="min-w-40 bg-muted" /> + <GlobalActions /> + </div> + <div className="hidden items-center sm:flex"> + {headerItems.map((item) => ( + <Tooltip key={item.name} delayDuration={0}> + <TooltipTrigger asChild> + <Button variant="ghost"> + <Link href={item.path} className="flex items-center"> + {item.icon} + </Link> + </Button> + </TooltipTrigger> + <TooltipContent side="bottom">{item.name}</TooltipContent> + </Tooltip> + ))} + <ProfileOptions /> + </div> + </header> + ); +} diff --git a/apps/web/components/dashboard/sidebar/SidebarProfileOptions.tsx b/apps/web/components/dashboard/header/ProfileOptions.tsx index af3f1548..df31171c 100644 --- a/apps/web/components/dashboard/sidebar/SidebarProfileOptions.tsx +++ b/apps/web/components/dashboard/header/ProfileOptions.tsx @@ -1,6 +1,7 @@ "use client"; import Link from "next/link"; +import { redirect } from "next/navigation"; import { useToggleTheme } from "@/components/theme-provider"; import { Button } from "@/components/ui/button"; import { @@ -9,8 +10,8 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { LogOut, Moon, MoreHorizontal, Paintbrush, Sun } from "lucide-react"; -import { signOut } from "next-auth/react"; +import { LogOut, Moon, Paintbrush, Sun } from "lucide-react"; +import { signOut, useSession } from "next-auth/react"; import { useTheme } from "next-themes"; function DarkModeToggle() { @@ -35,11 +36,17 @@ function DarkModeToggle() { export default function SidebarProfileOptions() { const toggleTheme = useToggleTheme(); + const { data: session } = useSession(); + if (!session) return redirect("/"); + return ( <DropdownMenu> <DropdownMenuTrigger asChild> - <Button variant="ghost"> - <MoreHorizontal /> + <Button + className="border-new-gray-200 aspect-square rounded-full border-4 bg-black p-0 text-white" + variant="ghost" + > + {session.user.name?.charAt(0) ?? "U"} </Button> </DropdownMenuTrigger> <DropdownMenuContent className="w-fit"> diff --git a/apps/web/components/dashboard/lists/ListHeader.tsx b/apps/web/components/dashboard/lists/ListHeader.tsx index 1655a80b..a6780e1e 100644 --- a/apps/web/components/dashboard/lists/ListHeader.tsx +++ b/apps/web/components/dashboard/lists/ListHeader.tsx @@ -1,7 +1,6 @@ "use client"; import { useRouter } from "next/navigation"; -import GlobalActions from "@/components/dashboard/GlobalActions"; import { Button } from "@/components/ui/button"; import { MoreHorizontal } from "lucide-react"; @@ -43,7 +42,6 @@ export default function ListHeader({ <MoreHorizontal /> </Button> </ListOptions> - <GlobalActions /> </div> </div> ); diff --git a/apps/web/components/dashboard/sidebar/AllLists.tsx b/apps/web/components/dashboard/sidebar/AllLists.tsx index 15bed88a..b6cadea9 100644 --- a/apps/web/components/dashboard/sidebar/AllLists.tsx +++ b/apps/web/components/dashboard/sidebar/AllLists.tsx @@ -47,12 +47,6 @@ export default function AllLists({ path={`/dashboard/favourites`} linkClassName="py-0.5" /> - <SidebarItem - logo={<span className="text-lg">🗄️</span>} - name="Archive" - path={`/dashboard/archive`} - linkClassName="py-0.5" - /> { <CollapsibleBookmarkLists diff --git a/apps/web/components/dashboard/sidebar/ModileSidebar.tsx b/apps/web/components/dashboard/sidebar/ModileSidebar.tsx index 635f63bd..1117dd61 100644 --- a/apps/web/components/dashboard/sidebar/ModileSidebar.tsx +++ b/apps/web/components/dashboard/sidebar/ModileSidebar.tsx @@ -1,8 +1,8 @@ +import ProfileOptions from "@/components/dashboard/header/ProfileOptions"; import HoarderLogoIcon from "@/public/icons/logo-icon.svg"; import { ClipboardList, Search, Settings, Tag } from "lucide-react"; import MobileSidebarItem from "./ModileSidebarItem"; -import SidebarProfileOptions from "./SidebarProfileOptions"; export default async function MobileSidebar() { return ( @@ -16,7 +16,7 @@ export default async function MobileSidebar() { <MobileSidebarItem logo={<ClipboardList />} path="/dashboard/lists" /> <MobileSidebarItem logo={<Tag />} path="/dashboard/tags" /> <MobileSidebarItem logo={<Settings />} path="/dashboard/settings" /> - <SidebarProfileOptions /> + <ProfileOptions /> </ul> </aside> ); diff --git a/apps/web/components/dashboard/sidebar/Sidebar.tsx b/apps/web/components/dashboard/sidebar/Sidebar.tsx index 84a10d2d..13260e07 100644 --- a/apps/web/components/dashboard/sidebar/Sidebar.tsx +++ b/apps/web/components/dashboard/sidebar/Sidebar.tsx @@ -1,16 +1,13 @@ -import Link from "next/link"; import { redirect } from "next/navigation"; -import HoarderLogo from "@/components/HoarderIcon"; import { Separator } from "@/components/ui/separator"; import { api } from "@/server/api/client"; import { getServerAuthSession } from "@/server/auth"; -import { Home, Search, Settings, Shield, Tag } from "lucide-react"; +import { Archive, Home, Search, Tag } from "lucide-react"; import serverConfig from "@hoarder/shared/config"; import AllLists from "./AllLists"; import SidebarItem from "./SidebarItem"; -import SidebarProfileOptions from "./SidebarProfileOptions"; export default async function Sidebar() { const session = await getServerAuthSession(); @@ -30,17 +27,6 @@ export default async function Sidebar() { ] : []; - const adminItem = - session.user.role == "admin" - ? [ - { - name: "Admin", - icon: <Shield size={18} />, - path: "/dashboard/admin", - }, - ] - : []; - const menu: { name: string; icon: JSX.Element; @@ -58,19 +44,14 @@ export default async function Sidebar() { path: "/dashboard/tags", }, { - name: "Settings", - icon: <Settings size={18} />, - path: "/dashboard/settings", + name: "Archive", + icon: <Archive size={18} />, + path: "/dashboard/archive", }, - ...adminItem, ]; return ( - <aside className="flex h-screen w-60 flex-col gap-5 border-r p-4"> - <Link href={"/dashboard/bookmarks"}> - <HoarderLogo height={20} gap="8px" /> - </Link> - <Separator /> + <aside className="flex h-[calc(100vh-64px)] w-60 flex-col gap-5 border-r p-4 "> <div> <ul className="space-y-2 text-sm font-medium"> {menu.map((item) => ( @@ -85,9 +66,8 @@ export default async function Sidebar() { </div> <Separator /> <AllLists initialData={lists} /> - <div className="mt-auto flex justify-between justify-self-end"> - <div className="my-auto"> {session.user.name} </div> - <SidebarProfileOptions /> + <div className="mt-auto flex items-center border-t pt-2"> + Hoarder v{serverConfig.serverVersion} </div> </aside> ); |
