diff --git a/app/components/layoutadmin/header.tsx b/app/components/layoutadmin/header.tsx index 83aea3e..80e696c 100644 --- a/app/components/layoutadmin/header.tsx +++ b/app/components/layoutadmin/header.tsx @@ -1,6 +1,5 @@ -// app/components/layoutadmin/header.tsx import { useState } from "react"; -import { Form } from "@remix-run/react"; +import { Form, useNavigation } from "@remix-run/react"; import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; @@ -14,6 +13,16 @@ import { DropdownMenuTrigger } from "~/components/ui/dropdown-menu"; import { Sheet, SheetContent, SheetTrigger } from "~/components/ui/sheet"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle +} from "~/components/ui/alert-dialog"; import { Menu, Search, @@ -26,221 +35,258 @@ import { ChevronDown, MoreHorizontal, PanelLeftClose, - PanelLeft + PanelLeft, + Loader2, + Shield } from "lucide-react"; +import { SessionData } from "~/sessions.server"; interface AdminHeaderProps { onMenuClick: () => void; sidebarCollapsed: boolean; isMobile: boolean; + user: SessionData; } export function AdminHeader({ onMenuClick, sidebarCollapsed, - isMobile + isMobile, + user }: AdminHeaderProps) { - const [isDark, setIsDark] = useState(false); + const [showLogoutDialog, setShowLogoutDialog] = useState(false); + const navigation = useNavigation(); + const isLoggingOut = navigation.formAction === "/action/logout"; + + const getUserInitials = (email: string) => { + if (email) { + return email.substring(0, 2).toUpperCase(); + } + return "AD"; + }; return ( -
-
- {/* Mobile header */} -
- {/* Hamburger menu button */} - + <> +
+
+ {/* Mobile header */} +
+ {/* Hamburger menu button */} + - {/* Mobile logo */} -
-
- LOGO -
-
- - {/* Mobile more menu */} - - - - - -
- - -
-
-
- - {/* Desktop search - Commented out for now */} - {/*
-
- - -
- - ⌘K - + {/* Mobile logo */} +
+
+ +
+ Admin Panel +
-
*/} -
- {/* Desktop header actions */} -
-
- {/* Theme toggle */} - - - {/* Notifications */} - - - - - -
-

+ + +
+

-
-
-

- New user registered -

-

- 2 minutes ago -

-
-
-

- System update available -

-

- 1 hour ago -

-
-
-

- New message received -

-

- 3 hours ago -

-
-
- + + + {/* Mobile Logout */} +
-
-
+ + +
- {/* User menu */} - - - + + -
- - - - MU - - -
-
- Musharof +
+

+ Admin Notifications + + 5 new + +

+
+
+

+ New user verification required +

+

+ 2 minutes ago +

-
- Administrator +
+

+ New pengelola registration +

+

+ 15 minutes ago +

+
+
+

+ System backup completed +

+

+ 1 hour ago +

