feat: Gemini image generation test button in admin settings

API: new testGeminiConnection adminProcedure
- Generates a simple test image via Gemini API
- Returns { ok, model } on success, { ok: false, error } on failure
- Audit logged: "Gemini test succeeded/failed"

UI: "Test Gemini" button next to "Save Image Settings"
- Only visible when Gemini provider is selected
- Shows green success or red error result below the buttons
- Displays the model name on success

Model: gemini-2.0-flash-preview-image-generation (correct name)

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-23 15:11:28 +01:00
parent 502ecba9e9
commit 3ceba38ac8
2 changed files with 70 additions and 1 deletions
@@ -257,6 +257,12 @@ export function SystemSettingsClient() {
},
});
const [geminiTestResult, setGeminiTestResult] = useState<{ ok: boolean; model?: string; error?: string } | null>(null);
const testGeminiMut = trpc.settings.testGeminiConnection.useMutation({
onSuccess: (data) => setGeminiTestResult(data as any),
onError: (err) => setGeminiTestResult({ ok: false, error: err.message }),
});
function handleSaveSmtp() {
saveSmtpMutation.mutate({
smtpHost: smtpHost || undefined,
@@ -1187,7 +1193,7 @@ export function SystemSettingsClient() {
</div>
)}
<div className="flex items-center gap-3 pt-1">
<div className="flex flex-wrap items-center gap-3 pt-1">
<button
type="button"
className={PRIMARY_BUTTON_CLASS}
@@ -1196,10 +1202,31 @@ export function SystemSettingsClient() {
>
{saveImageMutation.isPending ? "Saving..." : "Save Image Settings"}
</button>
{imageProvider === "gemini" && (
<button
type="button"
className="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 text-sm font-medium disabled:opacity-50"
disabled={testGeminiMut.isPending}
onClick={() => testGeminiMut.mutate()}
>
{testGeminiMut.isPending ? "Testing..." : "Test Gemini"}
</button>
)}
{imageSaved && (
<span className="text-sm font-medium text-green-600 dark:text-green-400">Saved</span>
)}
</div>
{geminiTestResult && (
<div className={`mt-3 rounded-lg px-3 py-2 text-sm ${
geminiTestResult.ok
? "bg-green-50 text-green-700 border border-green-200 dark:bg-green-900/20 dark:text-green-300 dark:border-green-800"
: "bg-red-50 text-red-700 border border-red-200 dark:bg-red-900/20 dark:text-red-300 dark:border-red-800"
}`}>
{geminiTestResult.ok
? `Gemini image generation works! Model: ${(geminiTestResult as any).model}`
: `Test failed: ${(geminiTestResult as any).error}`}
</div>
)}
</div>
{/* ── SMTP / Email ──────────────────────────────────────────── */}