Remove admin model management panel — models use global selector instead

The admin model management (enable/disable, custom models, default override)
had persistent rendering issues. Removed the UI panel — models are managed
via the global model selector in the header, which works reliably. Backend
API endpoints for model config are retained for future use.
This commit is contained in:
ifedan-ed 2026-03-29 02:30:29 +00:00
parent e58876ddd9
commit 56d99e67b3
2 changed files with 1 additions and 240 deletions

View file

@ -202,63 +202,7 @@
</div>
</div>
<!-- ── CMS: AI Model Management ─────────────────────────────── -->
<div class="card">
<div class="card-header">
<h3><i class="fas fa-microchip"></i> AI Models</h3>
<span id="admin-model-provider" style="font-size:11px;padding:2px 8px;border-radius:10px;background:var(--blue-light);color:var(--blue);font-weight:600;">Loading...</span>
</div>
<div style="padding:16px;display:flex;flex-direction:column;gap:16px;">
<!-- Default model override -->
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
<label style="font-size:13px;font-weight:600;min-width:120px;">Default Model:</label>
<select id="admin-default-model" style="font-size:13px;padding:6px 10px;border:1px solid var(--g300);border-radius:6px;flex:1;max-width:400px;"></select>
<button id="btn-save-default-model" class="btn-sm btn-primary">Save</button>
</div>
<!-- Built-in models table -->
<div>
<label style="font-size:13px;font-weight:600;display:block;margin-bottom:8px;">Built-in Models <span style="font-weight:400;color:var(--g500);">(toggle to show/hide from users)</span></label>
<div style="overflow-x:auto;">
<table class="admin-table" id="admin-models-table">
<thead>
<tr>
<th>Model</th>
<th>Category</th>
<th>Cost</th>
<th>Tag</th>
<th style="text-align:center;">Enabled</th>
</tr>
</thead>
<tbody id="admin-models-body">
<tr><td colspan="5" style="text-align:center;color:var(--g400);padding:20px;">Loading...</td></tr>
</tbody>
</table>
</div>
</div>
<!-- Custom models -->
<div>
<label style="font-size:13px;font-weight:600;display:block;margin-bottom:8px;">Custom Models <span style="font-weight:400;color:var(--g500);">(add any OpenRouter/Bedrock model ID)</span></label>
<div id="admin-custom-models-list" style="margin-bottom:12px;"></div>
<div style="display:grid;grid-template-columns:1fr 1fr auto auto;gap:8px;align-items:end;">
<div>
<label style="font-size:11px;font-weight:600;color:var(--g500);display:block;margin-bottom:2px;">Model ID</label>
<input type="text" id="admin-custom-id" style="width:100%;font-size:12px;padding:6px 8px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="e.g. openai/gpt-4o">
</div>
<div>
<label style="font-size:11px;font-weight:600;color:var(--g500);display:block;margin-bottom:2px;">Display Name</label>
<input type="text" id="admin-custom-name" style="width:100%;font-size:12px;padding:6px 8px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="e.g. GPT-4o">
</div>
<div>
<label style="font-size:11px;font-weight:600;color:var(--g500);display:block;margin-bottom:2px;">Cost</label>
<input type="text" id="admin-custom-cost" style="width:80px;font-size:12px;padding:6px 8px;border:1px solid var(--g300);border-radius:6px;box-sizing:border-box;" placeholder="~$0.01">
</div>
<button id="btn-add-custom-model" class="btn-sm btn-primary" style="margin-bottom:1px;"><i class="fas fa-plus"></i> Add</button>
</div>
</div>
</div>
</div>
<!-- AI Models: configured via global model selector in header (powered by /api/models) -->
<!-- ── CMS: Reset to Defaults ─────────────────────────────────── -->
<div class="card" style="border:1px solid var(--red-light);">

View file

