// Sidebar, Topbar, common chrome (Superadmin flavor)
const Icon = ({name, size=18, color='currentColor'}) => {
const paths = {
dashboard: <>>,
building: <>>,
users: <>>,
credit: <>>,
headset: <>>,
activity: <>>,
shield: <>>,
settings: <>>,
bell: <>>,
search: <>>,
chevdown: <>>,
chevright: <>>,
plus: <>>,
check: <>>,
alert: <>>,
zap: <>>,
book: <>>,
logout: <>>,
sun: <>>,
moon: <>>,
ext: <>>,
download: <>>,
filter: <>>,
dot: ,
spark: <>>,
arrow_up: <>>,
arrow_dn: <>>,
};
return (
);
};
const NAV_ITEMS = [
{ k:'dashboard', lbl:'Nadzorna plošča', icon:'dashboard' },
{ k:'organizations',lbl:'Organizacije', icon:'building', badge:12 },
{ k:'users', lbl:'Uporabniki', icon:'users' },
{ k:'billing', lbl:'Naročnine in plačila',icon:'credit' },
{ k:'support', lbl:'Podpora', icon:'headset', badge:5 },
{ k:'system', lbl:'Sistem in integracije', icon:'activity' },
{ k:'audit', lbl:'Dnevnik dogodkov', icon:'shield' },
{ k:'settings', lbl:'Platforma', icon:'settings' },
];
function Sidebar({ active, onNav, collapsed, dark }) {
const w = collapsed ? 72 : 252;
const bg = dark ? '#1A1A1A' : '#FFFFFF';
const border = dark ? '#2B2B2B' : '#E5E5E5';
const fg = dark ? '#C8C8C8' : '#333';
const fgMuted = dark ? '#707070' : '#9A9A9A';
return (
);
}
function Topbar({ onToggleSidebar, onSearch, dark, onToggleDark, role, onRoleChange }) {
const [searchOpen, setSearchOpen] = React.useState(false);
const [notifOpen, setNotifOpen] = React.useState(false);
const [userOpen, setUserOpen] = React.useState(false);
const bg = dark ? '#1A1A1A' : '#fff';
const border = dark ? '#2B2B2B' : '#E5E5E5';
const fg = dark ? '#E5E5E5' : '#333';
const fgMuted = dark ? '#9A9A9A' : '#707070';
return (
setSearchOpen(true)} onBlur={()=>setTimeout(()=>setSearchOpen(false),150)}
style={{
width:'100%', padding:'9px 12px 9px 36px',
background: dark?'#2B2B2B':'#F5F5F5',
border:`1px solid ${dark?'#333':'transparent'}`, borderRadius:10, fontSize:13,
fontFamily:'inherit', color:fg, outline:'none',
}}/>
{searchOpen && (
Predlogi
{['Adria Hotels Group','Marko Kranjc','T-2847 — Booking.com sync','INV-4082'].map(s=>(
e.currentTarget.style.background=dark?'#2B2B2B':'#F5F5F5'}
onMouseLeave={e=>e.currentTarget.style.background='transparent'}>{s}
))}
)}
{notifOpen && (
Obvestila
Označi vse kot prebrano
{[
['alert','#FF9800','Piran Seaside Inn · neplačana naročnina (INV-4082)', 'pred 2 ur'],
['zap','#E53935','Google Hotel Ads integracija ni dosegljiva', 'pred 30 min'],
['check','#2DD164','Nova registracija: Kras Boutique (14-dnevni poskus)', 'včeraj'],
['headset','#2196F3','T-2846 eskaliran na urgent — Grand Union', 'pred 1 ur'],
].map(([ic,c,txt,time],i)=>(
e.currentTarget.style.background=dark?'#2B2B2B':'#FAFAFA'}
onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
))}
)}
{userOpen && (
Zamenjaj vlogo
{['Superadmin','Hotel Admin','Recepcija','Vzdrževanje','Gost (brez dostopa)'].map(r=>(
{onRoleChange(r); setUserOpen(false);}} style={{
padding:'8px 10px', borderRadius:6, fontSize:13, cursor:'pointer',
background: role===r ? (dark?'rgba(33,150,243,.15)':'#E3F2FD') : 'transparent',
color: role===r ? '#2196F3' : fg,
fontWeight: role===r ? 600 : 400,
display:'flex', alignItems:'center', justifyContent:'space-between',
}}>{r} {role===r && }
))}
{['Moj račun','Pomoč in dokumentacija','Bližnjice tipkovnice'].map(x=>(
e.currentTarget.style.background=dark?'#2B2B2B':'#F5F5F5'}
onMouseLeave={e=>e.currentTarget.style.background='transparent'}>{x}
))}
e.currentTarget.style.background=dark?'#2B2B2B':'#FDECEA'}
onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
Odjava
)}
);
}
function PageHeader({ title, subtitle, actions, breadcrumbs, dark }) {
const fgMuted = dark ? '#9A9A9A' : '#707070';
return (
{breadcrumbs && (
{breadcrumbs.map((b,i)=>(
{b}
{i}
))}
)}
{title}
{subtitle &&
{subtitle}
}
{actions &&
{actions}
}
);
}
function Btn({ children, variant='primary', size='md', onClick, icon, dark, style={} }) {
const base = {
fontFamily:'inherit', fontWeight:600, border:'none', cursor:'pointer',
display:'inline-flex', alignItems:'center', gap:8,
transition:'all 120ms', borderRadius:10, whiteSpace:'nowrap',
};
const sizes = {
sm:{padding:'6px 12px', fontSize:13},
md:{padding:'9px 16px', fontSize:14},
lg:{padding:'12px 22px', fontSize:15},
};
const variants = {
primary:{background:'#2196F3', color:'#fff'},
cyan:{background:'#00BCD4', color:'#fff'},
ghost:{background: dark?'#2B2B2B':'#fff', color: dark?'#E5E5E5':'#333', border:`1px solid ${dark?'#333':'#E5E5E5'}`},
danger:{background:'#fff', color:'#E53935', border:'1px solid #E53935'},
subtle:{background: dark?'rgba(33,150,243,.15)':'#E3F2FD', color:'#2196F3'},
};
return (
);
}
function Card({ children, dark, padding=20, style={} }) {
return (
{children}
);
}
function Badge({ children, tone='neutral' }) {
const tones = {
cyan: {bg:'#E0F7FA', fg:'#006064', dot:'#00BCD4'},
blue: {bg:'#E3F2FD', fg:'#0D47A1', dot:'#2196F3'},
orange: {bg:'#FFF3E0', fg:'#E65100', dot:'#FF9800'},
success: {bg:'#E6F9EC', fg:'#117A34', dot:'#2DD164'},
danger: {bg:'#FDECEA', fg:'#B71C1C', dot:'#E53935'},
neutral: {bg:'#F5F5F5', fg:'#4D4D4D', dot:'#9A9A9A'},
purple: {bg:'#F3E5F5', fg:'#4A148C', dot:'#9C27B0'},
};
const t = tones[tone] || tones.neutral;
return (
{children}
);
}
Object.assign(window, { Icon, Sidebar, Topbar, PageHeader, Btn, Card, Badge, NAV_ITEMS });