- +
- - - -
-
Musharof
-
- admin@example.com + + + + {/* User menu */} + + + + + +
+
Administrator
+
+ {user.email || "admin@example.com"} +
-
- - - - Profile - - - - Settings - - - -
- -
-
- - + + + + Admin Profile + + + + System Settings + + + setShowLogoutDialog(true)} + > + + Sign out + + + +
-
-
+
+ + {/* Logout Confirmation Dialog */} + + + + Konfirmasi Logout Admin + + Apakah Anda yakin ingin keluar dari admin panel? Anda perlu login + kembali untuk mengakses sistem administrasi. + + + + Batal +
+ + {isLoggingOut ? ( + <> + + Logging out... + + ) : ( + "Ya, Logout" + )} + +
+
+
+
+ ); } diff --git a/app/components/layoutadmin/layout-wrapper.tsx b/app/components/layoutadmin/layout-wrapper.tsx index d129dba..db654fe 100644 --- a/app/components/layoutadmin/layout-wrapper.tsx +++ b/app/components/layoutadmin/layout-wrapper.tsx @@ -1,13 +1,17 @@ -// app/components/layoutadmin/layout-wrapper.tsx import { useState, useEffect } from "react"; import { AdminSidebar } from "./sidebar"; import { AdminHeader } from "./header"; +import { SessionData } from "~/sessions.server"; interface AdminLayoutWrapperProps { children: React.ReactNode; + user: SessionData; } -export function AdminLayoutWrapper({ children }: AdminLayoutWrapperProps) { +export function AdminLayoutWrapper({ + children, + user +}: AdminLayoutWrapperProps) { const [sidebarOpen, setSidebarOpen] = useState(false); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [isHovered, setIsHovered] = useState(false); @@ -69,6 +73,7 @@ export function AdminLayoutWrapper({ children }: AdminLayoutWrapperProps) { onMenuClick={handleToggleSidebar} sidebarCollapsed={sidebarCollapsed} isMobile={isMobile} + user={user} /> {/* Page content */} diff --git a/app/components/layoutadmin/sidebar.tsx b/app/components/layoutadmin/sidebar.tsx index cffb20c..0233941 100644 --- a/app/components/layoutadmin/sidebar.tsx +++ b/app/components/layoutadmin/sidebar.tsx @@ -3,7 +3,6 @@ import { Link, useLocation } from "@remix-run/react"; import { Badge } from "~/components/ui/badge"; import { Button } from "~/components/ui/button"; import { ScrollArea } from "~/components/ui/scroll-area"; -import { Separator } from "~/components/ui/separator"; import { Collapsible, CollapsibleContent, @@ -19,21 +18,12 @@ import { LayoutDashboard, Recycle, Users, - TrendingUp, FileText, - CreditCard, - BarChart3, Settings, - MessageCircle, MapPin, - Package, - DollarSign, UserCheck, - Building2, Newspaper, HelpCircle, - Bell, - Shield, ChevronDown, X } from "lucide-react"; @@ -62,197 +52,58 @@ const menuItems: MenuItem[] = [ { title: "Dashboard", icon: , - children: [ - { - title: "Overview", - href: "/sys-rijig-adminpanel/dashboard" - }, - { title: "Analytics", href: "/admin/analytics" }, - { title: "Statistik Sampah", href: "/admin/waste-stats", badge: "new" }, - { title: "Laporan Harian", href: "/admin/daily-reports" } - ] + href: "/sys-rijig-adminpanel/dashboard" }, { title: "Data Sampah", icon: , - children: [ - { title: "Jenis Sampah", href: "/sys-rijig-adminpanel/dashboard/waste" }, - { title: "Harga Sampah", href: "/admin/waste-prices" }, - { title: "Volume Sampah", href: "/admin/waste-volume" }, - { title: "Tracking Sampah", href: "/admin/waste-tracking", badge: "new" }, - { title: "Kualitas Sampah", href: "/admin/waste-quality" } - ] + href: "/sys-rijig-adminpanel/dashboard/waste" }, { title: "Manajemen User", icon: , children: [ - { title: "Masyarakat", href: "/admin/users/community" }, - { title: "Pengepul", href: "/admin/users/collectors" }, - { title: "Pengelola Daur Ulang", href: "/admin/users/recyclers" }, { title: "Verifikasi User", href: "/sys-rijig-adminpanel/dashboard/users", badge: "urgent" }, - { title: "Rating & Review", href: "/admin/users/reviews" } - ] - }, - { - title: "Transaksi", - icon: , - children: [ - { title: "Semua Transaksi", href: "/admin/transactions/all" }, { - title: "Pembayaran Pending", - href: "/admin/transactions/pending", - badge: "urgent" + title: "Masyarakat", + href: "/sys-rijig-adminpanel/masyarakat" }, - { title: "Riwayat Pembayaran", href: "/admin/transactions/history" }, - { title: "Komisi & Fee", href: "/admin/transactions/commission" }, { - title: "Laporan Keuangan", - href: "/admin/transactions/financial-report" + title: "Pengepul", + href: "/sys-rijig-adminpanel/pengepul" + }, + { + title: "Pengelola", + href: "/sys-rijig-adminpanel/pengelola" } ] - } -]; - -const contentMenuItems: MenuItem[] = [ - { - title: "Content Management", - icon: , - children: [ - { title: "Artikel & Blog", href: "/sys-rijig-adminpanel/dashboard/artikel-blog" }, - { title: "Tips & Panduan", href: "/sys-rijig-adminpanel/dashboard/tips-panduan" }, - { title: "FAQ", href: "/admin/content/faq" }, - { - title: "Pengumuman", - href: "/admin/content/announcements", - badge: "new" - }, - { title: "Testimoni", href: "/admin/content/testimonials" } - ] }, { - title: "Lokasi & Mapping", - icon: , - children: [ - { title: "Peta Pengepul", href: "/admin/mapping/collectors" }, - { title: "Area Coverage", href: "/sys-rijig-adminpanel/dashboard/areacoverage" }, - { title: "Titik Pengumpulan", href: "/admin/mapping/collection-points" }, - { title: "Rute Optimal", href: "/admin/mapping/routes", badge: "new" } - ] + title: "Artikel & Blog", + icon: , + href: "/sys-rijig-adminpanel/dashboard/artikel-blog" }, { - title: "Notifikasi", - icon: , - children: [ - { title: "Push Notifications", href: "/admin/notifications/push" }, - { title: "Email Broadcast", href: "/admin/notifications/email" }, - { title: "SMS Gateway", href: "/admin/notifications/sms" }, - { title: "Template Pesan", href: "/admin/notifications/templates" } - ] - } -]; - -const analyticsMenuItems: MenuItem[] = [ - { - title: "Reports & Analytics", - icon: , - children: [ - { title: "Laporan Bulanan", href: "/admin/reports/monthly" }, - { - title: "Performa Pengepul", - href: "/admin/reports/collector-performance" - }, - { title: "Tren Harga", href: "/admin/reports/price-trends" }, - { - title: "Dampak Lingkungan", - href: "/admin/reports/environmental-impact", - badge: "new" - }, - { title: "ROI Analysis", href: "/admin/reports/roi-analysis" } - ] - }, - { - title: "Partner & Kerjasama", - icon: , - children: [ - { title: "Bank Sampah", href: "/admin/partners/waste-banks" }, - { - title: "Industri Daur Ulang", - href: "/admin/partners/recycling-industry" - }, - { title: "Pemerintah Daerah", href: "/admin/partners/government" }, - { title: "NGO & Komunitas", href: "/admin/partners/ngo-community" } - ] - }, - { - title: "Support & Help", + title: "Tips & Panduan", icon: , - children: [ - { - title: "Tiket Support", - href: "/admin/support/tickets", - badge: "urgent" - }, - { title: "Live Chat", href: "/admin/support/chat" }, - { title: "Knowledge Base", href: "/admin/support/knowledge-base" }, - { title: "Training Materials", href: "/admin/support/training" } - ] + href: "/sys-rijig-adminpanel/dashboard/tips-panduan" + }, + { + title: "Area Coverage", + icon: , + href: "/sys-rijig-adminpanel/dashboard/areacoverage" }, { title: "Pengaturan", icon: , - children: [ - { title: "Konfigurasi Sistem", href: "/sys-rijig-adminpanel/dashboard/pengaturan" }, - { title: "Pengaturan Harga", href: "/admin/settings/pricing" }, - { - title: "Role & Permission", - href: "/admin/settings/roles", - badge: "pro" - }, - { title: "Backup & Restore", href: "/admin/settings/backup" }, - { title: "API Management", href: "/admin/settings/api", badge: "pro" } - ] + href: "/sys-rijig-adminpanel/dashboard/pengaturan" } ]; -function MenuSection({ - title, - items, - isCollapsed, - isHovered -}: { - title: string; - items: MenuItem[]; - isCollapsed: boolean; - isHovered: boolean; -}) { - const showText = !isCollapsed || isHovered; - - return ( -
- {showText && ( -

- {title} -

- )} -
    - {items.map((item, index) => ( - - ))} -
