feat: implement app directory routing, authentication flow, and dynamic reporting module
This commit is contained in:
+76
-10
@@ -4,6 +4,12 @@ import { useEffect, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Store,
|
||||
ClipboardList,
|
||||
@@ -15,7 +21,6 @@ import {
|
||||
Info,
|
||||
BarChart2
|
||||
} from 'lucide-react'
|
||||
import { checkUser } from '@/app/actions'
|
||||
|
||||
const menuItems = [
|
||||
{ name: 'Open Order', href: '/', icon: Store, desc: 'Daftar PO live hari ini' },
|
||||
@@ -27,22 +32,38 @@ const menuItems = [
|
||||
export function Sidebar({ isOpen, onClose }: { isOpen?: boolean; onClose?: () => void }) {
|
||||
const pathname = usePathname()
|
||||
const [userName, setUserName] = useState<string>('Pengguna')
|
||||
const [userNameAccount, setUserNameAccount] = useState<string>('Pengguna')
|
||||
const [userPhoto, setUserPhoto] = useState<string | null>(null)
|
||||
const [userRole, setUserRole] = useState<string | null>(null)
|
||||
const [userId, setUserId] = useState<string | null>(null)
|
||||
const [isLogoutOpen, setIsLogoutOpen] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const id = localStorage.getItem('user_id')
|
||||
if (id) {
|
||||
setUserId(id)
|
||||
checkUser(id).then((u) => {
|
||||
import('@/app/actions').then(({ getSessionUser }) => {
|
||||
getSessionUser().then((u) => {
|
||||
if (u) {
|
||||
setUserId(u.id)
|
||||
setUserName(u.name)
|
||||
setUserNameAccount(u.username)
|
||||
setUserRole(u.role)
|
||||
if (u.photo) setUserPhoto(u.photo)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}, [pathname])
|
||||
|
||||
const handleLogout = async () => {
|
||||
const { logoutUser } = await import('@/app/actions')
|
||||
await logoutUser()
|
||||
window.location.href = '/login'
|
||||
}
|
||||
|
||||
// Dynamic Menu Items based on role
|
||||
const finalMenuItems = [...menuItems]
|
||||
if (userRole === 'superadmin') {
|
||||
finalMenuItems.push({ name: 'User Management', href: '/users', icon: UserCircle, desc: 'Kelola pengguna' })
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Mobile Backdrop */}
|
||||
@@ -106,9 +127,16 @@ export function Sidebar({ isOpen, onClose }: { isOpen?: boolean; onClose?: () =>
|
||||
</span>
|
||||
<ArrowUpRight className="w-3.5 h-3.5 text-slate-400 opacity-0 group-hover:opacity-100 transition-opacity" />
|
||||
</div>
|
||||
<span className="text-[10px] font-mono text-slate-400 truncate">
|
||||
{userId ? `ID: ${userId.slice(0, 10)}...` : 'Aktif'}
|
||||
</span>
|
||||
<div className="flex items-center gap-1.5 mt-0.5">
|
||||
{userRole === 'superadmin' && (
|
||||
<span className="px-1.5 py-[2px] rounded-md bg-purple-100 dark:bg-purple-900/30 text-purple-700 dark:text-purple-400 text-[8px] font-black uppercase tracking-wider">
|
||||
Admin
|
||||
</span>
|
||||
)}
|
||||
<span className="text-[10px] font-medium text-slate-500 dark:text-slate-400 truncate">
|
||||
{userNameAccount ? `@${userNameAccount}` : 'Aktif'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
|
||||
@@ -118,7 +146,7 @@ export function Sidebar({ isOpen, onClose }: { isOpen?: boolean; onClose?: () =>
|
||||
Navigasi Utama
|
||||
</div>
|
||||
<nav className="space-y-1">
|
||||
{menuItems.map((item) => {
|
||||
{finalMenuItems.map((item) => {
|
||||
const Icon = item.icon
|
||||
const isActive = pathname === item.href
|
||||
return (
|
||||
@@ -153,6 +181,44 @@ export function Sidebar({ isOpen, onClose }: { isOpen?: boolean; onClose?: () =>
|
||||
</Link>
|
||||
)
|
||||
})}
|
||||
|
||||
{/* Logout Button */}
|
||||
{userId && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setIsLogoutOpen(true)}
|
||||
className="w-full flex items-center gap-3 px-3.5 py-3 rounded-2xl text-sm font-semibold transition-all duration-200 group relative text-rose-600 dark:text-rose-400 hover:bg-rose-50 dark:hover:bg-rose-950/30 cursor-pointer"
|
||||
>
|
||||
<div className="w-8 h-8 rounded-xl flex items-center justify-center transition-colors bg-rose-100 dark:bg-rose-900/40 text-rose-500 group-hover:bg-rose-200 dark:group-hover:bg-rose-800/60">
|
||||
<X className="w-4 h-4" />
|
||||
</div>
|
||||
<div className="flex flex-col min-w-0 text-left">
|
||||
<span className="leading-tight">Logout</span>
|
||||
<span className="text-[10px] font-normal truncate mt-0.5 text-rose-400/80">Keluar dari akun</span>
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<Dialog open={isLogoutOpen} onOpenChange={setIsLogoutOpen}>
|
||||
<DialogContent className="sm:max-w-xs rounded-3xl p-0 overflow-hidden border-0">
|
||||
<div className="px-6 pt-6 pb-2 text-center">
|
||||
<div className="w-12 h-12 rounded-full bg-rose-100 text-rose-600 flex items-center justify-center mx-auto mb-3">
|
||||
<X className="w-6 h-6" />
|
||||
</div>
|
||||
<DialogTitle className="text-xl font-black text-slate-800">Konfirmasi Logout</DialogTitle>
|
||||
<p className="text-xs text-slate-500 mt-2">
|
||||
Apakah Anda yakin ingin keluar dari akun ini?
|
||||
</p>
|
||||
</div>
|
||||
<div className="p-4 flex gap-3 bg-slate-50">
|
||||
<Button type="button" variant="outline" onClick={() => setIsLogoutOpen(false)} className="flex-1 h-11 rounded-xl">Batal</Button>
|
||||
<Button type="button" onClick={handleLogout} className="flex-1 h-11 rounded-xl bg-rose-600 hover:bg-rose-700 text-white font-bold cursor-pointer">
|
||||
Ya, Logout
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user