Spaces:
Running
Running
Mhamdans17
feat: add max branches and cashiers limit, refine admin UI settings, implement tier-based qris fee
cd3f69c | import { useNavigate, useLocation } from 'react-router-dom'; | |
| import useAuthStore from '../store/useAuthStore'; | |
| import { getCompanySlug } from '../utils/slug'; | |
| const superAdminMenus = [ | |
| { path: '/admin/companies', label: 'Perusahaan', icon: 'store' }, | |
| { path: '/admin/tenant-settings', label: 'Pengaturan Tenant', icon: 'settings' }, | |
| { path: '/admin/topups', label: 'Verifikasi Saldo', icon: 'fact_check' }, | |
| { path: '/admin/fees', label: 'Fee Report', icon: 'payments' }, | |
| ]; | |
| const ownerMenus = [ | |
| { path: '/dashboard', label: 'Dashboard', icon: 'dashboard' }, | |
| { path: '/pos', label: 'Kasir (POS)', icon: 'point_of_sale' }, | |
| { path: '/admin/branches', label: 'Cabang', icon: 'store' }, | |
| { path: '/admin/products', label: 'Produk', icon: 'inventory_2' }, | |
| { path: '/admin/categories', label: 'Kategori', icon: 'category' }, | |
| { path: '/admin/kasir', label: 'Kasir', icon: 'group' }, | |
| { path: '/admin/orders', label: 'Riwayat Order', icon: 'receipt_long' }, | |
| { path: '/admin/members', label: 'Member', icon: 'group' }, | |
| { path: '/admin/vouchers', label: 'Katalog Voucher', icon: 'confirmation_number' }, | |
| { path: '/admin/wallet', label: 'Dompet Saldo', icon: 'account_balance_wallet' }, | |
| { path: '/owner/report', label: 'Laporan Harian', icon: 'bar_chart' }, | |
| ]; | |
| const kasirMenus = [ | |
| { path: '/pos', label: 'Kasir (POS)', icon: 'point_of_sale' }, | |
| { path: '/admin/members', label: 'Member', icon: 'group' }, | |
| { path: '/kasir/report', label: 'Laporan Harian', icon: 'bar_chart' }, | |
| ]; | |
| export default function Sidebar() { | |
| const navigate = useNavigate(); | |
| const location = useLocation(); | |
| const { user, logout } = useAuthStore(); | |
| const slug = getCompanySlug(user?.company_name); | |
| const getNavPath = (menuPath) => { | |
| if (user?.role === 'super_admin') return menuPath; | |
| return `/${slug}${menuPath}`; | |
| }; | |
| let menus = user?.role === 'super_admin' | |
| ? superAdminMenus | |
| : user?.role === 'owner' | |
| ? ownerMenus | |
| : kasirMenus; | |
| if (user?.role !== 'super_admin') { | |
| const settings = user?.company_settings || {}; | |
| if (settings.enable_member === false) { | |
| menus = menus.filter(m => m.path !== '/admin/members'); | |
| } | |
| if (settings.enable_voucher === false) { | |
| menus = menus.filter(m => m.path !== '/admin/vouchers'); | |
| } | |
| } | |
| return ( | |
| <aside className="sidebar-full" style={{ | |
| width: '256px', height: '100vh', position: 'fixed', left: 0, top: 0, zIndex: 40, | |
| background: '#f0f5f8', borderRight: '1px solid rgba(8,45,67,0.06)', | |
| display: 'flex', flexDirection: 'column', padding: '24px', | |
| transition: 'width 0.2s ease, padding 0.2s ease', | |
| }}> | |
| {/* Brand */} | |
| <div style={{ display: 'flex', alignItems: 'center', gap: '12px', marginBottom: '36px' }}> | |
| <div className="bg-ember" style={{ | |
| width: '40px', height: '40px', borderRadius: '12px', | |
| display: 'flex', alignItems: 'center', justifyContent: 'center', | |
| color: 'white', boxShadow: '0 4px 12px rgba(8,45,67,0.25)', | |
| }}> | |
| <span className="material-symbols-outlined" style={{ fontVariationSettings: "'FILL' 1", fontSize: '22px' }}>storefront</span> | |
| </div> | |
| <div className="sidebar-brand-text"> | |
| <h1 className="font-headline" style={{ fontSize: '18px', fontWeight: 700, color: '#0a1f2e', lineHeight: 1.2 }}>XOGM Go</h1> | |
| <p style={{ fontSize: '10px', fontWeight: 600, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(8,45,67,0.45)' }}>Sistem Kasir Modern</p> | |
| </div> | |
| </div> | |
| {/* Nav */} | |
| <nav style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: '4px', overflowY: 'auto', overflowX: 'hidden' }}> | |
| {menus.map((menu) => { | |
| const navPath = getNavPath(menu.path); | |
| const active = location.pathname === navPath; | |
| return ( | |
| <button key={menu.path} onClick={() => navigate(navPath)} | |
| style={{ | |
| width: '100%', display: 'flex', alignItems: 'center', gap: '12px', | |
| padding: '12px 16px', borderRadius: '12px', border: 'none', cursor: 'pointer', | |
| fontSize: '13px', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', | |
| textAlign: 'left', lineHeight: '1.3', | |
| transition: 'all 0.2s', | |
| ...(active | |
| ? { background: 'linear-gradient(135deg, #082D43, #0e4a6f)', color: 'white', boxShadow: '0 4px 16px rgba(8,45,67,0.25)' } | |
| : { background: 'transparent', color: 'rgba(8,45,67,0.55)' } | |
| ), | |
| }} | |
| onMouseEnter={(e) => { if (!active) e.currentTarget.style.background = 'rgba(8,45,67,0.06)'; }} | |
| onMouseLeave={(e) => { if (!active) e.currentTarget.style.background = 'transparent'; }} | |
| > | |
| <span className="material-symbols-outlined" style={{ fontSize: '20px', fontVariationSettings: active ? "'FILL' 1" : "'FILL' 0", flexShrink: 0 }}> | |
| {menu.icon} | |
| </span> | |
| <span className="sidebar-label">{menu.label}</span> | |
| </button> | |
| ); | |
| })} | |
| </nav> | |
| {/* Bottom */} | |
| <div style={{ paddingTop: '16px', borderTop: '1px solid rgba(8,45,67,0.06)' }}> | |
| <button className="sidebar-logout" onClick={logout} style={{ | |
| width: '100%', display: 'flex', alignItems: 'center', gap: '12px', | |
| padding: '12px 16px', borderRadius: '12px', border: 'none', cursor: 'pointer', | |
| fontSize: '13px', fontWeight: 600, letterSpacing: '0.04em', textTransform: 'uppercase', | |
| background: 'transparent', color: 'rgba(8,45,67,0.55)', transition: 'all 0.2s', | |
| }} | |
| onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(8,45,67,0.06)'} | |
| onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} | |
| > | |
| <span className="material-symbols-outlined" style={{ fontSize: '20px', flexShrink: 0 }}>logout</span> | |
| <span className="sidebar-label">Keluar</span> | |
| </button> | |
| </div> | |
| </aside> | |
| ); | |
| } | |