@ -279,8 +279,6 @@
if (e.target.closest('#btn-clear-smtp')) clearSmtp();
if (e.target.closest('#btn-save-auto-delete')) saveAutoDelete();
if (e.target.closest('#btn-reset-all-defaults')) resetAllDefaults();
if (e.target.closest('#btn-add-custom-model')) addCustomModel();
if (e.target.closest('#btn-save-default-model')) saveDefaultModel();
});
// When email template selector changes, repopulate fields
@ -328,7 +326,6 @@
loadPromptList();
loadSmtp();
loadModels();
}
// ---- ANNOUNCEMENT ----
@ -560,186 +557,6 @@
.catch(function() { showToast('Request failed', 'error'); });
}
// ---- AI MODEL MANAGEMENT ----
var modelsLoaded = false;
function loadModels() {
if (modelsLoaded) return;
modelsLoaded = true;
fetch('/api/admin/config/models', { headers: getAuthHeaders() })
.then(function(r) { return r.json(); })
.then(function(data) {
if (!data.success) { modelsLoaded = false; return; }
window._adminModelsData = data;
var providerEl = document.getElementById('admin-model-provider');
if (providerEl) providerEl.textContent = (data.provider || 'openrouter').toUpperCase();
renderModelTable(data.models || []);
renderCustomModels(data.custom || []);
populateDefaultModelSelect(data.models || [], data.custom || []);
})
.catch(function(err) { modelsLoaded = false; console.error('[Admin] Models load failed:', err); });
}
function renderModelTable(models) {
var tbody = document.getElementById('admin-models-body');
if (!tbody) return;
if (models.length === 0) {
tbody.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--g400);padding:20px;">No models</td></tr>';
return;
}
var catColors = { free: 'var(--green)', fast: 'var(--blue)', smart: 'var(--purple)', premium: 'var(--amber)' };
tbody.innerHTML = models.map(function(m) {
var catColor = catColors[m.category] || 'var(--g500)';
var checked = m.enabled ? 'checked' : '';
return '<tr>' +
'<td style="font-size:13px;"><div style="font-weight:600;">' + esc(m.name) + '</div><div style="font-size:11px;color:var(--g400);font-family:monospace;">' + esc(m.id) + '</div></td>' +
'<td><span style="font-size:12px;color:' + catColor + ';font-weight:600;text-transform:uppercase;">' + esc(m.category) + '</span></td>' +
'<td style="font-size:12px;color:var(--g600);">' + esc(m.cost || '?') + '</td>' +
'<td><span style="font-size:11px;padding:1px 6px;border-radius:4px;background:var(--g100);color:var(--g600);">' + esc(m.tag || '') + '</span></td>' +
'<td style="text-align:center;"><label class="toggle-switch"><input type="checkbox" class="model-toggle" data-model-id="' + esc(m.id) + '" ' + checked + '><span class="toggle-slider"></span></label></td>' +
'</tr>';
}).join('');
// Bind toggle events
tbody.querySelectorAll('.model-toggle').forEach(function(cb) {
cb.addEventListener('change', function() {
var modelId = cb.dataset.modelId;
var enabled = cb.checked;
toggleModel(modelId, enabled);
});
});
}
function toggleModel(modelId, enabled) {
fetch('/api/admin/config/models/toggle', {
method: 'PUT',
headers: getAuthHeaders(),
body: JSON.stringify({ modelId: modelId, enabled: enabled })
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success) {
showToast((enabled ? 'Enabled' : 'Disabled') + ': ' + modelId, 'success');
} else {
showToast(data.error || 'Toggle failed', 'error');
}
})
.catch(function() { showToast('Request failed', 'error'); });
}
function renderCustomModels(custom) {
var container = document.getElementById('admin-custom-models-list');
if (!container) return;
if (custom.length === 0) {
container.innerHTML = '<div style="font-size:12px;color:var(--g400);padding:4px 0;">No custom models added</div>';
return;
}
container.innerHTML = custom.map(function(m) {
return '<div style="display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--g50);border-radius:6px;margin-bottom:4px;">' +
'<span style="font-size:13px;font-weight:600;flex:1;">' + esc(m.name) + '</span>' +
'<span style="font-size:11px;color:var(--g500);font-family:monospace;">' + esc(m.id) + '</span>' +
'<span style="font-size:11px;color:var(--g500);">' + esc(m.cost || '?') + '</span>' +
'<button class="btn-sm" style="background:var(--red-light);color:var(--red);border:none;border-radius:4px;padding:2px 8px;font-size:11px;cursor:pointer;" data-delete-model="' + esc(m.id) + '"><i class="fas fa-trash"></i></button>' +
'</div>';
}).join('');
container.querySelectorAll('[data-delete-model]').forEach(function(btn) {
btn.addEventListener('click', function() {
var id = btn.dataset.deleteModel;
if (!confirm('Remove custom model "' + id + '"?')) return;
deleteCustomModel(id);
});
});
}
function populateDefaultModelSelect(models, custom) {
var sel = document.getElementById('admin-default-model');
if (!sel) return;
sel.innerHTML = '<option value="">(Provider default)</option>';
var all = models.filter(function(m) { return m.enabled; }).concat(custom || []);
all.forEach(function(m) {
var opt = document.createElement('option');
opt.value = m.id;
opt.textContent = m.name + ' (' + (m.cost || '?') + ')';
sel.appendChild(opt);
});
// Load current default from already-loaded config, or fetch if needed
var cfg = window._adminEmailConfig || {};
if (cfg['models.default']) {
sel.value = cfg['models.default'];
} else {
// Fetch only the default model setting
fetch('/api/admin/config', { headers: getAuthHeaders() })
.then(function(r) { return r.json(); })
.then(function(data) {
if (!data.success) return;
(data.config || []).forEach(function(row) {
if (row.key === 'models.default' && row.value) sel.value = row.value;
});
})
.catch(function() {});
}
}
function addCustomModel() {
var id = document.getElementById('admin-custom-id').value.trim();
var name = document.getElementById('admin-custom-name').value.trim();
var cost = document.getElementById('admin-custom-cost').value.trim();
if (!id || !name) { showToast('Model ID and name required', 'error'); return; }
fetch('/api/admin/config/models/custom', {
method: 'POST',
headers: getAuthHeaders(),
body: JSON.stringify({ id: id, name: name, cost: cost || '?', category: 'smart' })
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success) {
showToast('Custom model added: ' + name, 'success');
document.getElementById('admin-custom-id').value = '';
document.getElementById('admin-custom-name').value = '';
document.getElementById('admin-custom-cost').value = '';
modelsLoaded = false;
loadModels();
} else {
showToast(data.error || 'Add failed', 'error');
}
})
.catch(function() { showToast('Request failed', 'error'); });
}
function deleteCustomModel(modelId) {
fetch('/api/admin/config/models/custom/' + encodeURIComponent(modelId), {
method: 'DELETE',
headers: getAuthHeaders()
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success) {
showToast('Model removed', 'info');
modelsLoaded = false;
loadModels();
} else {
showToast(data.error || 'Delete failed', 'error');
}
})
.catch(function() { showToast('Request failed', 'error'); });
}
function saveDefaultModel() {
var sel = document.getElementById('admin-default-model');
if (!sel) return;
var val = sel.value;
putConfig('models.default', val)
.then(function() { showToast('Default model saved' + (val ? ': ' + val : ' (provider default)'), 'success'); })
.catch(function() { showToast('Save failed', 'error'); });
}
// ---- HELPERS ----
function putConfig(key, value) {