// ==========================================
// Chat Switchboard — Admin Surfaces UI
// ==========================================
// v0.25.0: Surface lifecycle management.
// Renders in the "Surfaces" admin section via ADMIN_LOADERS.
//
// Exports: window._loadAdminSurfaces
(function() {
'use strict';
async function _loadAdminSurfaces() {
const container = document.getElementById('adminSurfacesContent');
if (!container) return;
container.innerHTML = `
Manage which surfaces are available. Disabled surfaces redirect to Chat and are hidden from navigation.
`;
// 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 = 'No surfaces registered
';
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 =
'' +
'
' +
'' + esc(s.title) + '' +
'' + sourceLabel + '' +
(isProtected ? 'Required' : '') +
'
' +
'
' +
'' + esc(s.id) + '' +
(route ? '' + esc(route) + '' : '') +
'
' +
'
' +
'' +
'' +
(s.source === 'extension' ? '' : '') +
'
';
// 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 = 'Failed to load: ' + esc(e.message) + '
';
}
}
// ── Exports ─────────────────────────────────
window._loadAdminSurfaces = _loadAdminSurfaces;
})();