-
- ); -} - function MenuItemComponent({ item, isCollapsed, @@ -292,7 +143,8 @@ function MenuItemComponent({ variant="ghost" className={cn( "w-full h-12 p-0 justify-center transition-colors", - isChildActive && "bg-green-100 dark:bg-green-900/30" + (isChildActive || pathname === item.href) && + "bg-green-100 dark:bg-green-900/30" )} asChild={!hasChildren} > @@ -437,33 +289,55 @@ function MenuItemComponent({ asChild variant="ghost" className={cn( - "w-full justify-start px-3 py-2.5 h-auto transition-colors", + "w-full justify-between px-3 py-2.5 h-auto transition-colors", "hover:bg-gray-100 dark:hover:bg-gray-700", isActive && "bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/40 dark:text-green-400 dark:hover:bg-green-900/50" )} > -
- - {item.icon} - - {showText && ( +
+
- {item.title} + {item.icon} + {showText && ( + + {item.title} + + )} +
+ {showText && item.badge && ( + + {item.badge} + )}
@@ -500,12 +374,15 @@ export function AdminSidebar({ {/* Logo */}
{showText && ( - +
♻️
- RIjig Admin + Rijig Admin
)} @@ -530,25 +407,25 @@ export function AdminSidebar({ {/* Navigation */} -
- - - +
+ {/* Main Menu */} +
+ {showText && ( +

+ Admin Panel +

+ )} +
    + {menuItems.map((item, index) => ( + + ))} +
+
{/* Footer CTA - Only show when not collapsed or when hovered */} @@ -557,7 +434,7 @@ export function AdminSidebar({

- RIjig Dashboard + Rijig Dashboard

diff --git a/app/routes/pengelola.tsx b/app/routes/pengelola.tsx index f96cd60..472d0e7 100644 --- a/app/routes/pengelola.tsx +++ b/app/routes/pengelola.tsx @@ -19,8 +19,6 @@ export async function loader({ request }: LoaderFunctionArgs) { }); } -// pada kode app/routes/pengelola.tsx pada bagian: - export default function PengelolaPanelLayout() { const { user } = useLoaderData(); return ( @@ -29,7 +27,3 @@ export default function PengelolaPanelLayout() { ); } - - -/* terdapat error ini: Type '{ children: Element; user: SessionData; }' is not assignable to type 'IntrinsicAttributes & PengelolaLayoutWrapperProps'. - Property 'user' does not exist on type 'IntrinsicAttributes & PengelolaLayoutWrapperProps'. */ \ No newline at end of file diff --git a/app/routes/sys-rijig-adminpanel.tsx b/app/routes/sys-rijig-adminpanel.tsx index 8699d42..b5d7425 100644 --- a/app/routes/sys-rijig-adminpanel.tsx +++ b/app/routes/sys-rijig-adminpanel.tsx @@ -1,25 +1,30 @@ -import { json } from "@remix-run/node"; +import { json, LoaderFunctionArgs } from "@remix-run/node"; import { Outlet, useLoaderData } from "@remix-run/react"; import { AdminLayoutWrapper } from "~/components/layoutadmin/layout-wrapper"; +import { requireUserSession, SessionData } from "~/sessions.server"; -export const loader = async () => { - // Data untuk layout bisa diambil di sini - return json({ - user: { - name: "Musharof", - email: "admin@example.com", - role: "Administrator" - } +interface LoaderData { + user: SessionData; +} + +export async function loader({ request }: LoaderFunctionArgs) { + const userSession = await requireUserSession( + request, + "administrator", + "complete" + ); + + return json({ + user: userSession }); -}; +} export default function AdminPanelLayout() { const { user } = useLoaderData(); return ( - - {/* Outlet akan merender child routes */} + ); -} \ No newline at end of file +}