From 18e835450aa11a72cd9498a63bb75b9183c4c5c5 Mon Sep 17 00:00:00 2001
From: Hafid Nur <73023445+hafidnrzs@users.noreply.github.com>
Date: Sun, 30 Nov 2025 12:44:28 +0700
Subject: [PATCH] feat(backoffice): Create a nested/dropdown sidebar list
- Create a dropdown sidebar list
- Show back the old navigation and group them
- Make the sidebar responsive for mobile view
---
.../backoffice/src/app/(protected)/layout.tsx | 42 ++-
apps/backoffice/src/index.css | 4 +-
.../backoffice-sidebar/backoffice-sidebar.tsx | 294 ++++++++++++++----
3 files changed, 284 insertions(+), 56 deletions(-)
diff --git a/apps/backoffice/src/app/(protected)/layout.tsx b/apps/backoffice/src/app/(protected)/layout.tsx
index bc53fd2..78df939 100644
--- a/apps/backoffice/src/app/(protected)/layout.tsx
+++ b/apps/backoffice/src/app/(protected)/layout.tsx
@@ -1,13 +1,51 @@
-import { FC, ReactElement } from 'react';
+import { FC, ReactElement, useState } from 'react';
import { Outlet } from 'react-router-dom';
import { BackofficeSidebar } from '@imphnen-frontend-service/ui/organisms';
export const AppLayout: FC = (): ReactElement => {
+ const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
+
return (
-
+
setMobileSidebarOpen(false)}
+ />
+ {/* Sticky top header */}
+
+ {/* Mobile menu button (shown on small screens) */}
+
+
+ IMPHNEN Backoffice
+
+
+
diff --git a/apps/backoffice/src/index.css b/apps/backoffice/src/index.css
index a477593..fd9d7f2 100644
--- a/apps/backoffice/src/index.css
+++ b/apps/backoffice/src/index.css
@@ -130,7 +130,7 @@
html {
font-family: 'Bai Jamjuree', sans-serif;
font-weight: 400;
- font-size: 12px;
+ font-size: 14px;
line-height: 1.2;
}
-}
\ No newline at end of file
+}
diff --git a/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx b/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx
index 25079ee..fa28e11 100644
--- a/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx
+++ b/libs/ui/src/organisms/backoffice-sidebar/backoffice-sidebar.tsx
@@ -3,91 +3,257 @@ import {
AuditOutlined,
BookOutlined,
CommentOutlined,
+ DownOutlined,
InboxOutlined,
LogoutOutlined,
+ ReadOutlined,
ReloadOutlined,
+ RightOutlined,
ScheduleOutlined,
SettingOutlined,
+ StockOutlined,
UsergroupAddOutlined,
UserOutlined,
UserSwitchOutlined,
} from '@ant-design/icons';
import { Button } from '../../atoms';
-import { FC, ReactElement } from 'react';
+import { FC, ReactElement, useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { cn, For, useSession } from '@imphnen-frontend-service/utils';
-const MENUS = [
+type MenuItem = {
+ label: string;
+ href?: string;
+ icon?: ReactElement;
+ children?: Array<{ label: string; href: string; icon?: ReactElement }>;
+};
+
+const MENUS: MenuItem[] = [
{
- label: 'Dashboard & Set Gacha',
- href: '/dashboard',
- icon:
,
+ label: 'Hackathon',
+ icon:
,
+ children: [
+ {
+ label: 'Dashboard',
+ href: '/hackathon-dashboard',
+ icon:
,
+ },
+ {
+ label: 'Users',
+ href: '/hackathon-users',
+ icon:
,
+ },
+ {
+ label: 'Teams',
+ href: '/hackathon-teams',
+ icon:
,
+ },
+ {
+ label: 'Submissions',
+ href: '/hackathon-submissions',
+ icon:
,
+ },
+ ],
},
{
- label: 'Hackathon Dashboard',
- href: '/hackathon-dashboard',
- icon:
,
+ label: 'Dimentorin',
+ icon:
,
+ children: [
+ {
+ label: 'Dashboard - Dimentorin',
+ href: '/dashboard-dimentorin',
+ icon:
,
+ },
+ {
+ label: 'User - Dimentorin',
+ href: '/users-dimentorin',
+ icon:
,
+ },
+ {
+ label: 'Session - Dimentorin',
+ href: '/session-dimentorin',
+ icon:
,
+ },
+ {
+ label: 'Content & Roadmap',
+ href: '/roadmap-dimentorin',
+ icon:
,
+ },
+ {
+ label: 'Feedback & Review',
+ href: '/feedback-review-dimentorin',
+ icon:
,
+ },
+ {
+ label: 'Settings - Dimentorin',
+ href: '/settings-dimentorin',
+ icon:
,
+ },
+ ],
},
{
- label: 'Hackathon Users',
- href: '/hackathon-users',
- icon:
,
+ label: 'Gacha',
+ icon:
,
+ children: [
+ {
+ label: 'Dashboard & Set Gacha',
+ href: '/dashboard',
+ icon:
,
+ },
+ {
+ label: 'Gacha Roll',
+ href: '/gacha-roll',
+ icon:
,
+ },
+ {
+ label: 'Validasi Transaksi',
+ href: '/transactions',
+ icon:
,
+ },
+ {
+ label: 'Data Pengiriman Hadiah',
+ href: '/prizes',
+ icon:
,
+ },
+ ],
},
{
- label: 'Hackathon Teams',
- href: '/hackathon-teams',
- icon:
,
+ label: 'Permissions',
+ href: '/permissions',
+ icon:
,
},
{
- label: 'Hackathon Submissions',
- href: '/hackathon-submissions',
- icon:
,
+ label: 'Roles',
+ href: '/roles',
+ icon:
,
+ },
+ {
+ label: 'Data Akun',
+ href: '/accounts',
+ icon:
,
},
- // { label: 'Dashboard - Dimentorin', href: '/dashboard-dimentorin', icon:
},
- // { label: 'Gacha Roll', href: '/gacha-roll', icon:
},
- // { label: 'Permissions', href: '/permissions', icon:
},
- // { label: 'Roles', href: '/roles', icon:
},
- // { label: 'Data Akun', href: '/accounts', icon:
},
- // { label: 'Validasi Transaksi', href: '/transactions', icon:
},
- // { label: 'Data Pengiriman Hadiah', href: '/prizes', icon:
},
- // { label: 'User - Dimentorin', href: '/users-dimentorin', icon:
},
- // { label: 'Session - Dimentorin', href: '/session-dimentorin', icon:
},
- // { label: 'Content & Roadmap', href: '/roadmap-dimentorin', icon:
},
- // { label: 'Feedback & Review', href: '/feedback-review-dimentorin', icon:
},
- // { label: 'Settings - Dimentorin', href: '/settings-dimentorin', icon:
},
];
-export const BackofficeSidebar: FC = (): ReactElement => {
+interface SidebarProps {
+ isOpen?: boolean;
+ onClose?: () => void;
+}
+
+export const BackofficeSidebar: FC
= ({
+ isOpen = false,
+ onClose,
+}): ReactElement => {
const { signOut } = useSession();
const location = useLocation();
+ const [openGroups, setOpenGroups] = useState>({});
const isActive = (path: string) => {
if (path === '/dashboard' && location.pathname === '/dashboard-dimentorin')
return false;
return location.pathname.includes(path);
};
- return (
-