Skip to content

Commit

Permalink
feature(web): Introduce a new sticky navbar. Fixes 520 (hoarder-app#515)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
mdsaban and MohamedBassem authored Oct 12, 2024
1 parent e2644eb commit 1f768be
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 87 deletions.
16 changes: 5 additions & 11 deletions apps/web/app/dashboard/archive/page.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
}
Expand Down
10 changes: 1 addition & 9 deletions apps/web/app/dashboard/bookmarks/page.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
}
2 changes: 0 additions & 2 deletions apps/web/app/dashboard/favourites/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import Bookmarks from "@/components/dashboard/bookmarks/Bookmarks";
import GlobalActions from "@/components/dashboard/GlobalActions";

export default async function FavouritesBookmarkPage() {
return (
<Bookmarks
header={
<div className="flex items-center justify-between">
<p className="text-2xl">⭐️ Favourites</p>
<GlobalActions />
</div>
}
query={{ favourited: true }}
Expand Down
30 changes: 17 additions & 13 deletions apps/web/app/dashboard/layout.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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>
);
}
11 changes: 1 addition & 10 deletions apps/web/app/dashboard/search/page.tsx
Original file line number Diff line number Diff line change
@@ -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} />
) : (
Expand Down
2 changes: 1 addition & 1 deletion apps/web/components/dashboard/GlobalActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
71 changes: 71 additions & 0 deletions apps/web/components/dashboard/header/Header.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
}
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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() {
Expand All @@ -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">
Expand Down
2 changes: 0 additions & 2 deletions apps/web/components/dashboard/lists/ListHeader.tsx
Original file line number Diff line number Diff line change
@@ -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";

Expand Down Expand Up @@ -43,7 +42,6 @@ export default function ListHeader({
<MoreHorizontal />
</Button>
</ListOptions>
<GlobalActions />
</div>
</div>
);
Expand Down
6 changes: 0 additions & 6 deletions apps/web/components/dashboard/sidebar/AllLists.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions apps/web/components/dashboard/sidebar/ModileSidebar.tsx
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -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>
);
Expand Down
34 changes: 7 additions & 27 deletions apps/web/components/dashboard/sidebar/Sidebar.tsx
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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;
Expand All @@ -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) => (
Expand All @@ -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>
);
Expand Down

0 comments on commit 1f768be

Please sign in to comment.