warungpos-ui / src /components /Sidebar.jsx
Mhamdans17
feat: add max branches and cashiers limit, refine admin UI settings, implement tier-based qris fee
cd3f69c
Raw
History Blame Contribute Delete
6.1 kB
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>
);
}