Changeset 0.28.7 (#193)
This commit is contained in:
191
src/js/admin-packages.js
Normal file
191
src/js/admin-packages.js
Normal file
@@ -0,0 +1,191 @@
|
||||
// ==========================================
|
||||
// Chat Switchboard — Admin Packages UI
|
||||
// ==========================================
|
||||
// Renders in the "Packages" admin section via ADMIN_LOADERS.
|
||||
// Replaces admin-surfaces.js (v0.28.7).
|
||||
//
|
||||
// Exports: window._loadAdminPackages
|
||||
|
||||
async function _loadAdminPackages() {
|
||||
const container = document.getElementById('adminPackagesContent');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML =
|
||||
'<div style="margin-bottom:16px">' +
|
||||
'<p class="text-muted" style="font-size:12px;margin-bottom:12px">' +
|
||||
'Surfaces, extensions, and full packages. Install .pkg or .surface archives.' +
|
||||
'</p>' +
|
||||
'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-bottom:12px">' +
|
||||
'<button class="btn-small" id="pkgFilterAll">All</button>' +
|
||||
'<button class="btn-small" id="pkgFilterSurface">Surfaces</button>' +
|
||||
'<button class="btn-small" id="pkgFilterExtension">Extensions</button>' +
|
||||
'<button class="btn-small" id="pkgFilterFull">Full</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div id="adminPkgInstallForm" style="display:none;margin-bottom:16px;padding:14px;background:var(--surface);border:1px solid var(--border);border-radius:8px">' +
|
||||
'<h4 style="margin:0 0 8px 0;font-size:13px">Install Package</h4>' +
|
||||
'<input type="file" id="pkgInstallFile" accept=".pkg,.surface,.zip" style="font-size:12px">' +
|
||||
'<div style="display:flex;gap:8px;margin-top:8px">' +
|
||||
'<button class="btn-small btn-primary" id="pkgInstallSubmit">Upload & Install</button>' +
|
||||
'<button class="btn-small" id="pkgInstallCancel">Cancel</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div id="adminPkgList" class="admin-list"><div class="loading">Loading...</div></div>';
|
||||
|
||||
var base = document.body.dataset.basePath || '';
|
||||
var currentFilter = '';
|
||||
|
||||
// Filter buttons
|
||||
['All', 'Surface', 'Extension', 'Full'].forEach(function(label) {
|
||||
var btn = document.getElementById('pkgFilter' + label);
|
||||
if (btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
currentFilter = label === 'All' ? '' : label.toLowerCase();
|
||||
loadList();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Install form
|
||||
var installSubmit = document.getElementById('pkgInstallSubmit');
|
||||
var installCancel = document.getElementById('pkgInstallCancel');
|
||||
if (installSubmit) {
|
||||
installSubmit.addEventListener('click', async function() {
|
||||
var fileInput = document.getElementById('pkgInstallFile');
|
||||
if (!fileInput || !fileInput.files[0]) {
|
||||
UI.toast('Select a .pkg, .surface, or .zip file', 'warning');
|
||||
return;
|
||||
}
|
||||
var fd = new FormData();
|
||||
fd.append('file', fileInput.files[0]);
|
||||
try {
|
||||
var resp = await fetch(base + '/api/v1/admin/packages/install', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + API.accessToken },
|
||||
body: fd,
|
||||
});
|
||||
if (!resp.ok) {
|
||||
var err = await resp.json();
|
||||
UI.toast(err.error || 'Install failed', 'error');
|
||||
return;
|
||||
}
|
||||
var data = await resp.json();
|
||||
UI.toast('Installed: ' + (data.title || data.id), 'success');
|
||||
fileInput.value = '';
|
||||
document.getElementById('adminPkgInstallForm').style.display = 'none';
|
||||
loadList();
|
||||
} catch (e) {
|
||||
UI.toast('Install failed: ' + e.message, 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
if (installCancel) {
|
||||
installCancel.addEventListener('click', function() {
|
||||
document.getElementById('adminPkgInstallForm').style.display = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
async function loadList() {
|
||||
var listEl = document.getElementById('adminPkgList');
|
||||
if (!listEl) return;
|
||||
listEl.innerHTML = '<div class="loading">Loading...</div>';
|
||||
|
||||
try {
|
||||
var url = '/api/v1/admin/packages';
|
||||
if (currentFilter) url += '?type=' + currentFilter;
|
||||
var resp = await API._get(url);
|
||||
var packages = resp.data || [];
|
||||
|
||||
if (packages.length === 0) {
|
||||
listEl.innerHTML = '<div style="padding:12px;font-size:12px;color:var(--text-3);">No packages found</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
var html = '<table class="admin-table" style="width:100%"><thead><tr>' +
|
||||
'<th style="width:30%">Package</th>' +
|
||||
'<th>Type</th>' +
|
||||
'<th>Version</th>' +
|
||||
'<th>Source</th>' +
|
||||
'<th>Status</th>' +
|
||||
'<th style="width:100px">Actions</th>' +
|
||||
'</tr></thead><tbody>';
|
||||
|
||||
packages.forEach(function(pkg) {
|
||||
var typeBadge = {
|
||||
surface: '<span class="badge badge-info">surface</span>',
|
||||
extension: '<span class="badge badge-accent">extension</span>',
|
||||
full: '<span class="badge badge-warning">full</span>',
|
||||
}[pkg.type] || '<span class="badge">' + pkg.type + '</span>';
|
||||
|
||||
var sourceBadge = {
|
||||
core: '<span class="text-muted">core</span>',
|
||||
builtin: '<span class="text-muted">builtin</span>',
|
||||
extension: '<span class="text-muted">installed</span>',
|
||||
}[pkg.source] || pkg.source;
|
||||
|
||||
var statusBadge = pkg.enabled
|
||||
? '<span class="badge badge-success">enabled</span>'
|
||||
: '<span class="badge badge-muted">disabled</span>';
|
||||
|
||||
var actions = '';
|
||||
// Toggle enable/disable (not for chat/admin)
|
||||
if (pkg.id !== 'chat' && pkg.id !== 'admin') {
|
||||
if (pkg.enabled) {
|
||||
actions += '<button class="btn-small btn-ghost pkg-disable" data-id="' + pkg.id + '" title="Disable">Disable</button> ';
|
||||
} else {
|
||||
actions += '<button class="btn-small btn-ghost pkg-enable" data-id="' + pkg.id + '" title="Enable">Enable</button> ';
|
||||
}
|
||||
}
|
||||
// Delete (non-core only)
|
||||
if (pkg.source !== 'core') {
|
||||
actions += '<button class="btn-small btn-danger-ghost pkg-delete" data-id="' + pkg.id + '" title="Delete">Delete</button>';
|
||||
}
|
||||
|
||||
var desc = pkg.description ? '<div class="text-muted" style="font-size:11px">' + pkg.description + '</div>' : '';
|
||||
var system = pkg.is_system ? ' <span class="badge badge-muted" style="font-size:10px">system</span>' : '';
|
||||
|
||||
html += '<tr>' +
|
||||
'<td><strong>' + pkg.title + '</strong>' + system + '<div class="text-muted" style="font-size:11px">' + pkg.id + '</div>' + desc + '</td>' +
|
||||
'<td>' + typeBadge + '</td>' +
|
||||
'<td style="font-size:12px;font-family:monospace">' + (pkg.version || '—') + '</td>' +
|
||||
'<td>' + sourceBadge + '</td>' +
|
||||
'<td>' + statusBadge + '</td>' +
|
||||
'<td>' + actions + '</td>' +
|
||||
'</tr>';
|
||||
});
|
||||
|
||||
html += '</tbody></table>';
|
||||
listEl.innerHTML = html;
|
||||
|
||||
// Wire action buttons
|
||||
listEl.querySelectorAll('.pkg-enable').forEach(function(btn) {
|
||||
btn.addEventListener('click', async function() {
|
||||
await API._put('/api/v1/admin/packages/' + btn.dataset.id + '/enable');
|
||||
UI.toast('Enabled', 'success');
|
||||
loadList();
|
||||
});
|
||||
});
|
||||
listEl.querySelectorAll('.pkg-disable').forEach(function(btn) {
|
||||
btn.addEventListener('click', async function() {
|
||||
await API._put('/api/v1/admin/packages/' + btn.dataset.id + '/disable');
|
||||
UI.toast('Disabled', 'success');
|
||||
loadList();
|
||||
});
|
||||
});
|
||||
listEl.querySelectorAll('.pkg-delete').forEach(function(btn) {
|
||||
btn.addEventListener('click', async function() {
|
||||
if (!confirm('Delete package "' + btn.dataset.id + '"? This cannot be undone.')) return;
|
||||
await API._del('/api/v1/admin/packages/' + btn.dataset.id);
|
||||
UI.toast('Deleted', 'success');
|
||||
loadList();
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
listEl.innerHTML = '<div style="padding:12px;color:var(--danger);">Failed to load packages: ' + e.message + '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
loadList();
|
||||
}
|
||||
|
||||
sb.register('_loadAdminPackages', _loadAdminPackages);
|
||||
@@ -190,30 +190,8 @@
|
||||
|
||||
SCAFFOLDING.storage = '<div id="adminStorageContent"></div>';
|
||||
|
||||
SCAFFOLDING.extensions =
|
||||
'<div id="adminExtensionsList" class="admin-list"></div>' +
|
||||
'<div id="adminInstallExtForm" class="admin-inline-form" style="display:none">' +
|
||||
'<h4>Install Extension</h4>' +
|
||||
'<div class="form-row">' +
|
||||
'<div class="form-group"><label>ID</label><input type="text" id="extInstallId" placeholder="my-extension"></div>' +
|
||||
'<div class="form-group"><label>Name</label><input type="text" id="extInstallName" placeholder="My Extension"></div>' +
|
||||
'<div class="form-group"><label>Version</label><input type="text" id="extInstallVersion" placeholder="1.0.0"></div>' +
|
||||
'</div>' +
|
||||
'<div class="form-row">' +
|
||||
'<div class="form-group"><label>Author</label><input type="text" id="extInstallAuthor" placeholder="Author"></div>' +
|
||||
'<div class="form-group"><label>System</label>' +
|
||||
'<select id="extInstallSystem"><option value="browser">Browser JS</option><option value="starlark">Starlark</option><option value="sidecar">Sidecar</option></select>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="form-group"><label>Description</label><textarea id="extInstallDesc" rows="2" placeholder="What it does..."></textarea></div>' +
|
||||
'<div class="form-group"><label>Manifest JSON</label><textarea id="extInstallManifest" rows="4"></textarea></div>' +
|
||||
'<div class="form-group"><label>Script Source</label><textarea id="extInstallScript" rows="6"></textarea></div>' +
|
||||
'<label class="toggle-label"><input type="checkbox" id="extInstallEnabled" checked><span class="toggle-track"></span><span>Enabled</span></label>' +
|
||||
'<div class="form-row" style="margin-top:8px">' +
|
||||
'<button class="btn-small btn-primary" id="adminInstallExtSubmit">Install</button>' +
|
||||
'<button class="btn-small" id="adminInstallExtCancelBtn">Cancel</button>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
SCAFFOLDING.packages =
|
||||
'<div id="adminPackagesContent"></div>';
|
||||
|
||||
// -- Routing ----------------------------------------------------------
|
||||
|
||||
@@ -268,9 +246,6 @@
|
||||
'</div>' +
|
||||
'<div id="adminArchivedList" class="admin-list"><div class="loading">Loading...</div></div>';
|
||||
|
||||
SCAFFOLDING.surfaces =
|
||||
'<div id="adminSurfacesContent"></div>';
|
||||
|
||||
|
||||
// === Add button actions ==============================================
|
||||
|
||||
@@ -303,8 +278,8 @@
|
||||
var n = document.getElementById('adminGroupName');
|
||||
if (n) n.focus();
|
||||
},
|
||||
extensions: function() {
|
||||
var f = document.getElementById('adminInstallExtForm');
|
||||
packages: function() {
|
||||
var f = document.getElementById('adminPkgInstallForm');
|
||||
if (f) f.style.display = f.style.display === 'none' ? '' : 'none';
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,199 +0,0 @@
|
||||
// ==========================================
|
||||
// Chat Switchboard — Admin Surfaces UI
|
||||
// ==========================================
|
||||
// v0.25.0: Surface lifecycle management.
|
||||
// Renders in the "Surfaces" admin section via ADMIN_LOADERS.
|
||||
//
|
||||
// Exports: window._loadAdminSurfaces
|
||||
|
||||
|
||||
async function _loadAdminSurfaces() {
|
||||
const container = document.getElementById('adminSurfacesContent');
|
||||
if (!container) return;
|
||||
|
||||
container.innerHTML = `
|
||||
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:16px;">
|
||||
<p style="font-size:13px;color:var(--text-2);margin:0;line-height:1.6;flex:1;">
|
||||
Manage which surfaces are available. Disabled surfaces redirect to Chat and are hidden from navigation.
|
||||
</p>
|
||||
<button id="adminSurfaceInstallBtn" class="btn-small" style="margin-left:16px;white-space:nowrap;">
|
||||
+ Install Surface
|
||||
</button>
|
||||
</div>
|
||||
<div id="adminSurfaceInstallForm" style="display:none;margin-bottom:16px;padding:16px;background:var(--bg-secondary);border:1px solid var(--border);border-radius:8px;">
|
||||
<div style="font-size:13px;font-weight:600;margin-bottom:8px;">Install Surface Archive</div>
|
||||
<p style="font-size:12px;color:var(--text-3);margin:0 0 12px;">Upload a <code>.surface</code> or <code>.zip</code> archive containing manifest.json, js/, css/, and assets/.</p>
|
||||
<div style="display:flex;align-items:center;gap:8px;">
|
||||
<input type="file" id="adminSurfaceFile" accept=".surface,.zip" style="font-size:12px;color:var(--text-2);">
|
||||
<button id="adminSurfaceUploadBtn" class="btn-small btn-primary" disabled>Upload</button>
|
||||
<button id="adminSurfaceCancelBtn" class="btn-small">Cancel</button>
|
||||
</div>
|
||||
<div id="adminSurfaceInstallStatus" style="font-size:12px;margin-top:8px;"></div>
|
||||
</div>
|
||||
<div id="adminSurfaceList" class="admin-surface-list">
|
||||
<div style="padding:12px;font-size:12px;color:var(--text-3);">Loading…</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Wire install form
|
||||
var installBtn = document.getElementById('adminSurfaceInstallBtn');
|
||||
var installForm = document.getElementById('adminSurfaceInstallForm');
|
||||
var fileInput = document.getElementById('adminSurfaceFile');
|
||||
var uploadBtn = document.getElementById('adminSurfaceUploadBtn');
|
||||
var cancelBtn = document.getElementById('adminSurfaceCancelBtn');
|
||||
var statusEl = document.getElementById('adminSurfaceInstallStatus');
|
||||
|
||||
if (installBtn) installBtn.addEventListener('click', function() {
|
||||
installForm.style.display = '';
|
||||
installBtn.style.display = 'none';
|
||||
});
|
||||
if (cancelBtn) cancelBtn.addEventListener('click', function() {
|
||||
installForm.style.display = 'none';
|
||||
installBtn.style.display = '';
|
||||
fileInput.value = '';
|
||||
uploadBtn.disabled = true;
|
||||
statusEl.textContent = '';
|
||||
});
|
||||
if (fileInput) fileInput.addEventListener('change', function() {
|
||||
uploadBtn.disabled = !fileInput.files.length;
|
||||
});
|
||||
if (uploadBtn) uploadBtn.addEventListener('click', async function() {
|
||||
var file = fileInput.files[0];
|
||||
if (!file) return;
|
||||
uploadBtn.disabled = true;
|
||||
uploadBtn.textContent = 'Uploading…';
|
||||
statusEl.textContent = '';
|
||||
statusEl.style.color = 'var(--text-3)';
|
||||
|
||||
try {
|
||||
var formData = new FormData();
|
||||
formData.append('file', file);
|
||||
var base = window.__BASE__ || '';
|
||||
var resp = await fetch(base + '/api/v1/admin/surfaces/install', {
|
||||
method: 'POST',
|
||||
headers: API._authHeaders ? (function() { var h = API._authHeaders(); delete h['Content-Type']; return h; })() : {},
|
||||
body: formData,
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (!resp.ok) throw new Error(data.error || 'Upload failed');
|
||||
|
||||
statusEl.style.color = 'var(--success)';
|
||||
statusEl.textContent = 'Installed: ' + (data.title || data.id);
|
||||
if (typeof UI !== 'undefined') UI.toast('Surface installed: ' + (data.title || data.id), 'success');
|
||||
|
||||
// Refresh the list
|
||||
installForm.style.display = 'none';
|
||||
installBtn.style.display = '';
|
||||
fileInput.value = '';
|
||||
_loadSurfaceList();
|
||||
} catch (e) {
|
||||
statusEl.style.color = 'var(--danger)';
|
||||
statusEl.textContent = 'Error: ' + e.message;
|
||||
uploadBtn.disabled = false;
|
||||
uploadBtn.textContent = 'Upload';
|
||||
}
|
||||
});
|
||||
|
||||
_loadSurfaceList();
|
||||
}
|
||||
|
||||
async function _loadSurfaceList() {
|
||||
var listEl = document.getElementById('adminSurfaceList');
|
||||
if (!listEl) return;
|
||||
|
||||
try {
|
||||
var resp = await API._get('/api/v1/admin/surfaces');
|
||||
var surfaces = resp.surfaces || [];
|
||||
|
||||
if (surfaces.length === 0) {
|
||||
listEl.innerHTML = '<div style="padding:12px;font-size:12px;color:var(--text-3);">No surfaces registered</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
listEl.innerHTML = '';
|
||||
surfaces.forEach(function(s) {
|
||||
var row = document.createElement('div');
|
||||
row.className = 'admin-surface-row';
|
||||
row.dataset.surfaceId = s.id;
|
||||
|
||||
var isProtected = s.id === 'chat' || s.id === 'admin';
|
||||
var sourceLabel = s.source === 'core' ? 'Core' : 'Extension';
|
||||
var route = s.manifest && s.manifest.route ? s.manifest.route : '';
|
||||
|
||||
row.innerHTML =
|
||||
'<div class="admin-surface-info">' +
|
||||
'<div class="admin-surface-title">' +
|
||||
'<span class="admin-surface-name">' + esc(s.title) + '</span>' +
|
||||
'<span class="admin-surface-badge admin-surface-badge--' + s.source + '">' + sourceLabel + '</span>' +
|
||||
(isProtected ? '<span class="admin-surface-badge admin-surface-badge--required">Required</span>' : '') +
|
||||
'</div>' +
|
||||
'<div class="admin-surface-meta">' +
|
||||
'<span class="admin-surface-id">' + esc(s.id) + '</span>' +
|
||||
(route ? '<span class="admin-surface-route">' + esc(route) + '</span>' : '') +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div class="admin-surface-actions">' +
|
||||
'<label class="admin-surface-toggle' + (isProtected ? ' admin-surface-toggle--locked' : '') + '">' +
|
||||
'<input type="checkbox"' + (s.enabled ? ' checked' : '') + (isProtected ? ' disabled' : '') +
|
||||
' data-surface-id="' + esc(s.id) + '">' +
|
||||
'<span class="admin-surface-toggle-label">' + (s.enabled ? 'Enabled' : 'Disabled') + '</span>' +
|
||||
'</label>' +
|
||||
(s.source === 'extension' ? '<button class="btn-small btn-danger admin-surface-uninstall" data-surface-id="' + esc(s.id) + '">Uninstall</button>' : '') +
|
||||
'</div>';
|
||||
|
||||
// Toggle handler
|
||||
var checkbox = row.querySelector('input[type="checkbox"]');
|
||||
if (checkbox && !isProtected) {
|
||||
checkbox.addEventListener('change', (function(surface) {
|
||||
return async function() {
|
||||
var id = this.dataset.surfaceId;
|
||||
var action = this.checked ? 'enable' : 'disable';
|
||||
var label = row.querySelector('.admin-surface-toggle-label');
|
||||
var cb = this;
|
||||
|
||||
try {
|
||||
await API._put('/api/v1/admin/surfaces/' + id + '/' + action);
|
||||
if (label) label.textContent = cb.checked ? 'Enabled' : 'Disabled';
|
||||
if (typeof UI !== 'undefined') {
|
||||
UI.toast(surface.title + ' ' + (cb.checked ? 'enabled' : 'disabled') + ' — takes effect on next page load', 'success');
|
||||
}
|
||||
} catch (e) {
|
||||
cb.checked = !cb.checked;
|
||||
if (label) label.textContent = cb.checked ? 'Enabled' : 'Disabled';
|
||||
if (typeof UI !== 'undefined') UI.toast('Failed: ' + e.message, 'error');
|
||||
}
|
||||
};
|
||||
})(s));
|
||||
}
|
||||
|
||||
// Uninstall handler
|
||||
var uninstallBtn = row.querySelector('.admin-surface-uninstall');
|
||||
if (uninstallBtn) {
|
||||
uninstallBtn.addEventListener('click', (function(surface) {
|
||||
return async function() {
|
||||
var ok = typeof showConfirm === 'function'
|
||||
? await showConfirm('Uninstall ' + surface.title + '? This removes all files.')
|
||||
: window.confirm('Uninstall ' + surface.title + '?');
|
||||
if (!ok) return;
|
||||
|
||||
try {
|
||||
await API._del('/api/v1/admin/surfaces/' + surface.id);
|
||||
row.remove();
|
||||
if (typeof UI !== 'undefined') UI.toast(surface.title + ' uninstalled', 'success');
|
||||
} catch (e) {
|
||||
if (typeof UI !== 'undefined') UI.toast('Failed: ' + e.message, 'error');
|
||||
}
|
||||
};
|
||||
})(s));
|
||||
}
|
||||
|
||||
listEl.appendChild(row);
|
||||
});
|
||||
} catch (e) {
|
||||
listEl.innerHTML = '<div style="padding:12px;font-size:12px;color:var(--danger);">Failed to load: ' + esc(e.message) + '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ── Exports ─────────────────────────────────
|
||||
sb.register('_loadAdminSurfaces', _loadAdminSurfaces);
|
||||
@@ -11,7 +11,7 @@ const ADMIN_SECTIONS = {
|
||||
ai: ['providers', 'models', 'personas', 'roles', 'knowledgeBases', 'memory'],
|
||||
workflows: ['workflows', 'tasks'],
|
||||
routing: ['health', 'routing', 'capabilities'],
|
||||
system: ['settings', 'storage', 'extensions', 'channels', 'surfaces', 'broadcast'],
|
||||
system: ['settings', 'storage', 'packages', 'channels', 'broadcast'],
|
||||
monitoring: ['usage', 'audit', 'stats'],
|
||||
};
|
||||
|
||||
@@ -20,7 +20,7 @@ const ADMIN_LABELS = {
|
||||
providers: 'Providers', models: 'Models', personas: 'Personas', roles: 'Roles', knowledgeBases: 'Knowledge', memory: 'Memory',
|
||||
health: 'Health', routing: 'Routing', capabilities: 'Capabilities',
|
||||
workflows: 'Workflows', tasks: 'Tasks',
|
||||
settings: 'Settings', storage: 'Storage', extensions: 'Extensions', channels: 'Channels', surfaces: 'Surfaces', broadcast: 'Broadcast',
|
||||
settings: 'Settings', storage: 'Storage', packages: 'Packages', channels: 'Channels', broadcast: 'Broadcast',
|
||||
usage: 'Usage', audit: 'Audit', stats: 'Stats',
|
||||
};
|
||||
|
||||
@@ -43,8 +43,7 @@ const ADMIN_LOADERS = {
|
||||
},
|
||||
settings: () => UI.loadAdminSettings(),
|
||||
storage: () => typeof loadAdminStorage === 'function' ? loadAdminStorage() : null,
|
||||
extensions: () => UI.loadAdminExtensions(),
|
||||
surfaces: () => typeof _loadAdminSurfaces === 'function' ? _loadAdminSurfaces() : null,
|
||||
packages: () => typeof _loadAdminPackages === 'function' ? _loadAdminPackages() : null,
|
||||
health: () => UI.loadAdminHealth(),
|
||||
routing: () => UI.loadAdminRouting(),
|
||||
capabilities: () => UI.loadAdminCapabilities(),
|
||||
|
||||
Reference in New Issue
Block a user