Spaces:
Sleeping
Sleeping
| import { router } from '@inertiajs/react'; | |
| import { | |
| BrainCircuit, | |
| Database, | |
| FileSliders, | |
| KeyRound, | |
| LayoutDashboard, | |
| LogOut, | |
| MessageSquare, | |
| Users, | |
| } from 'lucide-react'; | |
| import type { CSSProperties, ReactNode } from 'react'; | |
| import { useState, useSyncExternalStore } from 'react'; | |
| import { Avatar, AvatarFallback } from '@/components/ui/avatar'; | |
| import { Button } from '@/components/ui/button'; | |
| import AppearanceToggle from '@/components/appearance-toggle'; | |
| import { | |
| Dialog, | |
| DialogContent, | |
| DialogDescription, | |
| DialogFooter, | |
| DialogHeader, | |
| DialogTitle, | |
| } from '@/components/ui/dialog'; | |
| import { | |
| Sidebar, | |
| SidebarContent, | |
| SidebarFooter, | |
| SidebarGroup, | |
| SidebarGroupContent, | |
| SidebarHeader, | |
| SidebarInset, | |
| SidebarMenu, | |
| SidebarMenuButton, | |
| SidebarMenuItem, | |
| SidebarProvider, | |
| SidebarRail, | |
| SidebarTrigger, | |
| useSidebar, | |
| } from '@/components/ui/sidebar'; | |
| import { clearAuthSession, getStoredAuthUser } from '@/lib/auth'; | |
| import { | |
| dashboard as adminDashboard, | |
| llmConfig as adminLlmConfig, | |
| login as adminLogin, | |
| retrievalConfig as adminRetrievalConfig, | |
| vectordbConfig as adminVectorDbConfig, | |
| } from '@/routes/admin'; | |
| import { index as adminApiKeys } from '@/routes/admin/api-keys'; | |
| import { index as adminUsers } from '@/routes/admin/users'; | |
| export type AdminMenu = | |
| | 'api-keys' | |
| | 'dashboard' | |
| | 'llm-config' | |
| | 'rag-config' | |
| | 'retrieval-config' | |
| | 'settings' | |
| | 'users' | |
| | 'vector-retrieval-config' | |
| | 'vectordb-config'; | |
| const adminSidebarStyle = { | |
| '--sidebar-width': '260px', | |
| '--sidebar-width-icon': '64px', | |
| } as CSSProperties; | |
| function getStoredAdminName(): string { | |
| return getStoredAuthUser()?.name ?? 'Admin'; | |
| } | |
| function subscribeToAuthStorage(onStoreChange: () => void): () => void { | |
| if (typeof window === 'undefined') { | |
| return () => {}; | |
| } | |
| window.addEventListener('storage', onStoreChange); | |
| return () => window.removeEventListener('storage', onStoreChange); | |
| } | |
| function useStoredAdminName(): string { | |
| return useSyncExternalStore( | |
| subscribeToAuthStorage, | |
| getStoredAdminName, | |
| () => 'Admin', | |
| ); | |
| } | |
| function getInitials(name: string): string { | |
| return name | |
| .split(' ') | |
| .filter(Boolean) | |
| .slice(0, 2) | |
| .map((part) => part[0]?.toUpperCase()) | |
| .join(''); | |
| } | |
| function confirmLogout(): void { | |
| clearAuthSession(); | |
| router.visit(adminLogin.url()); | |
| } | |
| function AdminSidebarBrand() { | |
| const { state, toggleSidebar } = useSidebar(); | |
| const handleBrandClick = (): void => { | |
| if (state === 'collapsed') { | |
| toggleSidebar(); | |
| } | |
| }; | |
| return ( | |
| <button | |
| aria-label="Buka sidebar" | |
| className="lecturer-brand" | |
| onClick={handleBrandClick} | |
| type="button" | |
| > | |
| <span className="lecturer-brand-mark"> | |
| <MessageSquare className="size-4" /> | |
| </span> | |
| <span className="lecturer-brand-copy">RAG Hub</span> | |
| </button> | |
| ); | |
| } | |
| function AdminSidebar({ activeMenu }: { activeMenu: AdminMenu }) { | |
| const adminName = useStoredAdminName(); | |
| const [isLogoutDialogOpen, setIsLogoutDialogOpen] = useState(false); | |
| return ( | |
| <> | |
| <Sidebar className="lecturer-sidebar" collapsible="icon"> | |
| <SidebarHeader className="lecturer-sidebar-header"> | |
| <AdminSidebarBrand /> | |
| <SidebarTrigger className="lecturer-sidebar-trigger" /> | |
| </SidebarHeader> | |
| <SidebarContent className="lecturer-sidebar-content"> | |
| <SidebarGroup className="lecturer-nav-group"> | |
| <SidebarGroupContent> | |
| <SidebarMenu className="lecturer-nav-list"> | |
| <SidebarMenuItem> | |
| <SidebarMenuButton | |
| className="lecturer-nav-button" | |
| isActive={activeMenu === 'dashboard'} | |
| onClick={() => | |
| router.visit(adminDashboard()) | |
| } | |
| tooltip="Dashboard" | |
| > | |
| <LayoutDashboard className="size-4" /> | |
| <span>Dashboard</span> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| <SidebarMenuItem> | |
| <SidebarMenuButton | |
| className="lecturer-nav-button" | |
| isActive={activeMenu === 'llm-config'} | |
| onClick={() => | |
| router.visit(adminLlmConfig()) | |
| } | |
| tooltip="LLM Config" | |
| > | |
| <BrainCircuit className="size-4" /> | |
| <span>LLM Config</span> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| <SidebarMenuItem> | |
| <SidebarMenuButton | |
| className="lecturer-nav-button" | |
| isActive={ | |
| activeMenu === 'vectordb-config' | |
| } | |
| onClick={() => | |
| router.visit(adminVectorDbConfig()) | |
| } | |
| tooltip="VectorDB Config" | |
| > | |
| <Database className="size-4" /> | |
| <span>VectorDB Config</span> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| <SidebarMenuItem> | |
| <SidebarMenuButton | |
| className="lecturer-nav-button" | |
| isActive={ | |
| activeMenu === 'retrieval-config' | |
| } | |
| onClick={() => | |
| router.visit(adminRetrievalConfig()) | |
| } | |
| tooltip="Retrieval Config" | |
| > | |
| <FileSliders className="size-4" /> | |
| <span>Retrieval Config</span> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| <SidebarMenuItem> | |
| <SidebarMenuButton | |
| className="lecturer-nav-button" | |
| isActive={activeMenu === 'api-keys'} | |
| onClick={() => | |
| router.visit(adminApiKeys()) | |
| } | |
| tooltip="Generate API Key" | |
| > | |
| <KeyRound className="size-4" /> | |
| <span>Generate API Key</span> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| <SidebarMenuItem> | |
| <SidebarMenuButton | |
| className="lecturer-nav-button" | |
| isActive={activeMenu === 'users'} | |
| onClick={() => | |
| router.visit(adminUsers()) | |
| } | |
| tooltip="User Management" | |
| > | |
| <Users className="size-4" /> | |
| <span>User Management</span> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| </SidebarMenu> | |
| </SidebarGroupContent> | |
| </SidebarGroup> | |
| </SidebarContent> | |
| <SidebarFooter className="lecturer-sidebar-footer"> | |
| <SidebarMenu> | |
| <SidebarMenuItem> | |
| <SidebarMenuButton | |
| className="lecturer-user-button" | |
| tooltip={adminName} | |
| > | |
| <Avatar className="lecturer-user-avatar"> | |
| <AvatarFallback> | |
| {getInitials(adminName) || 'AD'} | |
| </AvatarFallback> | |
| </Avatar> | |
| <span className="lecturer-user-copy"> | |
| <span className="lecturer-user-name"> | |
| {adminName} | |
| </span> | |
| <span className="lecturer-user-role"> | |
| Admin | |
| </span> | |
| </span> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| <SidebarMenuItem> | |
| <SidebarMenuButton | |
| className="lecturer-logout-button" | |
| onClick={() => setIsLogoutDialogOpen(true)} | |
| tooltip="Logout" | |
| > | |
| <LogOut className="size-4" /> | |
| <span>Logout</span> | |
| </SidebarMenuButton> | |
| </SidebarMenuItem> | |
| </SidebarMenu> | |
| </SidebarFooter> | |
| <SidebarRail /> | |
| </Sidebar> | |
| <Dialog | |
| onOpenChange={setIsLogoutDialogOpen} | |
| open={isLogoutDialogOpen} | |
| > | |
| <DialogContent className="lecturer-confirm-dialog"> | |
| <DialogHeader> | |
| <DialogTitle>Keluar dari akun admin?</DialogTitle> | |
| <DialogDescription> | |
| Anda perlu login kembali untuk mengakses dashboard | |
| admin. | |
| </DialogDescription> | |
| </DialogHeader> | |
| <DialogFooter className="lecturer-confirm-dialog-footer"> | |
| <Button | |
| className="lecturer-cancel-button" | |
| onClick={() => setIsLogoutDialogOpen(false)} | |
| type="button" | |
| variant="outline" | |
| > | |
| Batal | |
| </Button> | |
| <Button | |
| className="lecturer-danger-button" | |
| onClick={confirmLogout} | |
| type="button" | |
| > | |
| Logout | |
| </Button> | |
| </DialogFooter> | |
| </DialogContent> | |
| </Dialog> | |
| </> | |
| ); | |
| } | |
| export function AdminShell({ | |
| activeMenu = 'dashboard', | |
| children, | |
| }: { | |
| activeMenu?: AdminMenu; | |
| children: ReactNode; | |
| }) { | |
| return ( | |
| <SidebarProvider | |
| className="lecturer-dashboard-shell" | |
| defaultOpen | |
| style={adminSidebarStyle} | |
| > | |
| <AdminSidebar activeMenu={activeMenu} /> | |
| <SidebarInset className="lecturer-workspace"> | |
| <div className="flex items-center justify-between px-3 py-2 md:hidden"> | |
| <SidebarTrigger /> | |
| <AppearanceToggle className="lecturer-theme-toggle" /> | |
| </div> | |
| {children} | |
| </SidebarInset> | |
| </SidebarProvider> | |
| ); | |
| } | |