Spaces:
Running
Running
File size: 9,957 Bytes
308a91d 17178b7 72c5e3a 308a91d 454ca1f 17178b7 3083f4d 3d08544 3083f4d 3d08544 3083f4d 17178b7 308a91d b9857f0 308a91d 7607c2e 308a91d baf13ff 308a91d 3083f4d 308a91d 7607c2e 308a91d b9857f0 7607c2e 308a91d 7607c2e 308a91d 7607c2e 308a91d 7607c2e 308a91d 407ebaa 16e8fe5 6efafa2 16e8fe5 407ebaa c83c8f8 17178b7 c83c8f8 7607c2e c83c8f8 7607c2e c83c8f8 7607c2e c83c8f8 7607c2e c83c8f8 17178b7 c83c8f8 7607c2e c83c8f8 7607c2e c83c8f8 7607c2e c83c8f8 7607c2e c83c8f8 308a91d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | 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>
);
}
|