98 lines
3.7 KiB
TypeScript
98 lines
3.7 KiB
TypeScript
'use client'
|
|
|
|
import React from 'react'
|
|
import type { LLMPolicy } from '../_types'
|
|
|
|
interface Props {
|
|
policies: LLMPolicy[]
|
|
onCreate: () => void
|
|
onEdit: (p: LLMPolicy) => void
|
|
onDelete: (id: string) => void
|
|
onToggleActive: (p: LLMPolicy) => void
|
|
}
|
|
|
|
export function PoliciesTab({ policies, onCreate, onEdit, onDelete, onToggleActive }: Props) {
|
|
return (
|
|
<div>
|
|
<div className="flex justify-between items-center mb-4">
|
|
<h2 className="text-lg font-semibold">{policies.length} LLM-Policies</h2>
|
|
<button
|
|
onClick={onCreate}
|
|
className="px-4 py-2 bg-purple-600 text-white rounded-lg text-sm hover:bg-purple-700"
|
|
>
|
|
+ Policy erstellen
|
|
</button>
|
|
</div>
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
|
{policies.map(policy => (
|
|
<div key={policy.id} className={`bg-white rounded-xl border p-5 ${
|
|
policy.is_active ? 'border-gray-200' : 'border-gray-100 opacity-60'
|
|
}`}>
|
|
<div className="flex items-start justify-between mb-3">
|
|
<div>
|
|
<h3 className="font-semibold text-gray-900">{policy.name}</h3>
|
|
<p className="text-xs text-gray-500 mt-0.5">{policy.description}</p>
|
|
</div>
|
|
<button
|
|
onClick={() => onToggleActive(policy)}
|
|
className={`px-2 py-0.5 rounded text-xs font-medium ${
|
|
policy.is_active ? 'bg-green-50 text-green-700' : 'bg-gray-100 text-gray-500'
|
|
}`}
|
|
>
|
|
{policy.is_active ? 'Aktiv' : 'Inaktiv'}
|
|
</button>
|
|
</div>
|
|
|
|
<div className="space-y-2 text-sm">
|
|
{(policy.allowed_models || []).length > 0 && (
|
|
<div>
|
|
<span className="text-gray-500">Erlaubte Models: </span>
|
|
<div className="flex flex-wrap gap-1 mt-1">
|
|
{policy.allowed_models.map(m => (
|
|
<span key={m} className="px-1.5 py-0.5 bg-blue-50 text-blue-700 rounded text-xs">{m}</span>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
<div className="flex justify-between text-gray-600">
|
|
<span>Rate-Limit</span>
|
|
<span className="font-mono text-xs">{policy.rate_limit_rpm} req/min, {policy.rate_limit_tpd} tok/tag</span>
|
|
</div>
|
|
<div className="flex justify-between text-gray-600">
|
|
<span>Max Tokens/Request</span>
|
|
<span className="font-mono text-xs">{policy.max_tokens_per_request}</span>
|
|
</div>
|
|
<div className="flex gap-3">
|
|
{policy.pii_detection_required && (
|
|
<span className="px-2 py-0.5 bg-amber-50 text-amber-700 rounded text-xs">PII-Erkennung</span>
|
|
)}
|
|
{policy.pii_redaction_required && (
|
|
<span className="px-2 py-0.5 bg-red-50 text-red-700 rounded text-xs">PII-Redaktion</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex justify-end gap-2 mt-3 pt-3 border-t border-gray-100">
|
|
<button
|
|
onClick={() => onEdit(policy)}
|
|
className="text-sm text-purple-600 hover:text-purple-700"
|
|
>
|
|
Bearbeiten
|
|
</button>
|
|
<button
|
|
onClick={() => onDelete(policy.id)}
|
|
className="text-sm text-red-600 hover:text-red-700"
|
|
>
|
|
Loeschen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
{policies.length === 0 && (
|
|
<div className="text-center py-12 text-gray-400">Keine LLM-Policies vorhanden</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|