API-Keys UI: Toggle+Check neben Badge, übernommene Keys ausblenden, Layout-Polish

- Toggle-Button (Aktivieren/Deaktivieren) links neben Usage-Badge
- Check-Button rechts neben Usage-Badge statt bei Actions
- '✅ Aktiv' aus Meta-Zeile entfernt (Toggle zeigt Status)
- 'Prüfe...' Text beim Check entfernt (Spinner im Button reicht)
- Übernommene Scanned-Keys komplett ausgeblendet statt halbtransparent
- isAdopted-Logik und Wrapper entfernt, Code entschachtelt
- admin-user-row: Border, Radius, Background hinzugefügt
- Aktive Keys visuell hervorgehoben (Primary-Border + Brightness)
- placeholder.css an gleichen Row-Style angeglichen
This commit is contained in:
Meleki committed 2026-03-12 19:19:35 +01:00
1 parent de388806cb
commit bdcfd1f8b3
4 files changed
+93 -75

No files matched your search

+13
View File
@@ -93,6 +93,19 @@ button.icon-btn:hover {
border-color: transparent; border-color: transparent;
} }
button.icon-btn[title="Deaktivieren"],
button.icon-btn[data-tooltip="Deaktivieren"] {
border-color: var(--primary);
background: var(--primary);
color: var(--f1-bg);
}
.admin-user-row:has(button.icon-btn[title="Deaktivieren"]),
.admin-user-row:has(button.icon-btn[data-tooltip="Deaktivieren"]) {
border-color: var(--primary);
filter: brightness(1.1);
}
textarea, code, pre, .tool-input, .tool-diff, .tool-result { textarea, code, pre, .tool-input, .tool-diff, .tool-result {
font-family: var(--mono); font-family: var(--mono);
font-size: var(--mono-size); font-size: var(--mono-size);
+4
View File
@@ -572,6 +572,7 @@ label.skill-item {
.admin-user-list { .admin-user-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--badge-spacing);
} }
.admin-user-row { .admin-user-row {
@@ -579,6 +580,9 @@ label.skill-item {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: var(--box-padding); padding: var(--box-padding);
border: 1px solid var(--f1-border);
border-radius: var(--border-radius);
background: color-mix(in srgb, var(--f1-bg) 25%, transparent);
} }
.admin-user-info { .admin-user-info {
+2 -2
View File
@@ -25,9 +25,9 @@
justify-content: space-between; justify-content: space-between;
gap: var(--box-spacing); gap: var(--box-spacing);
padding: var(--box-padding); padding: var(--box-padding);
background: var(--f1-bg); border: 1px solid var(--f1-border);
border: var(--border-width) solid var(--f1-border);
border-radius: var(--border-radius); border-radius: var(--border-radius);
background: color-mix(in srgb, var(--f1-bg) 25%, transparent);
} }
.setting-info { .setting-info {
+74 -73
View File
@@ -98,7 +98,6 @@ registerPage('api-keys', {
var metaSpan = document.createElement('span'); var metaSpan = document.createElement('span');
metaSpan.className = 'admin-user-meta'; metaSpan.className = 'admin-user-meta';
var parts = [key.provider]; var parts = [key.provider];
if (key.isActive) parts.push('✅ Aktiv');
parts.push(formatKeyStatus(key.status)); parts.push(formatKeyStatus(key.status));
if (key.plan) parts.push(key.plan); if (key.plan) parts.push(key.plan);
if (key.checkedAt) parts.push(new Date(key.checkedAt).toLocaleString()); if (key.checkedAt) parts.push(new Date(key.checkedAt).toLocaleString());
@@ -115,27 +114,50 @@ registerPage('api-keys', {
info.appendChild(urlSpan); info.appendChild(urlSpan);
} }
info.appendChild(metaSpan); info.appendChild(metaSpan);
var usageBadge = createUsageBadge(key.usage5h, key.usage7d);
if (usageBadge) info.appendChild(usageBadge);
var actions = document.createElement('div'); // Toggle + Usage-Badge nebeneinander
actions.className = 'admin-user-actions'; var badgeRow = document.createElement('div');
badgeRow.style.cssText = 'display:flex;align-items:center;gap:8px;margin-top:2px';
if (key.isActive) {
var deactivateBtn = document.createElement('button');
deactivateBtn.className = 'icon-btn';
deactivateBtn.title = 'Deaktivieren';
deactivateBtn.innerHTML = '<i class="fa fa-toggle-on" style="color:var(--accent)"></i>';
deactivateBtn.addEventListener('click', function() { deactivateKey(key); });
badgeRow.appendChild(deactivateBtn);
} else {
var activateBtn = document.createElement('button');
activateBtn.className = 'icon-btn';
activateBtn.title = 'Aktivieren';
activateBtn.innerHTML = '<i class="fa fa-toggle-off"></i>';
activateBtn.addEventListener('click', function() { activateKey(key); });
badgeRow.appendChild(activateBtn);
}
var usageBadge = createUsageBadge(key.usage5h, key.usage7d);
if (usageBadge) badgeRow.appendChild(usageBadge);
// Status-Zeile (für Check-Ergebnis) // Status-Zeile (für Check-Ergebnis)
var statusLine = document.createElement('div'); var statusLine = document.createElement('div');
statusLine.className = 'admin-user-meta'; statusLine.className = 'admin-user-meta';
statusLine.style.cssText = 'padding:0 var(--box-padding) .5rem;display:none;font-size:.85em'; statusLine.style.cssText = 'padding:0 var(--box-padding) .5rem;display:none;font-size:.85em';
// Check (nur Anthropic) // Check (nur Anthropic) – rechts neben Usage-Badge
if (key.provider === 'anthropic') { if (key.provider === 'anthropic') {
var checkBtn = document.createElement('button'); var checkBtn = document.createElement('button');
checkBtn.className = 'icon-btn'; checkBtn.className = 'icon-btn';
checkBtn.title = 'Key prüfen'; checkBtn.title = 'Key prüfen';
checkBtn.innerHTML = '<i class="fa fa-refresh"></i>'; checkBtn.innerHTML = '<i class="fa fa-refresh"></i>';
checkBtn.addEventListener('click', function() { checkKey(key, checkBtn, statusLine); }); checkBtn.addEventListener('click', function() { checkKey(key, checkBtn, statusLine); });
actions.appendChild(checkBtn); badgeRow.appendChild(checkBtn);
} }
info.appendChild(badgeRow);
var actions = document.createElement('div');
actions.className = 'admin-user-actions';
// Bearbeiten // Bearbeiten
var editBtn = document.createElement('button'); var editBtn = document.createElement('button');
editBtn.className = 'icon-btn'; editBtn.className = 'icon-btn';
@@ -144,23 +166,6 @@ registerPage('api-keys', {
editBtn.addEventListener('click', function() { startEdit(key); }); editBtn.addEventListener('click', function() { startEdit(key); });
actions.appendChild(editBtn); actions.appendChild(editBtn);
// Aktivieren/Deaktivieren
if (key.isActive) {
var deactivateBtn = document.createElement('button');
deactivateBtn.className = 'icon-btn';
deactivateBtn.title = 'Deaktivieren';
deactivateBtn.innerHTML = '<i class="fa fa-toggle-on" style="color:var(--accent)"></i>';
deactivateBtn.addEventListener('click', function() { deactivateKey(key); });
actions.appendChild(deactivateBtn);
} else {
var activateBtn = document.createElement('button');
activateBtn.className = 'icon-btn';
activateBtn.title = 'Aktivieren';
activateBtn.innerHTML = '<i class="fa fa-toggle-off"></i>';
activateBtn.addEventListener('click', function() { activateKey(key); });
actions.appendChild(activateBtn);
}
// Löschen // Löschen
var deleteBtn = document.createElement('button'); var deleteBtn = document.createElement('button');
deleteBtn.className = 'icon-btn'; deleteBtn.className = 'icon-btn';
@@ -190,9 +195,8 @@ registerPage('api-keys', {
var origHtml = btn.innerHTML; var origHtml = btn.innerHTML;
btn.innerHTML = '<i class="fa fa-spinner fa-spin"></i>'; btn.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';
btn.disabled = true; btn.disabled = true;
statusLine.style.display = 'block'; statusLine.style.display = 'none';
statusLine.textContent = 'Prüfe…'; statusLine.innerHTML = '';
statusLine.style.opacity = '0.5';
try { try {
var result = await apiCheckApiKey(key.keyId); var result = await apiCheckApiKey(key.keyId);
statusLine.style.opacity = '1'; statusLine.style.opacity = '1';
@@ -322,7 +326,8 @@ registerPage('api-keys', {
function renderScannedKeys() { function renderScannedKeys() {
scannedList.innerHTML = ''; scannedList.innerHTML = '';
if (scannedKeys.length === 0) { var visibleKeys = scannedKeys.filter(function(sk) { return sk.adoptedApiKeyId == null; });
if (visibleKeys.length === 0) {
var empty = document.createElement('div'); var empty = document.createElement('div');
empty.className = 'admin-user-meta'; empty.className = 'admin-user-meta';
empty.style.padding = '1rem 0'; empty.style.padding = '1rem 0';
@@ -331,12 +336,10 @@ registerPage('api-keys', {
return; return;
} }
scannedKeys.forEach(function(sk) { visibleKeys.forEach(function(sk) {
var isAdopted = sk.adoptedApiKeyId != null;
var row = document.createElement('div'); var row = document.createElement('div');
row.className = 'admin-user-row'; row.className = 'admin-user-row';
if (isAdopted) row.style.opacity = '0.5';
var info = document.createElement('div'); var info = document.createElement('div');
info.className = 'admin-user-info'; info.className = 'admin-user-info';
@@ -354,7 +357,7 @@ registerPage('api-keys', {
var metaSpan = document.createElement('span'); var metaSpan = document.createElement('span');
metaSpan.className = 'admin-user-meta'; metaSpan.className = 'admin-user-meta';
var metaParts = isAdopted ? ['📥 Übernommen'] : []; var metaParts = [];
metaParts.push(formatKeyStatus(sk.status)); metaParts.push(formatKeyStatus(sk.status));
if (sk.plan && sk.plan !== 'Unknown') metaParts.push(sk.plan); if (sk.plan && sk.plan !== 'Unknown') metaParts.push(sk.plan);
if (sk.checkedAt) metaParts.push(new Date(sk.checkedAt).toLocaleString()); if (sk.checkedAt) metaParts.push(new Date(sk.checkedAt).toLocaleString());
@@ -367,54 +370,52 @@ registerPage('api-keys', {
var actions = document.createElement('div'); var actions = document.createElement('div');
actions.className = 'admin-user-actions'; actions.className = 'admin-user-actions';
if (!isAdopted) { // Recheck
// Recheck var recheckBtn = document.createElement('button');
var recheckBtn = document.createElement('button'); recheckBtn.className = 'icon-btn';
recheckBtn.className = 'icon-btn'; recheckBtn.title = 'Erneut prüfen';
recheckBtn.title = 'Erneut prüfen'; recheckBtn.innerHTML = '<i class="fa fa-refresh"></i>';
recheckBtn.innerHTML = '<i class="fa fa-refresh"></i>'; recheckBtn.addEventListener('click', async function() {
recheckBtn.addEventListener('click', async function() { recheckBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i>';
recheckBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i>'; recheckBtn.disabled = true;
recheckBtn.disabled = true; try {
try { await apiRecheckScannedKey(sk.scannedKeyId);
await apiRecheckScannedKey(sk.scannedKeyId); await loadScannedKeys();
await loadScannedKeys(); } catch (err) { showToast(err.message, 'error'); }
} catch (err) { showToast(err.message, 'error'); } finally { recheckBtn.innerHTML = '<i class="fa fa-refresh"></i>'; recheckBtn.disabled = false; }
finally { recheckBtn.innerHTML = '<i class="fa fa-refresh"></i>'; recheckBtn.disabled = false; } });
}); actions.appendChild(recheckBtn);
actions.appendChild(recheckBtn);
// Übernehmen (bei valid oder quota_exceeded) // Übernehmen (bei valid oder quota_exceeded)
if (sk.status === 'valid' || sk.status === 'quota_exceeded') { if (sk.status === 'valid' || sk.status === 'quota_exceeded') {
var adoptBtn = document.createElement('button'); var adoptBtn = document.createElement('button');
adoptBtn.className = 'icon-btn'; adoptBtn.className = 'icon-btn';
adoptBtn.title = 'Als API-Key übernehmen'; adoptBtn.title = 'Als API-Key übernehmen';
adoptBtn.innerHTML = '<i class="fa fa-plus-circle" style="color:var(--accent)"></i>'; adoptBtn.innerHTML = '<i class="fa fa-plus-circle" style="color:var(--accent)"></i>';
adoptBtn.addEventListener('click', async function() { adoptBtn.addEventListener('click', async function() {
try {
await apiAdoptScannedKey(sk.scannedKeyId);
showToast('Key übernommen');
await loadScannedKeys();
await loadKeys();
} catch (err) { showToast(err.message, 'error'); }
});
actions.appendChild(adoptBtn);
}
// Löschen
var delBtn = document.createElement('button');
delBtn.className = 'icon-btn';
delBtn.title = 'Ignorieren';
delBtn.innerHTML = '<i class="fa fa-trash"></i>';
delBtn.addEventListener('click', async function() {
try { try {
await apiDeleteScannedKey(sk.scannedKeyId); await apiAdoptScannedKey(sk.scannedKeyId);
showToast('Key übernommen');
await loadScannedKeys(); await loadScannedKeys();
await loadKeys();
} catch (err) { showToast(err.message, 'error'); } } catch (err) { showToast(err.message, 'error'); }
}); });
actions.appendChild(delBtn); actions.appendChild(adoptBtn);
} }
// Löschen
var delBtn = document.createElement('button');
delBtn.className = 'icon-btn';
delBtn.title = 'Ignorieren';
delBtn.innerHTML = '<i class="fa fa-trash"></i>';
delBtn.addEventListener('click', async function() {
try {
await apiDeleteScannedKey(sk.scannedKeyId);
await loadScannedKeys();
} catch (err) { showToast(err.message, 'error'); }
});
actions.appendChild(delBtn);
row.appendChild(info); row.appendChild(info);
row.appendChild(actions); row.appendChild(actions);
scannedList.appendChild(row); scannedList.appendChild(row);