// aerOS Superadmin — Organizations: list, create wizard, detail, actions, impersonate const { useState: _oS } = React; function vBadge(t, v) { return {t[v]}; } function planBadge(tier) { const map = { starter: 'neutral', basic: 'neutral', growth: 'brand', pro: 'brand', scale: 'info', enterprise: 'ok' }; // Real plan name from the hydrated plan list; fallback to a capitalised slug. const plan = ((window.SA_DATA && window.SA_DATA.PLANS) || []).find(p => p.tier === tier); const label = (plan && plan.name) || (tier ? tier.charAt(0).toUpperCase() + tier.slice(1) : '—'); return {label}; } function _lim(v) { return (v === -1 || v >= 9999) ? '∞' : v; } /* ----------------------------- Org list ----------------------------- */ function SAOrgs({ t, orgs, setOrgs, onImpersonate, onCreate }) { const toast = useToast(); const D = window.SA_DATA; const [detail, setDetail] = _oS(null); const [confirm, setConfirm] = _oS(null); const [menuFor, setMenuFor] = _oS(null); const doSuspendToggle = async (org) => { const next = !org.is_active; try { next ? await window.SA_API.activate(org.id) : await window.SA_API.suspend(org.id); setOrgs(os => os.map(o => o.id === org.id ? { ...o, is_active: next } : o)); toast.ok(`${org.name} ${next ? t.active.toLowerCase() : t.suspended.toLowerCase()}.`, t.statusChanged); } catch (e) { toast.error(e.message || 'Hata', t.statusChanged); } }; const doChangePlan = async (org, tier) => { const p = D.PLANS.find(p => p.tier === tier); try { await window.SA_API.changePlan(org.id, tier); setOrgs(os => os.map(o => o.id === org.id ? { ...o, plan_tier: tier, rooms_max: p ? p.maxRooms : o.rooms_max, products_max: p ? p.maxProducts : o.products_max } : o)); toast.ok(`${org.name} → ${p ? p.name : tier}`, t.planChanged); setDetail(d => d && d.id === org.id ? { ...d, plan_tier: tier, rooms_max: p ? p.maxRooms : d.rooms_max, products_max: p ? p.maxProducts : d.products_max } : d); } catch (e) { toast.error(e.message || 'Hata', t.planChanged); } }; const columns = [ { key: 'name', label: t.name, sortable: true, render: o => (
{o.name}
{o.slug}
) }, { key: 'vertical', label: t.vertical, sortable: true, render: o => vBadge(t, o.vertical) }, { key: 'plan_tier', label: t.plan, sortable: true, render: o => planBadge(o.plan_tier) }, { key: 'usage', label: t.usage, render: o => (
{o.rooms_used}/{_lim(o.rooms_max)} {t.rooms.toLowerCase()}
) }, { key: 'monthly_orders', label: t.monthlyOrders, align: 'right', sortable: true, render: o => {o.monthly_orders.toLocaleString('tr-TR')} }, { key: 'is_active', label: t.status, sortable: true, sortValue: o => o.is_active ? 1 : 0, render: o => {o.is_active ? t.active : t.suspended} }, { key: 'created_at', label: t.created, sortable: true, align: 'right', render: o => {o.created_at} }, { key: 'act', label: '', align: 'right', render: o => (
{ e.stopPropagation(); setMenuFor(menuFor === o.id ? null : o.id); }} active={menuFor === o.id} /> {menuFor === o.id && ( <>
{ e.stopPropagation(); setMenuFor(null); }} style={{ position: 'fixed', inset: 0, zIndex: 50 }} />
e.stopPropagation()} style={{ position: 'absolute', right: 0, top: 38, zIndex: 51, background: 'var(--bg-0)', border: '1px solid var(--border)', borderRadius: 'var(--r-sm)', boxShadow: 'var(--shadow-lg)', padding: 5, minWidth: 184, textAlign: 'left' }}> { setDetail(o); setMenuFor(null); }} /> { onImpersonate(o); setMenuFor(null); }} /> { const h = location.hostname, p = location.port ? ':' + location.port : ''; window.open(location.protocol + '//' + o.slug + '.' + h + p + '/guest.html?mode=room&room=101', '_blank'); setMenuFor(null); }} />
{o.is_active ? { setConfirm({ org: o, type: 'suspend' }); setMenuFor(null); }} /> : { setConfirm({ org: o, type: 'activate' }); setMenuFor(null); }} />}
)}
) }, ]; return (
setDetail(o)} filters={[ { key: 'vertical', label: t.vertical, options: [{ value: '', label: t.all + ' · ' + t.vertical }, { value: 'hotel', label: t.hotel }, { value: 'restaurant', label: t.restaurant }, { value: 'hospital', label: t.hospital }] }, { key: 'plan_tier', label: t.plan, options: [{ value: '', label: t.all + ' · ' + t.plan }, ...D.PLANS.map(p => ({ value: p.tier, label: p.name }))] }, { key: 'is_active', label: t.status, options: [{ value: '', label: t.all + ' · ' + t.status }, { value: 'true', label: t.active }, { value: 'false', label: t.suspended }] }, ]} rightTools={} initialSort={{ key: 'created_at', dir: 'desc' }} /> setDetail(null)} onChangePlan={doChangePlan} onToggle={(o) => setConfirm({ org: o, type: o.is_active ? 'suspend' : 'activate' })} onImpersonate={onImpersonate} /> setConfirm(null)} onConfirm={() => doSuspendToggle(confirm.org)} tone={confirm?.type === 'suspend' ? 'danger' : 'ok'} title={confirm?.type === 'suspend' ? t.suspendConfirm : t.activateConfirm} message={<>{confirm?.org.name} {confirm?.type === 'suspend' ? t.suspendMsg : t.activateMsg}} confirmLabel={confirm?.type === 'suspend' ? t.suspend : t.activate} cancelLabel={t.cancel} />
); } function MenuItem({ icon, label, onClick, tone }) { const [h, setH] = _oS(false); return ( ); } /* ----------------------------- Org detail sheet ----------------------------- */ function OrgDetailSheet({ t, org, onClose, onChangePlan, onToggle, onImpersonate }) { const D = window.SA_DATA; if (!org) return null; return ( }>
{vBadge(t, org.vertical)}{planBadge(org.plan_tier)}{org.is_active ? t.active : t.suspended}
{t.created}: {org.created_at}
{t.usage} · {t.limits}
{t.changePlan}
{ set('name', v); if (!f._slugTouched) { const s = autoSlug(v); set('slug', s); if (!f._adminTouched) { set('adminLogin', s ? s + '-admin' : ''); set('adminMail', s ? 'admin@' + s + '.com' : ''); } } }} placeholder="Radisson Collection Vadistanbul" /> { const s = autoSlug(v); set('slug', s); set('_slugTouched', true); if (!f._adminTouched) { set('adminLogin', s ? s + '-admin' : ''); set('adminMail', s ? 'admin@' + s + '.com' : ''); } }} suffix=".qrmenu.boranyazilim.com" /> set('plan', v)} options={D.PLANS.map(p => ({ value: p.tier, label: `${p.name}${p.price ? ' · €' + Number(p.price).toLocaleString('de-DE') + '/ay' : ''}` }))} /> )} {step === 1 && (
set('lang', v)} options={[{ value: 'tr', label: 'Türkçe' }, { value: 'en', label: 'English' }, { value: 'de', label: 'Deutsch' }, { value: 'ar', label: 'العربية' }]} />
{colors.map(c => (
{/* live brand preview */}
Önizleme
{(f.name || 'A')[0]}
{f.name || t.orgName}
Sipariş ver Menü
)} {step === 2 && (
{Icon.info({ size: 16 })}İlk yönetici hesabı oluşturulacak ve giriş bilgileri e-posta ile gönderilecek.
{ set('adminLogin', v); set('_adminTouched', true); }} placeholder={(f.slug || 'otel') + '-admin'} icon={Icon.user} /> set('adminMail', v)} type="email" placeholder="admin@otel.com" /> set('adminPass', v)} type="password" icon={Icon.shield} />
)} ); } Object.assign(window, { SAOrgs, SACreateOrg });