gitscope
← marzneshin / Diff · 7bd6cce → 7bd6cce
Changes 7bd6cce..7bd6cce
Patch commit
diff --git a/dashboard/src/components/page/index.tsx b/dashboard/src/components/page/index.tsx
index 383e2fd..48eae7e 100644
--- a/dashboard/src/components/page/index.tsx
+++ b/dashboard/src/components/page/index.tsx
@@ -3,15 +3,10 @@ import { ScrollArea } from '@marzneshin/components'
interface PageProps extends PropsWithChildren { }
-/* WARN: The current page height which effect the scroll
-* area activness, is calculated based on height of header
-* and margin.
-*/
-
export const Page: FC<PageProps> = ({ children }) => {
return (
- <ScrollArea style={{ height: `calc(100vh - 5rem)` }} className="sm:w-screen md:p-3 md:w-[100%]">
- <div className="flex flex-col justify-center items-center h-full sm:w-screen md:w-[100%]">
+ <ScrollArea className="w-full h-full overflow-y-auto">
+ <div className="flex flex-col justify-center items-center h-full w-full">
{children}
</div>
</ScrollArea>
diff --git a/dashboard/src/features/bottom-menu/index.tsx b/dashboard/src/features/bottom-menu/index.tsx
new file mode 100644
index 0000000..8957648
--- /dev/null
+++ b/dashboard/src/features/bottom-menu/index.tsx
@@ -0,0 +1,76 @@
+import { Button, SidebarItem } from "@marzneshin/components";
+import i18n from "@marzneshin/features/i18n";
+import { Link } from "@tanstack/react-router";
+import { FC } from "react";
+import { Box, Home, Server, ServerCog, UsersIcon } from 'lucide-react';
+import { useIsCurrentRoute } from "@marzneshin/hooks";
+
+type BottomMenuItemProps = Omit<SidebarItem, 'isParent' | 'subItem'>
+
+const BottomMenuItem: FC<BottomMenuItemProps & { active: boolean }> = ({ title, icon, to, active }) => {
+ return (
+ <Button asChild variant={active ? "default" : "ghost"} className="gap-1 flex flex-col justify-center text-xs h-full py-2 size-14">
+ <Link to={to}>
+ {icon}
+ {title}
+ </Link>
+ </Button>
+ )
+}
+
+const adminItems: BottomMenuItemProps[] = [
+ {
+ title: i18n.t('users'),
+ to: '/users',
+ icon: <UsersIcon />,
+ },
+ {
+ title: i18n.t('home'),
+ to: '/',
+ icon: <Home />,
+ },
+]
+
+const sudoAdminItems: BottomMenuItemProps[] = [
+ {
+ title: i18n.t('users'),
+ to: '/users',
+ icon: <UsersIcon />,
+ },
+ {
+ title: i18n.t('services'),
+ to: '/services',
+ icon: <Server />,
+ },
+ {
+ title: i18n.t('home'),
+ to: '/',
+ icon: <Home />,
+ },
+ {
+ title: i18n.t('nodes'),
+ to: '/nodes',
+ icon: <Box />,
+ },
+ {
+ title: i18n.t('hosts'),
+ to: '/hosts',
+ icon: <ServerCog />,
+ },
+]
+
+export const DashboardBottomMenu = ({ variant = "admin" }: { variant: "sudo-admin" | "admin" }) => {
+ const { isCurrentRouteActive } = useIsCurrentRoute()
+ const items = variant === "sudo-admin" ? sudoAdminItems : adminItems;
+ return (
+ <div className="w-full flex flex-row justify-between items-center">
+ {items.map((item: BottomMenuItemProps) => (
+ <BottomMenuItem
+ active={isCurrentRouteActive(item.to)}
+ {...item}
+ />
+ ))}
+ </div>
+ )
+}
+
diff --git a/dashboard/src/features/sidebar/sidebar.tsx b/dashboard/src/features/sidebar/sidebar.tsx
index afa1fdb..637fb2d 100644
--- a/dashboard/src/features/sidebar/sidebar.tsx
+++ b/dashboard/src/features/sidebar/sidebar.tsx
@@ -2,7 +2,7 @@ import {
Sidebar,
type SidebarItem,
} from "@marzneshin/components";
-import { useRouterState } from "@tanstack/react-router";
+import { useIsCurrentRoute } from "@marzneshin/hooks";
import type { FC } from "react";
import { sidebarItems as sidebarItemsSudoAdmin, sidebarItemsNonSudoAdmin } from ".";
import { cn } from "@marzneshin/utils";
@@ -24,15 +24,8 @@ export const DashboardSidebar: FC<DashboardSidebarProps> = ({
setOpen,
open,
}) => {
- const router = useRouterState();
const { isSudo } = useAuth();
- const currentActivePath = router.location.pathname;
- const isActive = (path: string) => {
- if (path === "/") {
- return currentActivePath === path;
- }
- return currentActivePath.startsWith(path);
- };
+ const { isCurrentRouteActive } = useIsCurrentRoute()
const sidebarItems = isSudo() ? sidebarItemsSudoAdmin : sidebarItemsNonSudoAdmin
return (
<aside className="size-full py-4 px-4 ">
@@ -51,7 +44,7 @@ export const DashboardSidebar: FC<DashboardSidebarProps> = ({
<Sidebar.Group>{key}</Sidebar.Group>
{sidebarItems[key].map((item: SidebarItem) => (
<Sidebar.Item
- variant={isActive(item.to) ? "active" : "default"}
+ variant={isCurrentRouteActive(item.to) ? "active" : "default"}
className={cn("my-2 border-transparent", {
"w-10 h-10": collapsed,
})}
diff --git a/dashboard/src/features/sidebar/toggle.tsx b/dashboard/src/features/sidebar/toggle.tsx
index 88214bb..8a54f72 100644
--- a/dashboard/src/features/sidebar/toggle.tsx
+++ b/dashboard/src/features/sidebar/toggle.tsx
@@ -1,19 +1,15 @@
import { Button } from '@marzneshin/components';
-import { PanelBottomClose, PanelBottomOpen, PanelRightClose, PanelRightOpen } from 'lucide-react';
+import { PanelRightClose, PanelRightOpen } from 'lucide-react';
import { FC } from 'react';
interface ToggleButtonProps {
- isDesktop: boolean;
collapsed: boolean;
- open: boolean;
onToggle: () => void;
}
-export const ToggleButton: FC<ToggleButtonProps> = ({ isDesktop, collapsed, open, onToggle }) => {
- const Icon = isDesktop ?
- (collapsed ? PanelRightClose : PanelRightOpen) :
- (open ? PanelBottomClose : PanelBottomOpen);
+export const ToggleButton: FC<ToggleButtonProps> = ({ collapsed, onToggle }) => {
+ const Icon = (collapsed ? PanelRightClose : PanelRightOpen);
return (
<Button className="p-2 bg-secondary-foreground border-2 text-primary-foreground dark:bg-secondary dark:text-primary" onClick={onToggle}>
diff --git a/dashboard/src/features/footer/index.tsx b/dashboard/src/features/version-indicator/index.tsx
similarity index 88%
rename from dashboard/src/features/footer/index.tsx
rename to dashboard/src/features/version-indicator/index.tsx
index 7074afe..eb4a99d 100644
--- a/dashboard/src/features/footer/index.tsx
+++ b/dashboard/src/features/version-indicator/index.tsx
@@ -1,6 +1,6 @@
import { Badge } from "@marzneshin/components";
-export const DashboardFooter = () => {
+export const VersionIndicator = () => {
return (
<div className="size-full flex justify-center items-center dark:text-neutral-300 text-neutral-800">
<Badge>
diff --git a/dashboard/src/hooks/index.ts b/dashboard/src/hooks/index.ts
index f0caed8..a08b959 100644
--- a/dashboard/src/hooks/index.ts
+++ b/dashboard/src/hooks/index.ts
@@ -1,3 +1,4 @@
export * from './use-screen-breakpoint'
export * from './use-dialog'
export * from './use-mutation-dialog'
+export * from "./use-is-current-route"
diff --git a/dashboard/src/hooks/use-is-current-route.ts b/dashboard/src/hooks/use-is-current-route.ts
new file mode 100644
index 0000000..8d03d7f
--- /dev/null
+++ b/dashboard/src/hooks/use-is-current-route.ts
@@ -0,0 +1,15 @@
+import { useRouterState } from "@tanstack/react-router";
+
+export const useIsCurrentRoute = () => {
+ const router = useRouterState();
+ const currentActivePath = router.location.pathname;
+
+ const isCurrentRouteActive = (path: string) => {
+ if (path === "/") {
+ return currentActivePath === path;
+ }
+ return currentActivePath.startsWith(path);
+ };
+
+ return { isCurrentRouteActive };
+}
diff --git a/dashboard/src/routes/_dashboard.tsx b/dashboard/src/routes/_dashboard.tsx
index 9e0c7e1..adb35d3 100644
--- a/dashboard/src/routes/_dashboard.tsx
+++ b/dashboard/src/routes/_dashboard.tsx
@@ -1,6 +1,4 @@
import {
- Drawer,
- DrawerContent,
Header,
ResizableHandle,
ResizablePanel,
@@ -24,40 +22,41 @@ import {
} from "@tanstack/react-router";
import { GithubRepo } from "@marzneshin/features/github-repo";
import { CommandBox } from "@marzneshin/features/search-command";
-import { DashboardFooter } from "@marzneshin/features/footer";
-
+import { DashboardBottomMenu } from "@marzneshin/features/bottom-menu";
+import { VersionIndicator } from "@marzneshin/features/version-indicator";
+import { Settings } from "lucide-react";
export const DashboardLayout = () => {
const isDesktop = useScreenBreakpoint("md");
const {
collapsed,
panelRef,
- open,
setCollapsed,
- setOpen,
toggleCollapse,
- toggleOpen,
} = usePanelToggle(isDesktop);
+ const { isSudo } = useAuth();
return (
<div className="flex flex-col w-screen h-screen">
<Header
- start={
+ start={isDesktop ? (
<>
+
<Link to="/">
<HeaderLogo />
</Link>
<ToggleButton
- isDesktop={isDesktop}
collapsed={collapsed}
- open={open}
- onToggle={isDesktop ? toggleCollapse : toggleOpen}
+ onToggle={toggleCollapse}
/>
</>
+ ) : (
+ <Link to="/settings">
+ <Settings className="bg-gray-800 size-10 rounded-md text-2xl p-2" />
+ </Link>
+ )
}
- center={
- <CommandBox />
- }
+ center={<CommandBox />}
end={
<>
<GithubRepo variant={isDesktop ? "full" : "mini"} />
@@ -65,57 +64,51 @@ export const DashboardLayout = () => {
</>
}
/>
- {isDesktop ? (
- <ResizablePanelGroup direction="horizontal" className="block sm:hidden">
- <ResizablePanel
- collapsible
- collapsedSize={2}
- onCollapse={() => setCollapsed(true)}
- onExpand={() => setCollapsed(false)}
- minSize={15}
- className={cn("w-[120px] min-w-[70px]")}
- defaultSize={20}
- ref={panelRef}
- maxSize={30}
- >
- <DashboardSidebar
- collapsed={collapsed}
- setCollapsed={setCollapsed}
- />
- </ResizablePanel>
- <ResizableHandle withHandle className="w-[2px]" />
- <ResizablePanel>
- <main className="flex flex-col items-center justify-center">
+ <div className="flex flex-1 overflow-hidden">
+ {isDesktop ? (
+ <ResizablePanelGroup direction="horizontal" className="flex h-full w-full">
+ <ResizablePanel
+ collapsible
+ collapsedSize={2}
+ onCollapse={() => setCollapsed(true)}
+ onExpand={() => setCollapsed(false)}
+ minSize={15}
+ className={cn("w-[120px] min-w-[70px]")}
+ defaultSize={20}
+ ref={panelRef}
+ maxSize={30}
+ >
+ <DashboardSidebar
+ collapsed={collapsed}
+ setCollapsed={setCollapsed}
+ />
+ </ResizablePanel>
+ <ResizableHandle withHandle className="w-[2px]" />
+ <ResizablePanel className="flex-col flex justify-between">
+ <main className="flex flex-col h-full">
+ <Suspense fallback={<Loading />}>
+ <Outlet />
+ </Suspense>
+ </main>
+ <footer className="h-10 py-2">
+ <VersionIndicator />
+ </footer>
+ </ResizablePanel>
+ </ResizablePanelGroup>
+ ) : (
+ <div className="flex flex-col h-full w-full">
+ <main className="flex flex-col h-full overflow-y-auto">
<Suspense fallback={<Loading />}>
<Outlet />
- <DashboardFooter />
</Suspense>
- <Toaster position="top-center" />
+ <footer className="h-30 border-t-3 py-2 px-5">
+ <DashboardBottomMenu variant={isSudo() ? "sudo-admin" : "admin"} />
+ </footer>
</main>
- </ResizablePanel>
- </ResizablePanelGroup>
- ) : (
- <div>
- <aside>
- <Drawer open={open} onOpenChange={setOpen}>
- <DrawerContent>
- <DashboardSidebar
- collapsed={collapsed}
- setCollapsed={setCollapsed}
- setOpen={setOpen}
- open={open}
- />
- </DrawerContent>
- </Drawer>
- </aside>
- <main className="sm:block">
- <Suspense fallback={<Loading />}>
- <Outlet />
- </Suspense>
- <Toaster position="top-center" />
- </main>
- </div>
- )}
+ </div>
+ )}
+ </div>
+ <Toaster position="top-center" />
</div>
);
};