warungpos-ui / src /components /Navbar.jsx
Mhamdans17
fix: mobile top navbar layout
b9857f0
Raw
History Blame Contribute Delete
9.96 kB
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>
);
}