// Dashboard page — Superadmin platform overview function Sparkline({ data, color='#2196F3', height=40, fill=true }) { const max = Math.max(...data); const min = Math.min(...data); const range = max - min || 1; const w = 100; const pts = data.map((v,i)=>{ const x = (i/(data.length-1))*w; const y = height - ((v-min)/range)*(height-4) - 2; return `${x},${y}`; }).join(' '); const areaPts = `0,${height} ${pts} ${w},${height}`; return ( {fill && } ); } function StatCard({ label, value, delta, tone='blue', trend, dark }) { const tones = { cyan:'#00BCD4', blue:'#2196F3', orange:'#FF9800', green:'#2DD164', purple:'#9C27B0', }; const color = tones[tone]; const fgMuted = dark?'#9A9A9A':'#707070'; const up = delta && !delta.startsWith('-'); return (
{label}
{value}
{delta && (
{delta}
)}
{trend &&
}
); } function MRRChart({ dark }) { const max = Math.max(...MRR_TREND); const fgMuted = dark?'#9A9A9A':'#707070'; const barBg = dark?'#2B2B2B':'#F5F5F5'; return (
MRR rast (zadnjih 12 mesecev)
Ponavljajoči mesečni prihodek · €
MRR
Izvozi
{MRR_TREND.map((v,i)=>{ const h = (v/max)*100; const isCurrent = i===MRR_TREND.length-1; return (
€{(v/1000).toFixed(1)}k
{MRR_MONTHS[i]}
); })}
); } function ActivityFeed({ dark }) { const fgMuted = dark?'#9A9A9A':'#707070'; const items = AUDIT.slice(0,6).map(a=>{ const iconMap = { logged_in:['zap','#2196F3'], impersonated:['shield','#FF9800'], subscription_failed:['alert','#E53935'], exported_guest_data:['download','#FF9800'], resolved_ticket:['check','#2DD164'], backup_completed:['check','#2DD164'], suspended_org:['alert','#FF9800'], integration_error:['alert','#E53935'], changed_pricing:['credit','#2196F3'], new_signup:['plus','#00BCD4'] }; return {...a, ...(iconMap[a.action] ? {_i:iconMap[a.action][0], _c:iconMap[a.action][1]} : {_i:'dot', _c:'#9A9A9A'})}; }); const verb = { logged_in:'se je prijavil', impersonated:'impersoniral', subscription_failed:'neuspešno plačilo naročnine', exported_guest_data:'izvozil podatke gostov', resolved_ticket:'zaključil prijavo', backup_completed:'varnostna kopija zaključena', suspended_org:'suspendiral organizacijo', integration_error:'napaka integracije', changed_pricing:'spremenil cene', new_signup:'nova registracija' }; return (
Nedavna dejavnost
Prikaži vse →
{items.map((it,i)=>(
0 ? `1px solid ${dark?'#2B2B2B':'#F5F5F5'}` : 'none'}}>
{it.actor} {verb[it.action]} — {it.target}
{it.ts} · IP {it.ip}
))}
); } function TopOrgs({ dark }) { const sorted = [...ORGS].sort((a,b)=>b.mrr-a.mrr).slice(0,6); const maxMrr = sorted[0].mrr; const fgMuted = dark?'#9A9A9A':'#707070'; return (
Najbolj aktivne organizacije
Po mesečnem prihodku
{sorted.map((o,i)=>(
0?`1px solid ${dark?'#2B2B2B':'#F5F5F5'}`:'none'}}>
{o.name.split(' ').slice(0,2).map(w=>w[0]).join('')}
{o.name}
{o.city} · {o.rooms} sob · {o.plan}
€{o.mrr}
))} ); } function SystemStatusPanel({ dark }) { const fgMuted = dark?'#9A9A9A':'#707070'; return (
Integracije in storitve
1 napaka
{INTEGRATIONS.slice(0,6).map((it,i)=>{ const toneMap = {ok:'success', degraded:'orange', down:'danger'}; const statusLbl = {ok:'Deluje', degraded:'Upočasnjeno', down:'Nedosegljivo'}; return (
0 ? `1px solid ${dark?'#2B2B2B':'#F5F5F5'}` : 'none'}}>
{it.icon}
{it.name}
{it.uptime}% uptime · {it.latency} ms
{statusLbl[it.status]}
); })}
); } function DashboardPage({ dark, onNav }) { const activeOrgs = ORGS.filter(o=>o.status==='active').length; const totalMrr = ORGS.reduce((s,o)=>s+o.mrr,0); const pastDue = ORGS.filter(o=>o.status==='past_due').length; const openTickets = TICKETS.filter(t=>t.status==='open' || t.status==='in_progress').length; return ( <> Tedensko poročilo onNav('organizations')}>Nova organizacija } />
u.status==='active').length} delta="+1 ta teden" trend={[9,10,10,11,11,12,12,12,12,13,13,13]}/>
{pastDue>0 && (
{pastDue} organizacija ima zapadlo naročnino
Piran Seaside Inn (INV-4082, €149) — 8 dni po roku plačila
onNav('billing')}>Pojdi na Naročnine Pošlji opomin
)}
); } Object.assign(window, { DashboardPage, Sparkline, StatCard });