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:
parent
e58876ddd9
commit
56d99e67b3
2 changed files with 1 additions and 240 deletions
|
|
@ -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);">
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue