/* @jsx React.createElement */ const { useState, useEffect, useRef, useMemo } = React; /* ============================================================ Yappari — PENGATURAN (Settings) + per-machine layout editor ============================================================ */ function Stepper({ value, min, max, onSet }) { return React.createElement('div', { className: 'row gap-2', style: { alignItems: 'center' } }, React.createElement('button', { className: 'icon-btn', style: { width: 28, height: 28, fontSize: 17, fontWeight: 800, lineHeight: 1 }, disabled: value <= min, onClick: () => onSet(value - 1) }, '\u2212'), React.createElement('span', { style: { minWidth: 24, textAlign: 'center', fontWeight: 800, fontFamily: 'var(--font-mono)', fontSize: 15 } }, value), React.createElement('button', { className: 'icon-btn', style: { width: 28, height: 28, fontSize: 16, fontWeight: 800, lineHeight: 1 }, disabled: value >= max, onClick: () => onSet(value + 1) }, '+')); } function LayoutEditor({ mid, onChange }) { const Y = window.YAPPARI; const [, force] = useState(0); const apply = (patch) => { Y.setLayout(mid, patch); force(x => x + 1); onChange && onChange(); }; const L = Y.LAYOUTS[mid]; const slots = Y.SLOTS[mid]; const visible = slots.filter(s => !s.hidden); const [selNo, setSelNo] = useState(visible[0] ? visible[0].no : 1); const sel = visible.find(s => s.no === selNo) || visible[0]; const setSlotCap = (no, cap) => { const sc = { ...(L.slotCap || {}) }; sc[no] = cap; apply({ slotCap: sc }); }; const toggleMerge = (s) => { const merges = { ...L.merges }; if (s.span > 1) delete merges[s.no]; else if (s.col + 1 < L.cols) merges[s.no] = 2; apply({ merges }); }; const applyRow = (r, cap) => { const rc = L.rowCap.slice(); rc[r] = cap; const sc = { ...(L.slotCap || {}) }; Object.keys(sc).forEach(no => { if (Math.floor((+no - 1) / L.cols) === r) delete sc[no]; }); apply({ rowCap: rc, slotCap: sc }); }; const dim = (label, value, min, max, on) => React.createElement('div', null, React.createElement('div', { className: 'field-label' }, label), React.createElement(Stepper, { value, min, max, onSet: on })); return React.createElement('div', { className: 'col gap-5', style: { marginTop: 16, paddingTop: 16, borderTop: '1px solid var(--border-subtle)' } }, React.createElement('div', { className: 'row', style: { justifyContent: 'space-between', flexWrap: 'wrap', gap: 16 } }, dim('Baris (shelf)', L.rows, 3, 8, (v) => apply({ rows: v })), dim('Kolom', L.cols, 5, 12, (v) => apply({ cols: v })), React.createElement('div', null, React.createElement('div', { className: 'field-label' }, 'Slot aktif'), React.createElement('div', { style: { fontWeight: 800, fontFamily: 'var(--font-mono)', fontSize: 18, paddingTop: 2, color: 'var(--st-maint)' } }, visible.length))), React.createElement('div', null, React.createElement('div', { className: 'field-label', style: { marginBottom: 6 } }, 'Slot, kapasitas & penggabungan'), React.createElement('p', { style: { fontSize: 12.5, color: 'var(--fg-3)', margin: '0 0 12px', lineHeight: 1.5 } }, 'Angka besar di tiap kotak = kapasitas spiral slot itu. Klik slot untuk memilih, lalu atur kapasitas / gabungkan di bawah.'), React.createElement('div', { style: { overflowX: 'auto', paddingBottom: 4 } }, React.createElement('div', { style: { minWidth: L.cols * 46, display: 'grid', gridTemplateColumns: 'repeat(' + L.cols + ',1fr)', gridAutoRows: 48, gap: 4 } }, visible.map(s => { const isSel = sel && s.no === sel.no; return React.createElement('button', { key: s.no, onClick: () => setSelNo(s.no), title: 'Slot ' + s.no + ' \u00b7 kapasitas ' + s.cap + (s.span > 1 ? ' \u00b7 gabungan \u00d7' + s.span : ''), style: { gridColumn: (s.col + 1) + ' / span ' + s.span, gridRow: s.row + 1, borderRadius: 6, position: 'relative', border: isSel ? '2px solid var(--accent)' : '1px solid ' + (s.span > 1 ? 'var(--st-maint)' : 'var(--border)'), background: s.span > 1 ? 'var(--st-maint-bg)' : 'var(--bg-elev)', cursor: 'pointer', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', boxShadow: isSel ? '0 0 0 3px rgba(240,64,0,.18)' : 'none', }, }, React.createElement('span', { style: { position: 'absolute', top: 3, left: 5, fontSize: 9, fontWeight: 800, color: 'var(--fg-4)', fontFamily: 'var(--font-mono)' } }, s.no), s.span > 1 && React.createElement('span', { style: { position: 'absolute', top: 3, right: 5, fontSize: 9, fontWeight: 800, color: 'var(--st-maint)' } }, '\u21c6'), React.createElement('span', { style: { fontSize: 17, fontWeight: 800, fontFamily: 'var(--font-mono)', color: 'var(--fg-1)', marginTop: 4 } }, s.cap)); }))), ), sel && React.createElement('div', { className: 'card', style: { padding: 14, boxShadow: 'none', background: 'var(--bg-sunken)' } }, React.createElement('div', { className: 'row', style: { justifyContent: 'space-between', flexWrap: 'wrap', gap: 16, alignItems: 'flex-end' } }, React.createElement('div', null, React.createElement('div', { className: 'field-label', style: { marginBottom: 2 } }, 'Slot terpilih'), React.createElement('div', { style: { fontWeight: 800, fontSize: 16, fontFamily: 'var(--font-mono)' } }, 'Slot ' + sel.no + (sel.span > 1 ? ' (gabung \u00d7' + sel.span + ')' : ''))), React.createElement('div', null, React.createElement('div', { className: 'field-label', style: { marginBottom: 4 } }, 'Kapasitas spiral'), React.createElement(Stepper, { value: sel.cap, min: 1, max: 20, onSet: (v) => setSlotCap(sel.no, v) }))), React.createElement('div', { className: 'row gap-2', style: { marginTop: 12, flexWrap: 'wrap' } }, React.createElement('button', { className: 'btn btn-ghost btn-sm', disabled: sel.span === 1 && sel.col + 1 >= L.cols, onClick: () => toggleMerge(sel) }, React.createElement(Icon, { name: 'layers', size: 14 }), sel.span > 1 ? 'Pisah slot' : 'Gabung dengan kanan'), React.createElement('button', { className: 'btn btn-ghost btn-sm', onClick: () => applyRow(sel.row, sel.cap) }, React.createElement(Icon, { name: 'check', size: 14 }), 'Samakan ke shelf ' + String.fromCharCode(65 + sel.row))), ), ); } function MachineSettingCard({ m, onChange }) { const Y = window.YAPPARI; const [edit, setEdit] = useState(false); const [showLayout, setShowLayout] = useState(false); const [name, setName] = useState(m.name); const [loc, setLoc] = useState(m.loc); const L = Y.LAYOUTS[m.id]; const active = Y.SLOTS[m.id].filter(s => !s.hidden).length; const field = (label, value, mono) => React.createElement('div', null, React.createElement('div', { className: 'field-label', style: { marginBottom: 2 } }, label), React.createElement('div', { style: { fontSize: 14, fontWeight: 600, color: 'var(--fg-1)', fontFamily: mono ? 'var(--font-mono)' : 'inherit' } }, value)); return React.createElement('div', { className: 'card', style: { overflow: 'hidden' } }, React.createElement('div', { className: 'card-head' }, React.createElement('div', { className: 'row gap-3' }, React.createElement('div', { style: { width: 40, height: 40, borderRadius: '50%', background: '#fff', display: 'grid', placeItems: 'center', border: '1px solid var(--border-subtle)' } }, React.createElement(PlumBlossom, { size: 24, color: '#004080' })), edit ? React.createElement('input', { className: 'input', value: name, onChange: e => setName(e.target.value), style: { fontWeight: 700, maxWidth: 180 } }) : React.createElement('h3', null, name)), React.createElement('div', { className: 'row gap-2' }, React.createElement(StatusBadge, { status: m.status }), React.createElement('button', { className: 'icon-btn', style: { width: 34, height: 34 }, onClick: () => setEdit(e => !e) }, React.createElement(Icon, { name: edit ? 'check' : 'pencil', size: 16 }))), ), React.createElement('div', { className: 'card-pad' }, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 'var(--sp-4)' } }, React.createElement('div', { style: { gridColumn: '1 / -1' } }, React.createElement('div', { className: 'field-label', style: { marginBottom: 2 } }, 'Lokasi'), edit ? React.createElement('input', { className: 'input', value: loc, onChange: e => setLoc(e.target.value) }) : React.createElement('div', { className: 'row gap-1', style: { fontSize: 14, fontWeight: 600, color: 'var(--fg-1)' } }, React.createElement(Icon, { name: 'mapPin', size: 14, style: { color: 'var(--st-maint)' } }), loc)), field('ID / Serial', m.serial, true), field('Model', m.model), field('Koordinat', m.coord, true), field('Layout planogram', L.rows + ' \u00d7 ' + L.cols + ' \u00b7 ' + active + ' slot'), ), React.createElement('button', { className: 'btn btn-ghost btn-sm', style: { marginTop: 16 }, onClick: () => setShowLayout(s => !s) }, React.createElement(Icon, { name: 'planogram', size: 15 }), showLayout ? 'Tutup pengaturan layout' : 'Atur layout & kapasitas', React.createElement(Icon, { name: showLayout ? 'chevronUp' : 'chevronDown', size: 14 })), showLayout && React.createElement(LayoutEditor, { mid: m.id, onChange }), ), ); } function UserModal({ user, machines, onClose, onSaved }) { const Y = window.YAPPARI; const isNew = !user; const [name, setName] = useState(user ? user.name : ''); const [email, setEmail] = useState(user ? user.email : ''); const [role, setRole] = useState(user ? user.role : 'operator'); const [phone, setPhone] = useState(user ? user.phone || '' : ''); const [active, setActive] = useState(user ? user.active !== false : true); const [password, setPassword] = useState(''); const [assigned, setAssigned] = useState(() => { const map = {}; (user && user.machines || []).forEach(m => { map[m.machine_id] = m.relation || 'staff'; }); return map; }); const [busy, setBusy] = useState(false); const [err, setErr] = useState(''); const toggleM = (id) => setAssigned(a => { const n = { ...a }; if (n[id]) delete n[id]; else n[id] = 'staff'; return n; }); const setRel = (id, rel) => setAssigned(a => ({ ...a, [id]: rel })); const save = async () => { setErr(''); if (!name || !email) { setErr('Nama & email wajib'); return; } if (isNew && (!password || password.length < 6)) { setErr('Password minimal 6 karakter untuk user baru'); return; } setBusy(true); try { const machinesArr = Object.entries(assigned).map(([machine_id, relation]) => ({ machine_id, relation })); const body = { email, name, role, phone, active, machines: machinesArr }; if (password) body.password = password; await Y.saveUser(body); onSaved(); } catch (e) { setErr(e.message || 'Gagal menyimpan'); setBusy(false); } }; return React.createElement('div', { style: { position: 'fixed', inset: 0, zIndex: 200, background: 'rgba(0,20,38,.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16, animation: 'fadeIn .15s' }, onClick: onClose }, React.createElement('div', { onClick: e => e.stopPropagation(), style: { width: 560, maxWidth: '96vw', maxHeight: '92vh', overflowY: 'auto', background: 'var(--bg)', borderRadius: 'var(--r-4)', boxShadow: 'var(--shadow-3)' } }, React.createElement('div', { style: { padding: '18px 22px', borderBottom: '1px solid var(--border-subtle)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' } }, React.createElement('h3', { style: { margin: 0 } }, isNew ? 'Tambah pengguna' : 'Edit pengguna'), React.createElement('button', { className: 'icon-btn', onClick: onClose }, React.createElement(Icon, { name: 'x', size: 18 }))), React.createElement('div', { style: { padding: 22 } }, React.createElement('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 } }, React.createElement('div', null, React.createElement('div', { className: 'field-label' }, 'Nama'), React.createElement('input', { className: 'input', value: name, onChange: e => setName(e.target.value) })), React.createElement('div', null, React.createElement('div', { className: 'field-label' }, 'Email'), React.createElement('input', { className: 'input', type: 'email', value: email, disabled: !isNew, onChange: e => setEmail(e.target.value) })), React.createElement('div', null, React.createElement('div', { className: 'field-label' }, 'Role'), React.createElement('select', { className: 'select-native', value: role, onChange: e => setRole(e.target.value) }, ['admin', 'operator', 'teknisi', 'finance'].map(r => React.createElement('option', { key: r, value: r }, r)))), React.createElement('div', null, React.createElement('div', { className: 'field-label' }, 'Telepon'), React.createElement('input', { className: 'input', value: phone, onChange: e => setPhone(e.target.value) })), React.createElement('div', { style: { gridColumn: '1 / -1' } }, React.createElement('div', { className: 'field-label' }, isNew ? 'Password' : 'Password baru (kosongkan jika tidak diubah)'), React.createElement('input', { className: 'input', type: 'password', value: password, placeholder: '••••••', onChange: e => setPassword(e.target.value) }))), React.createElement('div', { className: 'row', style: { justifyContent: 'space-between', marginTop: 14, paddingTop: 14, borderTop: '1px solid var(--border-subtle)' } }, React.createElement('div', null, React.createElement('div', { style: { fontWeight: 700, fontSize: 14 } }, 'Akun aktif'), React.createElement('div', { style: { fontSize: 12, color: 'var(--fg-3)' } }, 'Nonaktif = tidak bisa login')), React.createElement('button', { className: 'toggle ' + (active ? 'on' : ''), onClick: () => setActive(a => !a) })), role !== 'admin' && React.createElement('div', { style: { marginTop: 16 } }, React.createElement('div', { className: 'field-label', style: { marginBottom: 6 } }, 'Mesin yang ditugaskan'), React.createElement('p', { style: { fontSize: 12.5, color: 'var(--fg-3)', margin: '0 0 10px' } }, 'Pilih mesin yang boleh dikelola user ini. Admin otomatis bisa akses semua mesin.'), React.createElement('div', { className: 'col gap-2' }, machines.map(m => React.createElement('div', { key: m.id, className: 'row', style: { justifyContent: 'space-between', alignItems: 'center', padding: '8px 10px', borderRadius: 8, border: '1px solid var(--border-subtle)', background: assigned[m.id] ? 'var(--st-maint-bg)' : 'transparent' } }, React.createElement('label', { className: 'row gap-2', style: { cursor: 'pointer', flex: 1 } }, React.createElement('input', { type: 'checkbox', checked: !!assigned[m.id], onChange: () => toggleM(m.id) }), React.createElement('div', null, React.createElement('div', { style: { fontWeight: 700, fontSize: 13.5 } }, m.name), React.createElement('div', { style: { fontSize: 12, color: 'var(--fg-3)' } }, m.loc))), assigned[m.id] && React.createElement('select', { className: 'select-native', style: { width: 'auto', minWidth: 110 }, value: assigned[m.id], onChange: e => setRel(m.id, e.target.value) }, [['owner', 'Pemilik'], ['client', 'Mesin klien'], ['staff', 'Staf']].map(([v, l]) => React.createElement('option', { key: v, value: v }, l))))))), err && React.createElement('div', { style: { marginTop: 14, color: 'var(--st-error)', fontSize: 13 } }, err), React.createElement('div', { className: 'row gap-2', style: { marginTop: 18, justifyContent: 'flex-end' } }, React.createElement('button', { className: 'btn btn-ghost', onClick: onClose }, 'Batal'), React.createElement('button', { className: 'btn btn-primary', disabled: busy, onClick: save }, busy ? 'Menyimpan…' : 'Simpan'))), )); } function UsersSection({ machines }) { const Y = window.YAPPARI; const isAdmin = Y.CURRENT_USER && Y.CURRENT_USER.role === 'admin'; const [users, setUsers] = useState(Y.USERS); const [modal, setModal] = useState(null); const roleColor = { admin: 'var(--accent)', operator: 'var(--st-maint)', teknisi: 'var(--st-warn)', finance: 'var(--st-online)' }; const reload = async () => { try { const fresh = await window.yappariApi('/api/users'); setUsers(fresh.map(u => ({ ...u, machines: u.machines || [] }))); } catch (e) {} setModal(null); }; const del = async (email) => { if (!confirm('Hapus pengguna ' + email + '?')) return; try { await Y.deleteUser(email); reload(); } catch (e) { alert(e.message); } }; if (!isAdmin) { return React.createElement(SectionCard, { title: 'Akses pengguna', sub: 'Hanya admin' }, React.createElement('div', { style: { padding: 24, color: 'var(--fg-3)', fontSize: 13 } }, 'Hanya admin yang dapat mengelola pengguna.')); } const avatar = (u) => React.createElement('div', { style: { width: 36, height: 36, borderRadius: '50%', background: roleColor[u.role] || 'var(--fg-3)', color: '#fff', display: 'grid', placeItems: 'center', fontWeight: 800, fontSize: 14 } }, (u.name || '?').split(' ').map(n => n[0]).slice(0, 2).join('')); const row = (u, i) => React.createElement('tr', { key: u.email || i }, React.createElement('td', null, React.createElement('div', { className: 'row gap-3' }, avatar(u), React.createElement('div', null, React.createElement('div', { className: 'strong' }, u.name), React.createElement('div', { style: { fontSize: 12, color: 'var(--fg-3)' } }, u.email)))), React.createElement('td', null, React.createElement('span', { className: 'badge', style: { color: roleColor[u.role], background: 'var(--bg-sunken)' } }, u.role)), React.createElement('td', null, u.role === 'admin' ? React.createElement('span', { style: { fontSize: 12.5, color: 'var(--fg-3)' } }, 'Semua mesin') : React.createElement('span', { style: { fontSize: 12.5 } }, (u.machines || []).length + ' mesin')), React.createElement('td', { style: { textAlign: 'center' } }, React.createElement('span', { className: 'badge ' + (u.active ? 'badge-online' : 'badge-offline') }, u.active ? 'Aktif' : 'Nonaktif')), React.createElement('td', null, React.createElement('div', { className: 'row gap-1' }, React.createElement('button', { className: 'icon-btn', style: { width: 30, height: 30 }, onClick: () => setModal({ user: u }) }, React.createElement(Icon, { name: 'pencil', size: 14 })), React.createElement('button', { className: 'icon-btn', style: { width: 30, height: 30, color: 'var(--st-error)' }, onClick: () => del(u.email) }, React.createElement(Icon, { name: 'trash', size: 14 }))))); const card = React.createElement(SectionCard, { title: 'Pengguna & akses', sub: users.length + ' pengguna', motif: true, right: React.createElement('button', { className: 'btn btn-primary btn-sm', onClick: () => setModal({ new: true }) }, React.createElement(Icon, { name: 'plus', size: 14 }), 'Tambah'), }, React.createElement('table', { className: 'tbl' }, React.createElement('thead', null, React.createElement('tr', null, React.createElement('th', null, 'Pengguna'), React.createElement('th', null, 'Role'), React.createElement('th', null, 'Mesin'), React.createElement('th', { style: { textAlign: 'center' } }, 'Status'), React.createElement('th', null, ''))), React.createElement('tbody', null, users.map(row)))); const modalEl = modal ? React.createElement(UserModal, { user: modal.user, machines, onClose: () => setModal(null), onSaved: reload }) : null; return React.createElement(React.Fragment, null, card, modalEl); } function XenditSection() { const Y = window.YAPPARI; const isAdmin = Y.CURRENT_USER && Y.CURRENT_USER.role === 'admin'; const x = (Y.SETTINGS && Y.SETTINGS.xendit) || {}; const [enabled, setEnabled] = useState(!!x.enabled); const [secret, setSecret] = useState(''); const [webhook, setWebhook] = useState(''); const [busy, setBusy] = useState(false); const [msg, setMsg] = useState(''); const save = async () => { setBusy(true); setMsg(''); try { const body = { enabled }; if (secret) body.secret_key = secret; if (webhook) body.webhook_token = webhook; await Y.saveSettings('xendit', body); setMsg('Tersimpan'); setSecret(''); setWebhook(''); } catch (e) { setMsg(e.message || 'Gagal'); } setBusy(false); }; return React.createElement(SectionCard, { title: 'Pembayaran — Xendit (QRIS)', sub: x.enabled ? 'Aktif' : 'Belum aktif', right: React.createElement('span', { className: 'badge ' + (x.enabled ? 'badge-online' : 'badge-warn') }, x.enabled ? 'Aktif' : 'Menunggu key'), bodyStyle: { padding: 'var(--sp-5)' } }, React.createElement('div', { className: 'col gap-4' }, React.createElement('div', { style: { fontSize: 13, color: 'var(--fg-2)', lineHeight: 1.6, padding: '10px 12px', background: 'var(--st-warn-bg)', borderRadius: 8, border: '1px solid var(--st-warn)' } }, React.createElement('div', { className: 'row gap-2', style: { fontWeight: 700, color: 'var(--st-warn)', marginBottom: 4 } }, React.createElement(Icon, { name: 'alert', size: 15 }), 'Integrasi belum aktif'), 'Masukkan Secret Key & Webhook Token dari dashboard Xendit untuk mengaktifkan pembayaran QRIS di mesin. Key disimpan aman di server (tidak ditampilkan kembali).'), React.createElement('div', null, React.createElement('div', { className: 'field-label' }, 'Secret Key' + (x.has_secret_key ? ' (sudah tersimpan)' : '')), React.createElement('input', { className: 'input', type: 'password', placeholder: x.has_secret_key ? '•••••• (kosongkan jika tidak diubah)' : 'xnd_production_…', value: secret, disabled: !isAdmin, onChange: e => setSecret(e.target.value) })), React.createElement('div', null, React.createElement('div', { className: 'field-label' }, 'Webhook Verification Token' + (x.has_webhook_token ? ' (sudah tersimpan)' : '')), React.createElement('input', { className: 'input', type: 'password', placeholder: x.has_webhook_token ? '••••••' : 'token…', value: webhook, disabled: !isAdmin, onChange: e => setWebhook(e.target.value) })), React.createElement('div', null, React.createElement('div', { className: 'field-label' }, 'Callback URL (set di dashboard Xendit)'), React.createElement('input', { className: 'input', readOnly: true, value: x.callback_url || 'https://api.yappari.id/api/report/xendit', style: { fontFamily: 'var(--font-mono)', fontSize: 12.5 } })), React.createElement('div', { className: 'row', style: { justifyContent: 'space-between', paddingTop: 8, borderTop: '1px solid var(--border-subtle)' } }, React.createElement('div', { style: { paddingTop: 8 } }, React.createElement('div', { style: { fontWeight: 700, fontSize: 14 } }, 'Aktifkan QRIS'), React.createElement('div', { style: { fontSize: 12, color: 'var(--fg-3)' } }, 'Mesin akan menampilkan QR pembayaran')), React.createElement('button', { className: 'toggle ' + (enabled ? 'on' : ''), style: { marginTop: 10 }, disabled: !isAdmin, onClick: () => setEnabled(v => !v) })), isAdmin && React.createElement('div', { className: 'row gap-2', style: { justifyContent: 'flex-end', alignItems: 'center' } }, msg && React.createElement('span', { style: { fontSize: 13, color: msg === 'Tersimpan' ? 'var(--st-online)' : 'var(--st-error)' } }, msg), React.createElement('button', { className: 'btn btn-primary', disabled: busy, onClick: save }, busy ? 'Menyimpan…' : 'Simpan konfigurasi')))); } function SettingsView({ machines, onChange }) { const Y = window.YAPPARI; return React.createElement('div', { className: 'view' }, React.createElement('div', { style: { marginBottom: 'var(--sp-4)' } }, React.createElement('h2', { style: { fontSize: 'var(--fs-lg)', fontWeight: 800, margin: 0 } }, 'Mesin terdaftar'), React.createElement('p', { className: 'muted', style: { margin: '2px 0 0', fontSize: 13 } }, machines.length + ' unit \u00b7 atur nama, lokasi, layout grid, kapasitas spiral & penggabungan slot per mesin')), React.createElement('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(auto-fill,minmax(440px,1fr))', gap: 'var(--sp-4)', marginBottom: 'var(--sp-6)' } }, machines.map(m => React.createElement(MachineSettingCard, { key: m.id, m, onChange }))), React.createElement('div', { className: 'grid-2' }, React.createElement(UsersSection, { machines }), React.createElement(XenditSection, null)), ); } Object.assign(window, { SettingsView, MachineSettingCard, LayoutEditor, Stepper });