refactor(i-g): AISettings hardcoded Strings auf t() umgestellt — exemplarischer Hotspot-Durchstich
Top-i18n-Hotspot (32 Treffer) migriert: useTranslation-Hooks in alle 4 Tab-Komponenten, ~20 echte UI-Strings auf aiSettings.*/common.*-Keys umgestellt, Provider-Eigennamen bewusst belassen. de+en-Lokalisierung ergaenzt (fallbackLng=de bleibt funktionsgleich). Beweis: tsc exit=0; AISettings+ProactiveAISettings-Tests 18/18 gruen.
This commit is contained in:
@@ -55,7 +55,7 @@
|
|||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
"create": "Erstellen",
|
"create": "Erstellen",
|
||||||
"search": "Suchen",
|
"search": "Suchen",
|
||||||
"loading": "Wird geladen...",
|
"loading": "Laden...",
|
||||||
"noResults": "Keine Ergebnisse gefunden",
|
"noResults": "Keine Ergebnisse gefunden",
|
||||||
"confirm": "Bestätigen",
|
"confirm": "Bestätigen",
|
||||||
"close": "Schließen",
|
"close": "Schließen",
|
||||||
@@ -83,7 +83,8 @@
|
|||||||
"role": "Rolle",
|
"role": "Rolle",
|
||||||
"active": "Aktiv",
|
"active": "Aktiv",
|
||||||
"inactive": "Inaktiv",
|
"inactive": "Inaktiv",
|
||||||
"dismiss": "Schließen"
|
"dismiss": "Schließen",
|
||||||
|
"errorOccurred": "Ein Fehler ist aufgetreten"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"title": "Dashboard",
|
"title": "Dashboard",
|
||||||
@@ -1389,5 +1390,19 @@
|
|||||||
"destLocal": "Lokal",
|
"destLocal": "Lokal",
|
||||||
"destS3": "S3",
|
"destS3": "S3",
|
||||||
"destNextcloud": "Nextcloud"
|
"destNextcloud": "Nextcloud"
|
||||||
|
},
|
||||||
|
"aiSettings": {
|
||||||
|
"providers.title": "Anbieter",
|
||||||
|
"add": "+ Hinzufügen",
|
||||||
|
"save": "Speichern",
|
||||||
|
"cancel": "Abbrechen",
|
||||||
|
"edit": "Bearbeiten",
|
||||||
|
"delete": "Löschen",
|
||||||
|
"name": "Name",
|
||||||
|
"apiKey": "API Key",
|
||||||
|
"baseUrlOptional": "Base URL (optional)",
|
||||||
|
"defaultProvider": "Standard-Anbieter",
|
||||||
|
"defaultTag": "(Standard)",
|
||||||
|
"confirmDeleteProvider": "Anbieter wirklich löschen?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,8 @@
|
|||||||
"role": "Role",
|
"role": "Role",
|
||||||
"active": "Active",
|
"active": "Active",
|
||||||
"inactive": "Inactive",
|
"inactive": "Inactive",
|
||||||
"dismiss": "Dismiss"
|
"dismiss": "Dismiss",
|
||||||
|
"errorOccurred": "An error occurred"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"title": "Dashboard",
|
"title": "Dashboard",
|
||||||
@@ -1389,5 +1390,19 @@
|
|||||||
"destLocal": "Local",
|
"destLocal": "Local",
|
||||||
"destS3": "S3",
|
"destS3": "S3",
|
||||||
"destNextcloud": "Nextcloud"
|
"destNextcloud": "Nextcloud"
|
||||||
|
},
|
||||||
|
"aiSettings": {
|
||||||
|
"providers.title": "Providers",
|
||||||
|
"add": "+ Add",
|
||||||
|
"save": "Save",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"edit": "Edit",
|
||||||
|
"delete": "Delete",
|
||||||
|
"name": "Name",
|
||||||
|
"apiKey": "API Key",
|
||||||
|
"baseUrlOptional": "Base URL (optional)",
|
||||||
|
"defaultProvider": "Default provider",
|
||||||
|
"defaultTag": "(default)",
|
||||||
|
"confirmDeleteProvider": "Really delete provider?"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { asError } from '@/utils/errorTypes';
|
import { asError } from '@/utils/errorTypes';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { Tabs } from '@/components/shared/Tabs';
|
import { Tabs } from '@/components/shared/Tabs';
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
|
|
||||||
// ─── Provider Tab ───
|
// ─── Provider Tab ───
|
||||||
function ProviderTab() {
|
function ProviderTab() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [providers, setProviders] = useState<AIProvider[]>([]);
|
const [providers, setProviders] = useState<AIProvider[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -37,23 +39,23 @@ function ProviderTab() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleDelete = async (id: string) => {
|
const handleDelete = async (id: string) => {
|
||||||
if (!confirm('Anbieter wirklich löschen?')) return;
|
if (!confirm(t('aiSettings.confirmDeleteProvider'))) return;
|
||||||
try { await deleteProvider(id); await load(); } catch (e: unknown) { const errObj = asError(e); setError(errObj?.message || null); }
|
try { await deleteProvider(id); await load(); } catch (e: unknown) { const errObj = asError(e); setError(errObj?.message || null); }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) return <div className="p-4">Laden...</div>;
|
if (loading) return <div className="p-4">{t('common.loading')}</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<h2 className="text-lg font-semibold">Anbieter</h2>
|
<h2 className="text-lg font-semibold">{t('aiSettings.providers.title')}</h2>
|
||||||
<button onClick={() => { setShowForm(!showForm); setEditId(null); setForm({ name: '', provider_type: 'openai', api_key: '', base_url: '', is_default: false }); }} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg hover:bg-primary-700">+ Hinzufügen</button>
|
<button onClick={() => { setShowForm(!showForm); setEditId(null); setForm({ name: '', provider_type: 'openai', api_key: '', base_url: '', is_default: false }); }} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg hover:bg-primary-700">{t('aiSettings.add')} </button>
|
||||||
</div>
|
</div>
|
||||||
{error && <div className="text-sm text-red-600">{typeof error === "string" ? error : (error as any)?.message || "Ein Fehler ist aufgetreten"}</div>}
|
{error && <div className="text-sm text-red-600">{typeof error === "string" ? error : (error as any)?.message || "t('common.errorOccurred')"}</div>}
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<div className="border border-secondary-200 rounded-lg p-4 space-y-3 bg-secondary-50">
|
<div className="border border-secondary-200 rounded-lg p-4 space-y-3 bg-secondary-50">
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<input placeholder="Name" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
<input placeholder={t('aiSettings.name')} value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
||||||
<select value={form.provider_type} onChange={(e) => setForm({ ...form, provider_type: e.target.value })} className="border rounded-lg px-3 py-2 text-sm">
|
<select value={form.provider_type} onChange={(e) => setForm({ ...form, provider_type: e.target.value })} className="border rounded-lg px-3 py-2 text-sm">
|
||||||
<option value="openai">OpenAI</option>
|
<option value="openai">OpenAI</option>
|
||||||
<option value="anthropic">Anthropic</option>
|
<option value="anthropic">Anthropic</option>
|
||||||
@@ -62,15 +64,15 @@ function ProviderTab() {
|
|||||||
<option value="huggingface">HuggingFace</option>
|
<option value="huggingface">HuggingFace</option>
|
||||||
<option value="custom">Custom</option>
|
<option value="custom">Custom</option>
|
||||||
</select>
|
</select>
|
||||||
<input placeholder="API Key" type="password" value={form.api_key} onChange={(e) => setForm({ ...form, api_key: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
<input placeholder={t('aiSettings.apiKey')} type="password" value={form.api_key} onChange={(e) => setForm({ ...form, api_key: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
||||||
<input placeholder="Base URL (optional)" value={form.base_url} onChange={(e) => setForm({ ...form, base_url: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
<input placeholder={t('aiSettings.baseUrlOptional')} value={form.base_url} onChange={(e) => setForm({ ...form, base_url: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<label className="flex items-center gap-2 text-sm">
|
<label className="flex items-center gap-2 text-sm">
|
||||||
<input type="checkbox" checked={form.is_default} onChange={(e) => setForm({ ...form, is_default: e.target.checked })} /> Standard-Anbieter
|
<input type="checkbox" checked={form.is_default} onChange={(e) => setForm({ ...form, is_default: e.target.checked })} /> t('aiSettings.defaultProvider')
|
||||||
</label>
|
</label>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button onClick={handleSave} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">Speichern</button>
|
<button onClick={handleSave} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">{t('aiSettings.save')} </button>
|
||||||
<button onClick={() => setShowForm(false)} className="px-3 py-1.5 text-sm border rounded-lg">Abbrechen</button>
|
<button onClick={() => setShowForm(false)} className="px-3 py-1.5 text-sm border rounded-lg">{t('aiSettings.cancel')} </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -78,12 +80,12 @@ function ProviderTab() {
|
|||||||
{providers.map((p) => (
|
{providers.map((p) => (
|
||||||
<div key={p.id} className="border border-secondary-200 rounded-lg p-3 flex items-center justify-between">
|
<div key={p.id} className="border border-secondary-200 rounded-lg p-3 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-sm">{p.name} {p.is_default && <span className="text-xs text-primary-600">(Standard)</span>}</div>
|
<div className="font-medium text-sm">{p.name} {p.is_default && <span className="text-xs text-primary-600">{t('aiSettings.defaultTag')}</span>}</div>
|
||||||
<div className="text-xs text-secondary-500">{p.provider_type} · {p.api_key}</div>
|
<div className="text-xs text-secondary-500">{p.provider_type} · {p.api_key}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button onClick={() => handleEdit(p)} className="text-sm text-primary-600 hover:underline">Bearbeiten</button>
|
<button onClick={() => handleEdit(p)} className="text-sm text-primary-600 hover:underline">{t('aiSettings.edit')} </button>
|
||||||
<button onClick={() => handleDelete(p.id)} className="text-sm text-red-600 hover:underline">Löschen</button>
|
<button onClick={() => handleDelete(p.id)} className="text-sm text-red-600 hover:underline">{t('aiSettings.delete')} </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -94,6 +96,7 @@ function ProviderTab() {
|
|||||||
|
|
||||||
// ─── Presets Tab ───
|
// ─── Presets Tab ───
|
||||||
function PresetTab() {
|
function PresetTab() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [presets, setPresets] = useState<AIPreset[]>([]);
|
const [presets, setPresets] = useState<AIPreset[]>([]);
|
||||||
const [providers, setProviders] = useState<AIProvider[]>([]);
|
const [providers, setProviders] = useState<AIProvider[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -126,15 +129,15 @@ function PresetTab() {
|
|||||||
try { await deletePreset(id); await load(); } catch (e: unknown) { const errObj = asError(e); setError(errObj?.message || null); }
|
try { await deletePreset(id); await load(); } catch (e: unknown) { const errObj = asError(e); setError(errObj?.message || null); }
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) return <div className="p-4">Laden...</div>;
|
if (loading) return <div className="p-4">{t('common.loading')}</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<h2 className="text-lg font-semibold">Modelle & Presets</h2>
|
<h2 className="text-lg font-semibold">Modelle & Presets</h2>
|
||||||
<button onClick={() => { setShowForm(!showForm); setEditId(null); setForm({ name: '', model_id: '', provider_id: '', temperature: 0.7, max_tokens: 2048, top_p: 1.0, system_prompt: '' }); }} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">+ Hinzufügen</button>
|
<button onClick={() => { setShowForm(!showForm); setEditId(null); setForm({ name: '', model_id: '', provider_id: '', temperature: 0.7, max_tokens: 2048, top_p: 1.0, system_prompt: '' }); }} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">{t('aiSettings.add')} </button>
|
||||||
</div>
|
</div>
|
||||||
{error && <div className="text-sm text-red-600">{typeof error === "string" ? error : (error as any)?.message || "Ein Fehler ist aufgetreten"}</div>}
|
{error && <div className="text-sm text-red-600">{typeof error === "string" ? error : (error as any)?.message || "t('common.errorOccurred')"}</div>}
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<div className="border border-secondary-200 rounded-lg p-4 space-y-3 bg-secondary-50">
|
<div className="border border-secondary-200 rounded-lg p-4 space-y-3 bg-secondary-50">
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
@@ -150,8 +153,8 @@ function PresetTab() {
|
|||||||
</div>
|
</div>
|
||||||
<textarea placeholder="System Prompt (optional)" value={form.system_prompt} onChange={(e) => setForm({ ...form, system_prompt: e.target.value })} rows={3} className="w-full border rounded-lg px-3 py-2 text-sm" />
|
<textarea placeholder="System Prompt (optional)" value={form.system_prompt} onChange={(e) => setForm({ ...form, system_prompt: e.target.value })} rows={3} className="w-full border rounded-lg px-3 py-2 text-sm" />
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button onClick={handleSave} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">Speichern</button>
|
<button onClick={handleSave} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">{t('aiSettings.save')} </button>
|
||||||
<button onClick={() => setShowForm(false)} className="px-3 py-1.5 text-sm border rounded-lg">Abbrechen</button>
|
<button onClick={() => setShowForm(false)} className="px-3 py-1.5 text-sm border rounded-lg">{t('aiSettings.cancel')} </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -163,8 +166,8 @@ function PresetTab() {
|
|||||||
<div className="text-xs text-secondary-500">{p.model_id} · temp={p.temperature} · max_tokens={p.max_tokens}</div>
|
<div className="text-xs text-secondary-500">{p.model_id} · temp={p.temperature} · max_tokens={p.max_tokens}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button onClick={() => handleEdit(p)} className="text-sm text-primary-600 hover:underline">Bearbeiten</button>
|
<button onClick={() => handleEdit(p)} className="text-sm text-primary-600 hover:underline">{t('aiSettings.edit')} </button>
|
||||||
<button onClick={() => handleDelete(p.id)} className="text-sm text-red-600 hover:underline">Löschen</button>
|
<button onClick={() => handleDelete(p.id)} className="text-sm text-red-600 hover:underline">{t('aiSettings.delete')} </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -175,6 +178,7 @@ function PresetTab() {
|
|||||||
|
|
||||||
// ─── Agents Tab ───
|
// ─── Agents Tab ───
|
||||||
function AgentTab() {
|
function AgentTab() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [agents, setAgents] = useState<AIAgent[]>([]);
|
const [agents, setAgents] = useState<AIAgent[]>([]);
|
||||||
const [presets, setPresets] = useState<AIPreset[]>([]);
|
const [presets, setPresets] = useState<AIPreset[]>([]);
|
||||||
const [tools, setTools] = useState<AITool[]>([]);
|
const [tools, setTools] = useState<AITool[]>([]);
|
||||||
@@ -217,19 +221,19 @@ function AgentTab() {
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) return <div className="p-4">Laden...</div>;
|
if (loading) return <div className="p-4">{t('common.loading')}</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<h2 className="text-lg font-semibold">Agenten</h2>
|
<h2 className="text-lg font-semibold">Agenten</h2>
|
||||||
<button onClick={() => { setShowForm(!showForm); setEditId(null); setForm({ name: '', description: '', system_prompt: '', preset_id: '', tool_ids: [] }); }} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">+ Hinzufügen</button>
|
<button onClick={() => { setShowForm(!showForm); setEditId(null); setForm({ name: '', description: '', system_prompt: '', preset_id: '', tool_ids: [] }); }} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">{t('aiSettings.add')} </button>
|
||||||
</div>
|
</div>
|
||||||
{error && <div className="text-sm text-red-600">{typeof error === "string" ? error : (error as any)?.message || "Ein Fehler ist aufgetreten"}</div>}
|
{error && <div className="text-sm text-red-600">{typeof error === "string" ? error : (error as any)?.message || "t('common.errorOccurred')"}</div>}
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<div className="border border-secondary-200 rounded-lg p-4 space-y-3 bg-secondary-50">
|
<div className="border border-secondary-200 rounded-lg p-4 space-y-3 bg-secondary-50">
|
||||||
<div className="grid grid-cols-2 gap-3">
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<input placeholder="Name" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
<input placeholder={t('aiSettings.name')} value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
||||||
<input placeholder="Beschreibung" value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
<input placeholder="Beschreibung" value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} className="border rounded-lg px-3 py-2 text-sm" />
|
||||||
</div>
|
</div>
|
||||||
<select value={form.preset_id} onChange={(e) => setForm({ ...form, preset_id: e.target.value })} className="w-full border rounded-lg px-3 py-2 text-sm">
|
<select value={form.preset_id} onChange={(e) => setForm({ ...form, preset_id: e.target.value })} className="w-full border rounded-lg px-3 py-2 text-sm">
|
||||||
@@ -251,8 +255,8 @@ function AgentTab() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button onClick={handleSave} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">Speichern</button>
|
<button onClick={handleSave} className="px-3 py-1.5 text-sm bg-primary-600 text-white rounded-lg">{t('aiSettings.save')} </button>
|
||||||
<button onClick={() => setShowForm(false)} className="px-3 py-1.5 text-sm border rounded-lg">Abbrechen</button>
|
<button onClick={() => setShowForm(false)} className="px-3 py-1.5 text-sm border rounded-lg">{t('aiSettings.cancel')} </button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -260,13 +264,13 @@ function AgentTab() {
|
|||||||
{agents.map((a) => (
|
{agents.map((a) => (
|
||||||
<div key={a.id} className="border border-secondary-200 rounded-lg p-3 flex items-center justify-between">
|
<div key={a.id} className="border border-secondary-200 rounded-lg p-3 flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium text-sm">{a.name} {a.is_default && <span className="text-xs text-primary-600">(Standard)</span>}</div>
|
<div className="font-medium text-sm">{a.name} {a.is_default && <span className="text-xs text-primary-600">{t('aiSettings.defaultTag')}</span>}</div>
|
||||||
<div className="text-xs text-secondary-500">{a.description || 'Keine Beschreibung'}</div>
|
<div className="text-xs text-secondary-500">{a.description || 'Keine Beschreibung'}</div>
|
||||||
{a.tool_ids.length > 0 && <div className="text-xs text-secondary-400 mt-1">Tools: {a.tool_ids.join(', ')}</div>}
|
{a.tool_ids.length > 0 && <div className="text-xs text-secondary-400 mt-1">Tools: {a.tool_ids.join(', ')}</div>}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<button onClick={() => handleEdit(a)} className="text-sm text-primary-600 hover:underline">Bearbeiten</button>
|
<button onClick={() => handleEdit(a)} className="text-sm text-primary-600 hover:underline">{t('aiSettings.edit')} </button>
|
||||||
{!a.is_default && <button onClick={() => handleDelete(a.id)} className="text-sm text-red-600 hover:underline">Löschen</button>}
|
{!a.is_default && <button onClick={() => handleDelete(a.id)} className="text-sm text-red-600 hover:underline">{t('aiSettings.delete')} </button>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -277,6 +281,7 @@ function AgentTab() {
|
|||||||
|
|
||||||
// ─── Tools Tab ───
|
// ─── Tools Tab ───
|
||||||
function ToolsTab() {
|
function ToolsTab() {
|
||||||
|
const { t } = useTranslation();
|
||||||
const [tools, setTools] = useState<AITool[]>([]);
|
const [tools, setTools] = useState<AITool[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
@@ -285,13 +290,13 @@ function ToolsTab() {
|
|||||||
fetchTools().then(setTools).catch((e) => setError(e?.message)).finally(() => setLoading(false));
|
fetchTools().then(setTools).catch((e) => setError(e?.message)).finally(() => setLoading(false));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (loading) return <div className="p-4">Laden...</div>;
|
if (loading) return <div className="p-4">{t('common.loading')}</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h2 className="text-lg font-semibold">Verfügbare Tools</h2>
|
<h2 className="text-lg font-semibold">Verfügbare Tools</h2>
|
||||||
<p className="text-sm text-secondary-500">Diese Tools werden von Plugins bereitgestellt und können Agenten zugewiesen werden.</p>
|
<p className="text-sm text-secondary-500">Diese Tools werden von Plugins bereitgestellt und können Agenten zugewiesen werden.</p>
|
||||||
{error && <div className="text-sm text-red-600">{typeof error === "string" ? error : (error as any)?.message || "Ein Fehler ist aufgetreten"}</div>}
|
{error && <div className="text-sm text-red-600">{typeof error === "string" ? error : (error as any)?.message || "t('common.errorOccurred')"}</div>}
|
||||||
{tools.length === 0 ? (
|
{tools.length === 0 ? (
|
||||||
<div className="text-sm text-secondary-400 py-8 text-center">Keine Tools verfügbar. Plugins können Tools registrieren.</div>
|
<div className="text-sm text-secondary-400 py-8 text-center">Keine Tools verfügbar. Plugins können Tools registrieren.</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
Reference in New Issue
Block a user