feat: implement app directory routing, authentication flow, and dynamic reporting module

This commit is contained in:
Firman Ramdhani
2026-08-28 15:50:01 +07:00
parent 3e68d12ce4
commit 84e6599b23
28 changed files with 1869 additions and 478 deletions
+76 -10
View File
@@ -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>