// aerOS Superadmin — app root: auth gate, routing, impersonation banner
const { useState: _aS, useEffect: _aE } = React;
// Real platform notifications derived from org state (suspended / over-limit).
// No mock feed: empty when nothing needs attention.
function SaNotifications({ orgs, lang }) {
const [open, setOpen] = _aS(false);
const [reqs, setReqs] = _aS([]);
const loadReqs = async () => { try { const r = await window.SA_API.listUpgradeRequests(); setReqs((r || []).filter(x => x.status === 'pending')); } catch (e) {} };
_aE(() => { loadReqs(); const id = setInterval(loadReqs, 30000); return () => clearInterval(id); }, []);
const resolve = async (id, action) => { try { await window.SA_API.resolveUpgrade(id, action); await loadReqs(); } catch (e) {} };
const notifs = [];
(orgs || []).forEach(o => {
if (o.is_active === false) notifs.push({ id: 'su' + o.id, tone: 'danger', text: `${o.name} askıya alındı` });
if (o.rooms_max > 0 && o.rooms_used >= o.rooms_max) notifs.push({ id: 'rm' + o.id, tone: 'warn', text: `${o.name}: oda limiti doldu (${o.rooms_used}/${o.rooms_max})` });
if (o.products_max > 0 && o.products_used >= o.products_max) notifs.push({ id: 'pr' + o.id, tone: 'warn', text: `${o.name}: ürün limiti doldu` });
});
const total = notifs.length + reqs.length;
return (
setOpen(o => !o)} />
{open && (<>
setOpen(false)} style={{ position: 'fixed', inset: 0, zIndex: 40 }} />
{(lang && lang.notifications) || 'Bildirimler'}
{reqs.map(r => (
{Icon.trending ? '' : ''}Plan yükseltme talebi
{r.org_name} → {r.plan_name}{r.note ? ' · ' + r.note : ''}
))}
{total === 0
?
{Icon.bell({ size: 26 })}{(lang && lang.noNotifications) || 'Bildirim yok'}
: notifs.map(n => (
))}
>)}
);
}
function SuperadminApp() {
const ui = useUI('aeros-sa-ui');
const lang = ui.lang === 'en' ? window.SA_DATA.EN : window.SA_DATA.TR;
const [authed, setAuthed] = _aS(!!window.aerosApi.getToken());
const [route, setRoute] = _aS('overview');
const [orgs, setOrgs] = _aS([]);
const [loading, setLoading] = _aS(true);
const [createOpen, setCreateOpen] = _aS(false);
const [impersonating, setImpersonating] = _aS(null);
const loadAll = async () => {
setLoading(true);
try {
const [rows, plans] = await Promise.all([
window.SA_API.listOrgs(),
window.SA_API.listPlans(),
]);
if (Array.isArray(plans) && plans.length) window.SA_DATA.PLANS = plans;
try { const m = await window.SA_API.metrics(); if (m && m.orders_30) window.SA_DATA.ORDERS_30 = m.orders_30; } catch (e) {}
setOrgs(rows || []);
} catch (e) {
setOrgs([]);
// Expired/invalid session -> show login instead of a blank panel.
if (e && (e.status === 401 || e.status === 403)) { try { window.aerosApi.setToken(null); } catch (x) {} setAuthed(false); }
} finally { setLoading(false); }
};
_aE(() => { if (authed) loadAll(); }, [authed]);
if (!authed) return setAuthed(true)} />;
const nav = [
{ section: lang.nav_platform, items: [
{ id: 'overview', label: lang.overview, icon: Icon.grid },
{ id: 'orgs', label: lang.orgs, icon: Icon.building, badge: orgs.length },
] },
{ section: lang.nav_management, items: [
{ id: 'billing', label: lang.billing, icon: Icon.creditcard },
{ id: 'settings', label: lang.settings || 'Ayarlar', icon: Icon.settings || Icon.gear || Icon.creditcard },
] },
];
const titles = { overview: lang.overview, orgs: lang.orgsTitle, billing: lang.billingTitle, settings: lang.settings || 'Ayarlar' };
const subs = { overview: `${lang.today} · ${new Date().toLocaleDateString(ui.lang === 'en' ? 'en-GB' : 'tr-TR', { day: 'numeric', month: 'long', year: 'numeric' })}`, orgs: lang.orgsSub, billing: lang.billingSub, settings: 'Yapay zeka ve platform ayarları' };
const onImpersonate = async (org) => {
try {
const res = await window.SA_API.impersonate(org.id);
// Open the tenant's panel on ITS subdomain (token handoff via #t=), in a
// new tab so the superadmin session here stays intact. No token overwrite.
const slug = res.org_slug || org.slug;
const host = location.hostname, port = location.port ? ':' + location.port : '';
window.open(location.protocol + '//' + slug + '.' + host + port + '/admin.html#t=' + encodeURIComponent(res.token), '_blank');
} catch (e) { /* ignore */ }
};
const banner = impersonating ? (
{Icon.shield({ size: 17 })}
{lang.imperBanner} {impersonating.name} {lang.imperAs}.
) : null;
const topRight = (
<>
>
);
return (
{ window.SA_API.logout(); setAuthed(false); }} style={{ display: 'flex', alignItems: 'center', gap: 9, width: '100%', padding: '8px 10px', borderRadius: 'var(--r-sm)', border: 'none', background: 'transparent', color: 'var(--fg-2)', fontSize: 13, fontWeight: 500, cursor: 'pointer', fontFamily: 'inherit' }}>{Icon.logout({ size: 17 })}{lang.logout}}
>
{route === 'overview' && }
{route === 'orgs' && setCreateOpen(true)} />}
{route === 'billing' && }
{route === 'settings' && }
setCreateOpen(false)} onCreated={async (f) => {
await window.SA_API.createOrg(f); // throws on failure (caught by the wizard)
await loadAll();
setRoute('orgs');
}} />
);
}
ReactDOM.createRoot(document.getElementById('root')).render();