aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMd Saban <45597394+mdsaban@users.noreply.github.com>2024-10-12 18:37:20 +0530
committerGitHub <noreply@github.com>2024-10-12 14:07:20 +0100
commit1f768be0485bbfa6b542dd24183fe8389acb9355 (patch)
treeb803eefba19cddfad988b3bd1979b693fbc0f694
parente2644ebc11e9521ece054a846f8c993c322a8332 (diff)
downloadkarakeep-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.tsx16
-rw-r--r--apps/web/app/dashboard/bookmarks/page.tsx10
-rw-r--r--apps/web/app/dashboard/favourites/page.tsx2
-rw-r--r--apps/web/app/dashboard/layout.tsx30
-rw-r--r--apps/web/app/dashboard/search/page.tsx11
-rw-r--r--apps/web/components/dashboard/GlobalActions.tsx2
-rw-r--r--apps/web/components/dashboard/header/Header.tsx71
-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.tsx2
-rw-r--r--apps/web/components/dashboard/sidebar/AllLists.tsx6
-rw-r--r--apps/web/components/dashboard/sidebar/ModileSidebar.tsx4
-rw-r--r--apps/web/components/dashboard/sidebar/Sidebar.tsx34
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&apos;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&apos;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>
);