feat: API-Keys in Einstellungen gruppiert nach Kunde
This commit is contained in:
parent
cdb2443d8f
commit
56cb19d052
@ -347,16 +347,42 @@ function APIKeysSection() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Key Liste */}
|
||||
{/* Key Liste — gruppiert nach Kunde */}
|
||||
{loading ? (
|
||||
<div className="px-4 py-3 text-gray-500 text-xs">Laden...</div>
|
||||
) : keys.length === 0 ? (
|
||||
<div className="px-4 py-4 text-center text-gray-500 text-xs">Keine API-Keys vorhanden</div>
|
||||
) : (
|
||||
<div className="divide-y divide-gray-800/60">
|
||||
{keys.map((k) => {
|
||||
) : (() => {
|
||||
// Gruppen bauen: null-Kunde zuerst, dann nach Kundennummer sortiert
|
||||
const groups = {}
|
||||
keys.forEach(k => {
|
||||
const gid = k.customer_id ?? '__global__'
|
||||
if (!groups[gid]) groups[gid] = []
|
||||
groups[gid].push(k)
|
||||
})
|
||||
const sorted = Object.entries(groups).sort(([a], [b]) => {
|
||||
if (a === '__global__') return -1
|
||||
if (b === '__global__') return 1
|
||||
const ca = customers.find(c => c.id === parseInt(a))
|
||||
const cb = customers.find(c => c.id === parseInt(b))
|
||||
return (ca?.number || '').localeCompare(cb?.number || '')
|
||||
})
|
||||
return (
|
||||
<div className="divide-y divide-gray-800">
|
||||
{sorted.map(([gid, gkeys]) => {
|
||||
const customer = gid !== '__global__' ? customers.find(c => c.id === parseInt(gid)) : null
|
||||
return (
|
||||
<div key={gid}>
|
||||
{/* Gruppen-Header */}
|
||||
<div className="px-4 py-1.5 bg-gray-800/40 flex items-center gap-2">
|
||||
<span className="text-[10px] font-semibold text-gray-500 uppercase tracking-wide">
|
||||
{customer ? `${customer.number} — ${customer.name}` : 'Global'}
|
||||
</span>
|
||||
<span className="text-[10px] text-gray-700">({gkeys.length})</span>
|
||||
</div>
|
||||
{/* Keys der Gruppe */}
|
||||
{gkeys.map(k => {
|
||||
const perm = PERM_LABELS[k.permissions] || PERM_LABELS.read
|
||||
const customer = k.customer_id ? customers.find(c => c.id === k.customer_id) : null
|
||||
const lastUsedAgo = k.last_used
|
||||
? (() => {
|
||||
const s = (Date.now() - new Date(k.last_used)) / 1000
|
||||
@ -372,36 +398,35 @@ function APIKeysSection() {
|
||||
{perm.label}
|
||||
</span>
|
||||
<span className="text-xs text-gray-300 flex-1 min-w-0 truncate">{k.name}</span>
|
||||
{customer && (
|
||||
<span className="text-[10px] text-gray-600 flex-shrink-0 hidden sm:block">{customer.number}</span>
|
||||
)}
|
||||
<code className="text-xs text-gray-600 font-mono flex-shrink-0">
|
||||
{k.key.substring(0, 8)}…{k.key.substring(k.key.length - 4)}
|
||||
</code>
|
||||
{lastUsedAgo && (
|
||||
<span className="text-[10px] text-gray-700 flex-shrink-0 hidden md:block" title={`Zuletzt benutzt: ${new Date(k.last_used).toLocaleString('de-DE')}`}>
|
||||
<span className="text-[10px] text-gray-700 flex-shrink-0 hidden md:block"
|
||||
title={`Zuletzt: ${new Date(k.last_used).toLocaleString('de-DE')}`}>
|
||||
{lastUsedAgo}
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
onClick={() => copyText(k.key, k.id)}
|
||||
<button onClick={() => copyText(k.key, k.id)}
|
||||
className="text-gray-600 hover:text-orange-400 transition-colors flex-shrink-0 opacity-0 group-hover:opacity-100"
|
||||
title="Key kopieren"
|
||||
>
|
||||
title="Key kopieren">
|
||||
{copied === k.id ? <Check className="w-3.5 h-3.5 text-green-400" /> : <Copy className="w-3.5 h-3.5" />}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(k.id, k.name)}
|
||||
<button onClick={() => handleDelete(k.id, k.name)}
|
||||
className="text-gray-700 hover:text-red-400 transition-colors flex-shrink-0 opacity-0 group-hover:opacity-100"
|
||||
title="Key loeschen"
|
||||
>
|
||||
title="Key loeschen">
|
||||
<Trash2 className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
})()
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user