Spaces:
Running
Running
| import { useState, useRef, useEffect } from 'react'; | |
| import { useNavigate, useLocation } from 'react-router-dom'; | |
| import useAuthStore from '../store/useAuthStore'; | |
| import { getCompanySlug } from '../utils/slug'; | |
| import api from '../api/axios'; | |
| export default function Navbar() { | |
| const { user, logout } = useAuthStore(); | |
| const [open, setOpen] = useState(false); | |
| const ref = useRef(null); | |
| const navigate = useNavigate(); | |
| const location = useLocation(); | |
| const isOwner = user?.role === 'owner'; | |
| const slug = getCompanySlug(user?.company_name); | |
| const [balance, setBalance] = useState(null); | |
| const loadWallet = () => { | |
| if (user?.role === 'owner' || user?.role === 'kasir') { | |
| api.get('/api/companies/me').then(res => { | |
| setBalance(res.data.wallet_balance); | |
| }).catch(e => console.error("Gagal load wallet", e)); | |
| } | |
| }; | |
| useEffect(() => { | |
| loadWallet(); | |
| const handleWalletUpdate = () => loadWallet(); | |
| window.addEventListener('refresh_wallet', handleWalletUpdate); | |
| return () => window.removeEventListener('refresh_wallet', handleWalletUpdate); | |
| }, [user]); | |
| const formatRp = (num) => 'Rp ' + (num || 0).toLocaleString('id-ID'); | |
| const getNavPath = (p) => { | |
| if (user?.role === 'super_admin') return p; | |
| return `/${slug}${p}`; | |
| }; | |
| const isOnPOSArea = location.pathname.includes('/pos') || location.pathname.includes('/kasir'); | |
| const isOnReportPage = location.pathname.endsWith('/kasir/report'); | |
| useEffect(() => { | |
| const fn = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }; | |
| document.addEventListener('mousedown', fn); | |
| return () => document.removeEventListener('mousedown', fn); | |
| }, []); | |
| return ( | |
| <header className="navbar-header" style={{ | |
| position: 'sticky', top: 0, zIndex: 30, | |
| background: 'rgba(240,245,248,0.85)', backdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)', | |
| borderBottom: '1px solid rgba(8,45,67,0.06)', | |
| display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 32px', | |
| }}> | |
| <div className="nav-date" style={{ display: 'flex', alignItems: 'center', gap: '8px', color: 'rgba(8,45,67,0.45)', fontSize: '14px', fontWeight: 500 }}> | |
| <span className="material-symbols-outlined" style={{ fontSize: '18px' }}>calendar_today</span> | |
| {new Date().toLocaleDateString('id-ID', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })} | |
| </div> | |
| <div style={{ position: 'relative', display: 'flex', alignItems: 'center', gap: '16px' }} ref={ref}> | |
| {balance !== null && ( | |
| <div style={{ background: 'rgba(14, 165, 233, 0.1)', padding: '6px 12px', borderRadius: '8px', color: '#0ea5e9', fontSize: '13px', fontWeight: 700, display: 'flex', alignItems: 'center', gap: '6px' }} title="Sisa Saldo Wallet"> | |
| <span className="material-symbols-outlined" style={{ fontSize: '16px' }}>account_balance_wallet</span> | |
| {formatRp(balance)} | |
| </div> | |
| )} | |
| <button onClick={() => setOpen(!open)} style={{ | |
| display: 'flex', alignItems: 'center', gap: '12px', padding: '8px 12px', | |
| borderRadius: '12px', border: 'none', cursor: 'pointer', background: 'transparent', | |
| transition: 'background 0.2s', | |
| }} | |
| onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(8,45,67,0.06)'} | |
| onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} | |
| > | |
| <div className="bg-ember" style={{ width: '36px', height: '36px', borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', color: 'white' }}> | |
| <span className="material-symbols-outlined" style={{ fontSize: '18px' }}>person</span> | |
| </div> | |
| <div className="navbar-profile-text" style={{ textAlign: 'left' }}> | |
| <p className="navbar-profile-name" style={{ fontSize: '14px', fontWeight: 700, color: '#0a1f2e', lineHeight: 1.2, whiteSpace: 'nowrap' }}>{user?.name}</p> | |
| <p style={{ fontSize: '11px', color: 'rgba(8,45,67,0.45)', textTransform: 'capitalize' }}>{user?.role}</p> | |
| </div> | |
| <span className="material-symbols-outlined" style={{ fontSize: '18px', color: 'rgba(8,45,67,0.4)', transition: 'transform 0.2s', transform: open ? 'rotate(180deg)' : 'none' }}>expand_more</span> | |
| </button> | |
| {open && ( | |
| <div className="animate-fade-in" style={{ | |
| position: 'absolute', right: 0, top: '100%', marginTop: '8px', width: '220px', | |
| background: '#ffffff', borderRadius: '16px', overflow: 'hidden', | |
| border: '1px solid rgba(8,45,67,0.06)', boxShadow: '0 12px 40px rgba(8,45,67,0.12)', | |
| }}> | |
| <div style={{ padding: '12px 16px', borderBottom: '1px solid rgba(8,45,67,0.06)' }}> | |
| <p style={{ fontSize: '14px', fontWeight: 700, color: '#0a1f2e' }}>{user?.name}</p> | |
| <p style={{ fontSize: '12px', color: 'rgba(8,45,67,0.45)' }}>{user?.email}</p> | |
| </div> | |
| {/* Laporan Harian - muncul di halaman kasir/POS area */} | |
| {isOnPOSArea && ( | |
| <> | |
| <button onClick={() => { navigate(isOnReportPage ? getNavPath('/pos') : getNavPath('/kasir/report')); setOpen(false); }} style={{ | |
| width: '100%', padding: '12px 16px', textAlign: 'left', fontSize: '14px', fontWeight: 500, | |
| color: '#0a1f2e', display: 'flex', alignItems: 'center', gap: '8px', | |
| border: 'none', cursor: 'pointer', background: 'transparent', transition: 'background 0.2s', | |
| borderBottom: '1px solid rgba(8,45,67,0.06)', | |
| }} | |
| onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(8,45,67,0.04)'} | |
| onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} | |
| > | |
| <span className="material-symbols-outlined" style={{ fontSize: '18px', color: '#082D43' }}> | |
| {isOnReportPage ? 'point_of_sale' : 'bar_chart'} | |
| </span> | |
| {isOnReportPage ? 'Kembali ke Kasir' : 'Laporan Harian'} | |
| </button> | |
| {user?.company_settings?.enable_member !== false && ( | |
| <button onClick={() => { navigate(getNavPath('/admin/members')); setOpen(false); }} style={{ | |
| width: '100%', padding: '12px 16px', textAlign: 'left', fontSize: '14px', fontWeight: 500, | |
| color: '#0a1f2e', display: 'flex', alignItems: 'center', gap: '8px', | |
| border: 'none', cursor: 'pointer', background: 'transparent', transition: 'background 0.2s', | |
| borderBottom: '1px solid rgba(8,45,67,0.06)', | |
| }} | |
| onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(8,45,67,0.04)'} | |
| onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} | |
| > | |
| <span className="material-symbols-outlined" style={{ fontSize: '18px', color: '#082D43' }}>group</span> | |
| Member | |
| </button> | |
| )} | |
| </> | |
| )} | |
| {/* Buka Kasir - hanya owner di luar POS area */} | |
| {isOwner && !isOnPOSArea && ( | |
| <button onClick={() => { navigate(getNavPath('/pos')); setOpen(false); }} style={{ | |
| width: '100%', padding: '12px 16px', textAlign: 'left', fontSize: '14px', fontWeight: 500, | |
| color: '#0a1f2e', display: 'flex', alignItems: 'center', gap: '8px', | |
| border: 'none', cursor: 'pointer', background: 'transparent', transition: 'background 0.2s', | |
| borderBottom: '1px solid rgba(8,45,67,0.06)', | |
| }} | |
| onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(8,45,67,0.04)'} | |
| onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} | |
| > | |
| <span className="material-symbols-outlined" style={{ fontSize: '18px', color: '#082D43' }}>point_of_sale</span> | |
| Buka Kasir (POS) | |
| </button> | |
| )} | |
| {/* Kembali ke Dashboard - hanya owner di POS area */} | |
| {isOwner && isOnPOSArea && ( | |
| <button onClick={() => { navigate(getNavPath('/dashboard')); setOpen(false); }} style={{ | |
| width: '100%', padding: '12px 16px', textAlign: 'left', fontSize: '14px', fontWeight: 500, | |
| color: '#0a1f2e', display: 'flex', alignItems: 'center', gap: '8px', | |
| border: 'none', cursor: 'pointer', background: 'transparent', transition: 'background 0.2s', | |
| borderBottom: '1px solid rgba(8,45,67,0.06)', | |
| }} | |
| onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(8,45,67,0.04)'} | |
| onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} | |
| > | |
| <span className="material-symbols-outlined" style={{ fontSize: '18px', color: '#082D43' }}>dashboard</span> | |
| Kembali ke Dashboard | |
| </button> | |
| )} | |
| <button onClick={logout} style={{ | |
| width: '100%', padding: '12px 16px', textAlign: 'left', fontSize: '14px', fontWeight: 500, | |
| color: '#b31b25', display: 'flex', alignItems: 'center', gap: '8px', | |
| border: 'none', cursor: 'pointer', background: 'transparent', transition: 'background 0.2s', | |
| }} | |
| onMouseEnter={(e) => e.currentTarget.style.background = 'rgba(179,27,37,0.05)'} | |
| onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'} | |
| > | |
| <span className="material-symbols-outlined" style={{ fontSize: '18px' }}>logout</span> | |
| Keluar | |
| </button> | |
| </div> | |
| )} | |
| </div> | |
| </header> | |
| ); | |
| } | |