From 4ea8888da959fa86aac3d1191e62710558395c61 Mon Sep 17 00:00:00 2001 From: hanif salafi Date: Tue, 23 Sep 2025 18:36:36 +0700 Subject: [PATCH] feat: update template ui --- app/(admin)/admin/dashboard/page.tsx | 8 +- components/layout/admin-layout.tsx | 93 +++--- components/layout/modern-header.tsx | 136 +++++++++ components/layout/modern-sidebar.tsx | 266 ++++++++++++++++++ .../main/dashboard/dashboard-container.tsx | 64 +++-- 5 files changed, 481 insertions(+), 86 deletions(-) create mode 100644 components/layout/modern-header.tsx create mode 100644 components/layout/modern-sidebar.tsx diff --git a/app/(admin)/admin/dashboard/page.tsx b/app/(admin)/admin/dashboard/page.tsx index 3c31f0b..61f32a6 100644 --- a/app/(admin)/admin/dashboard/page.tsx +++ b/app/(admin)/admin/dashboard/page.tsx @@ -13,7 +13,7 @@ export default function AdminPage() { if (!mounted) { return ( -
+
); @@ -21,14 +21,12 @@ export default function AdminPage() { return ( -
- -
+
); } diff --git a/components/layout/admin-layout.tsx b/components/layout/admin-layout.tsx index 537bcdb..fd45da0 100644 --- a/components/layout/admin-layout.tsx +++ b/components/layout/admin-layout.tsx @@ -3,30 +3,40 @@ import { useEffect, useState } from "react"; import React, { ReactNode } from "react"; import { ThemeProvider } from "./theme-context"; -import { Breadcrumbs } from "./breadcrumbs"; -import { BurgerButtonIcon } from "../icons"; - import { motion, AnimatePresence } from "framer-motion"; import { SidebarProvider } from "./sidebar-context"; -import { RetractingSidebar } from "../landing-page/retracting-sidedar"; +import { ModernSidebar } from "./modern-sidebar"; +import { ModernHeader } from "./modern-header"; export const AdminLayout = ({ children }: { children: ReactNode }) => { - const [isOpen, setIsOpen] = useState(true); + const [sidebarOpen, setSidebarOpen] = useState(true); const [hasMounted, setHasMounted] = useState(false); - const updateSidebarData = (newData: boolean) => { - setIsOpen(newData); + const toggleSidebar = () => { + setSidebarOpen(!sidebarOpen); }; // Hooks useEffect(() => { setHasMounted(true); + // Auto-collapse sidebar on mobile + const handleResize = () => { + if (window.innerWidth < 1024) { + setSidebarOpen(false); + } else { + setSidebarOpen(true); + } + }; + + handleResize(); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); }, []); // Render loading state until mounted if (!hasMounted) { return ( -
+
); @@ -35,52 +45,33 @@ export const AdminLayout = ({ children }: { children: ReactNode }) => { return ( -
+
- + {/* Modern Sidebar */} + - - + {/* Modern Header */} + + + {/* Main Content */} + - {/* Header */} - -
-
- - -
-
-
- - {/* Main Content */} - -
{children}
-
-
-
+
+ {children} +
+ +
diff --git a/components/layout/modern-header.tsx b/components/layout/modern-header.tsx new file mode 100644 index 0000000..a1566db --- /dev/null +++ b/components/layout/modern-header.tsx @@ -0,0 +1,136 @@ +"use client"; + +import React, { useState, useEffect, useRef } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { + Search, + Bell, + User, + Settings, + LogOut, + ChevronDown, + Menu, +} from "lucide-react"; + +interface ModernHeaderProps { + onMenuToggle: () => void; + sidebarOpen: boolean; +} + +export const ModernHeader: React.FC = ({ + onMenuToggle, + sidebarOpen, +}) => { + const [showUserDropdown, setShowUserDropdown] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + const dropdownRef = useRef(null); + + // Close dropdown when clicking outside + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setShowUserDropdown(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, []); + + return ( +
+
+ {/* Left Section */} +
+ {/* Mobile Menu Button */} + + + {/* Search Bar */} +
+
+ + setSearchQuery(e.target.value)} + className="w-full pl-10 pr-4 py-2 bg-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all duration-200" + /> +
+
+
+ + {/* Right Section */} +
+ {/* Notifications */} + + + {/* User Avatar & Dropdown */} +
+ + + {/* Dropdown Menu */} + + {showUserDropdown && ( + +
+

Admin User

+

admin@netidhub.com

+
+ + + + + +
+ + +
+ )} +
+
+
+
+
+ ); +}; diff --git a/components/layout/modern-sidebar.tsx b/components/layout/modern-sidebar.tsx new file mode 100644 index 0000000..68ad996 --- /dev/null +++ b/components/layout/modern-sidebar.tsx @@ -0,0 +1,266 @@ +"use client"; + +import React, { useState } from "react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { motion, AnimatePresence } from "framer-motion"; +import { + LayoutDashboard, + Image, + Video, + FileText, + Music, + ChevronRight, + ChevronDown, + Menu, + X, +} from "lucide-react"; + +interface SidebarItem { + title: string; + icon: React.ComponentType<{ className?: string }>; + link?: string; + children?: SidebarItem[]; +} + +const sidebarItems: SidebarItem[] = [ + { + title: "Dashboard", + icon: LayoutDashboard, + link: "/admin/dashboard", + }, + { + title: "Content Management", + icon: FileText, + children: [ + { + title: "Foto", + icon: Image, + link: "/admin/content/image", + }, + { + title: "Audio Visual", + icon: Video, + link: "/admin/content/audio-visual", + }, + { + title: "Teks", + icon: FileText, + link: "/admin/content/document", + }, + { + title: "Audio", + icon: Music, + link: "/admin/content/audio", + }, + ], + }, +]; + +interface ModernSidebarProps { + isOpen: boolean; + onToggle: () => void; +} + +export const ModernSidebar: React.FC = ({ + isOpen, + onToggle, +}) => { + const pathname = usePathname(); + const [expandedItems, setExpandedItems] = useState([]); + + const toggleExpanded = (title: string) => { + setExpandedItems((prev) => + prev.includes(title) + ? prev.filter((item) => item !== title) + : [...prev, title] + ); + }; + + const SidebarItemComponent: React.FC<{ + item: SidebarItem; + level?: number; + }> = ({ item, level = 0 }) => { + const isExpanded = expandedItems.includes(item.title); + const hasChildren = item.children && item.children.length > 0; + const isActive = pathname === item.link; + + if (hasChildren) { + return ( +
+ {/* Parent Menu */} + + + {/* Expanded Submenu (when sidebar is open) */} + + {isOpen && isExpanded && ( + +
+ {item.children?.map((child) => ( + + ))} +
+
+ )} +
+ + {/* Collapsed Submenu Icons (when sidebar is collapsed) */} + {!isOpen && ( +
+ {item.children?.map((child) => ( + +
+ +
+ + ))} +
+ )} +
+ ); + } + + return ( + +
0 ? "ml-4" : ""}`} + > + + {isOpen && ( + {item.title} + )} +
+ + ); + }; + + return ( + <> + {/* Mobile Overlay */} + + {isOpen && ( + + )} + + + {/* Sidebar */} + + {/* Header */} +
+ +
+ N +
+ {isOpen && ( + +

+ Netidhub +

+

Admin Panel

+
+ )} +
+
+ + {/* Navigation */} + + +
+ + {/* Floating Toggle Button - Expand */} + + {!isOpen && ( + + + + )} + + + {/* Floating Toggle Button - Collapse */} + + {isOpen && ( + + + + )} + + + ); +}; diff --git a/components/main/dashboard/dashboard-container.tsx b/components/main/dashboard/dashboard-container.tsx index 4f2e0e1..61d8d44 100644 --- a/components/main/dashboard/dashboard-container.tsx +++ b/components/main/dashboard/dashboard-container.tsx @@ -125,18 +125,19 @@ export default function DashboardContainer() { return (
{/* Stats Cards */} -
+
{/* User Profile Card */}
-

{username}

-
+

Welcome back,

+

{username}

+

Admin Dashboard

@@ -146,7 +147,7 @@ export default function DashboardContainer() { {/* Total Posts */}
-

- {summary?.totalAll} +

+ {summary?.totalAll || 0}

-

Total Posts

+

Total Posts

{/* Total Views */}
-

- {summary?.totalViews} +

+ {summary?.totalViews || 0}

-

Total Views

+

Total Views

{/* Total Shares */}
-

- {summary?.totalShares} +

+ {summary?.totalShares || 0}

-

Total Shares

+

Total Shares

{/* Total Comments */}
-

- {summary?.totalComments} +

+ {summary?.totalComments || 0}

-

Total Comments

+

Total Comments

{/* Content Section */} -
+
{/* Analytics Chart */}
-

+

Analytics Overview

@@ -250,25 +251,28 @@ export default function DashboardContainer() { handleChange(option.value, checked as boolean) } /> - {option.label} + {option.label} ))}
+
+

Chart will be displayed here

+
{/* Recent Articles */}
-

+

Recent Content

- + @@ -279,7 +283,7 @@ export default function DashboardContainer() { {article?.map((list: any) => ( @@ -291,10 +295,10 @@ export default function DashboardContainer() { className="h-20 w-20 object-cover rounded-lg shadow-sm flex-shrink-0" />
-

+

{list?.title}

-

+

{convertDateFormat(list?.createdAt)}