feat: Kunden-Panel zeigt Agents + API-Keys nebeneinander

This commit is contained in:
cynfo3000 2026-03-10 00:20:56 +01:00
parent ebd8f1ebb8
commit 4a79305eee

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import api from '../api/client' import api from '../api/client'
import { copyToClipboard } from '../utils/clipboard' import { copyToClipboard } from '../utils/clipboard'
import { Plus, Trash2, Edit2, X, Check, ChevronDown, ChevronRight, Copy, Key } from 'lucide-react' import { Plus, Trash2, Edit2, X, Check, ChevronDown, ChevronRight, Copy, Key, Monitor } from 'lucide-react'
const PERM_LABELS = { const PERM_LABELS = {
agent: { label: 'Agent', color: 'text-blue-400 bg-blue-900/30 border-blue-700/50' }, agent: { label: 'Agent', color: 'text-blue-400 bg-blue-900/30 border-blue-700/50' },
@ -198,10 +198,12 @@ export default function Customers() {
function CustomerKeyPanel({ customerId }) { function CustomerKeyPanel({ customerId }) {
const [keys, setKeys] = useState(null) const [keys, setKeys] = useState(null)
const [agents, setAgents] = useState(null)
const [copied, setCopied] = useState(null) const [copied, setCopied] = useState(null)
useEffect(() => { useEffect(() => {
api.getCustomerAPIKeys(customerId).then(setKeys) api.getCustomerAPIKeys(customerId).then(setKeys)
api.getCustomerAgents(customerId).then(setAgents)
}, [customerId]) }, [customerId])
const copyKey = (key, id) => { const copyKey = (key, id) => {
@ -210,49 +212,74 @@ function CustomerKeyPanel({ customerId }) {
setTimeout(() => setCopied(null), 2000) setTimeout(() => setCopied(null), 2000)
} }
if (keys === null) { const loading = keys === null || agents === null
if (loading) {
return <div className="px-8 py-3 text-gray-500 text-xs bg-gray-800/20 border-t border-gray-800">Laden...</div> return <div className="px-8 py-3 text-gray-500 text-xs bg-gray-800/20 border-t border-gray-800">Laden...</div>
} }
if (keys.length === 0) { // Agent-Key fuer diesen Kunden ermitteln (zum Abgleich welcher Agent ihn nutzt)
return ( const agentKey = keys.find(k => k.permissions === 'agent')
<div className="px-8 py-3 text-gray-500 text-xs bg-gray-800/20 border-t border-gray-800 flex items-center gap-2">
<Key className="w-3.5 h-3.5" />
Keine API-Keys fuer diesen Kunden
</div>
)
}
return ( return (
<div className="bg-gray-800/20 border-t border-gray-800 px-8 py-3 space-y-1.5"> <div className="bg-gray-800/20 border-t border-gray-800 px-8 py-3 grid grid-cols-1 md:grid-cols-2 gap-x-10 gap-y-3">
<div className="flex items-center gap-2 mb-2">
<Key className="w-3.5 h-3.5 text-gray-500" /> {/* Agents */}
<span className="text-xs text-gray-500 font-medium">API-Keys</span> <div className="space-y-1.5">
<div className="flex items-center gap-2 mb-2">
<Monitor className="w-3.5 h-3.5 text-gray-500" />
<span className="text-xs text-gray-500 font-medium">Agents ({agents.length})</span>
</div>
{agents.length === 0 ? (
<div className="text-xs text-gray-600">Keine Agents zugeordnet</div>
) : (
agents.map((a) => (
<div key={a.id} className="flex items-center gap-2.5">
<span className={`w-1.5 h-1.5 rounded-full flex-shrink-0 ${
a.status === 'online' ? 'bg-green-400' :
a.status === 'stale' ? 'bg-yellow-400' : 'bg-gray-600'
}`} />
<span className="text-xs text-gray-300 flex-1 min-w-0 truncate">{a.name}</span>
<span className="text-[10px] text-gray-600 font-mono flex-shrink-0">{a.agent_version || '—'}</span>
</div>
))
)}
</div> </div>
{keys.map((k) => {
const perm = PERM_LABELS[k.permissions] || PERM_LABELS.read {/* API-Keys */}
return ( <div className="space-y-1.5">
<div key={k.id} className="flex items-center gap-3"> <div className="flex items-center gap-2 mb-2">
<span className={`text-[10px] font-bold px-1.5 py-0.5 rounded border leading-none w-16 text-center flex-shrink-0 ${perm.color}`}> <Key className="w-3.5 h-3.5 text-gray-500" />
{perm.label} <span className="text-xs text-gray-500 font-medium">API-Keys</span>
</span> </div>
<span className="text-xs text-gray-400 flex-1 min-w-0 truncate">{k.name}</span> {keys.length === 0 ? (
<code className="text-xs text-gray-500 font-mono flex-shrink-0"> <div className="text-xs text-gray-600">Keine Keys</div>
{k.key.substring(0, 8)}...{k.key.substring(k.key.length - 4)} ) : (
</code> keys.map((k) => {
<button const perm = PERM_LABELS[k.permissions] || PERM_LABELS.read
onClick={(e) => { e.stopPropagation(); copyKey(k.key, k.id) }} return (
className="text-gray-600 hover:text-orange-400 transition-colors flex-shrink-0" <div key={k.id} className="flex items-center gap-2.5">
title="Key kopieren" <span className={`text-[10px] font-bold px-1.5 py-0.5 rounded border leading-none w-16 text-center flex-shrink-0 ${perm.color}`}>
> {perm.label}
{copied === k.id </span>
? <Check className="w-3.5 h-3.5 text-green-400" /> <code className="text-xs text-gray-500 font-mono flex-1 min-w-0 truncate">
: <Copy className="w-3.5 h-3.5" /> {k.key.substring(0, 8)}...{k.key.substring(k.key.length - 4)}
} </code>
</button> <button
</div> onClick={(e) => { e.stopPropagation(); copyKey(k.key, k.id) }}
) className="text-gray-600 hover:text-orange-400 transition-colors flex-shrink-0"
})} 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>
</div>
)
})
)}
</div>
</div> </div>
) )
} }