From d438a8520d3d76f9abda365b18e2e5a3a55b2f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Fri, 8 May 2026 21:45:18 +0200 Subject: [PATCH] feat(admin): bug-report toggle, unify "Trennen" button styling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bucket E+F of the v1.4.3 polish pass. **Disconnect-button styling unified.** Three integrations had three different "Trennen" buttons: Withings used `outline` + `text- destructive`; moodLog used solid `destructive` (red bg, white text); the AI/Codex card used `ghost` + `text-destructive`. Same action, three readings — confusing. All three now use the Withings pattern (`outline` + `text-destructive`). The actual confirmation button inside the AlertDialog keeps its solid-red treatment so the final "yes, do it" still reads as a strong commit. **Bug-report explicit on/off toggle.** Until v1.4.3 the bug-report feature was implicitly gated on "is the GitHub token + repo configured". An admin who wanted to *temporarily* hide the report button (e.g. during a freeze window) had to clear the encrypted token, forcing re-entry on resume — destructive for a reversible intent. Migration `0027_bug_report_enabled_toggle` adds `app_settings.bug_report_enabled BOOLEAN NOT NULL DEFAULT TRUE`, the admin settings schema accepts `bugReportEnabled`, and a Switch in the admin → Bug Report card lets the operator flip it. `POST /api/bugreport` now returns 503 ("Bug reports are disabled by the administrator") when the flag is off — the client already renders a "not configured" screen on that status, so the report button hides gracefully without further client-side wiring. i18n keys added: `admin.bugReportEnabledLabel` + `…Description` in both DE and EN. Tests + typecheck green (671/671). Co-Authored-By: Claude Opus 4.7 (1M context) --- messages/de.json | 2 ++ messages/en.json | 2 ++ .../migration.sql | 16 +++++++++++++ prisma/schema.prisma | 5 ++++ src/app/api/admin/settings/route.ts | 3 +++ src/app/api/bugreport/route.ts | 9 ++++++++ src/components/admin/_shared.tsx | 1 + src/components/admin/bug-report-section.tsx | 23 +++++++++++++++++++ src/components/settings/ai-section.tsx | 5 +++- .../settings/integrations-section.tsx | 11 ++++++++- src/lib/validations/admin.ts | 1 + 11 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 prisma/migrations/0027_bug_report_enabled_toggle/migration.sql diff --git a/messages/de.json b/messages/de.json index 1bd4095f7..52c22e357 100644 --- a/messages/de.json +++ b/messages/de.json @@ -1273,6 +1273,8 @@ "monitoringSave": "Monitoring speichern", "bugReportGithub": "Bugreport (GitHub Issues)", "bugReportGithubDescription": "Hinterlege GitHub-Issue-Token und Repository für eingehende Bugreports.", + "bugReportEnabledLabel": "Bug-Reports aktiviert", + "bugReportEnabledDescription": "Wenn deaktiviert, ist der Report-Button im Frontend nicht mehr sichtbar — Token und Repository bleiben gespeichert.", "bugReportRepo": "Repository", "bugReportRepoPlaceholder": "owner/repo", "bugReportToken": "Issue-Token", diff --git a/messages/en.json b/messages/en.json index 3dff1947f..7b33c4cf8 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1273,6 +1273,8 @@ "monitoringSave": "Save monitoring", "bugReportGithub": "Bug report (GitHub Issues)", "bugReportGithubDescription": "Configure GitHub issue token and repository for incoming bug reports.", + "bugReportEnabledLabel": "Bug reports enabled", + "bugReportEnabledDescription": "When off, the in-app report button hides — token and repository stay configured.", "bugReportRepo": "Repository", "bugReportRepoPlaceholder": "owner/repo", "bugReportToken": "Issue token", diff --git a/prisma/migrations/0027_bug_report_enabled_toggle/migration.sql b/prisma/migrations/0027_bug_report_enabled_toggle/migration.sql new file mode 100644 index 000000000..8431b2f8a --- /dev/null +++ b/prisma/migrations/0027_bug_report_enabled_toggle/migration.sql @@ -0,0 +1,16 @@ +-- v1.4.3: explicit on/off switch for the bug-report surface. +-- +-- Until this release the bug-report feature was implicitly gated on +-- "is the GitHub token + repo configured?". That coupling meant an +-- admin who wanted to *temporarily* hide the bug-report button (e.g. +-- during a deploy freeze) had to clear the encrypted token, which +-- forced a re-entry on resume — destructive for a reversible +-- intent. +-- +-- Default `true` so existing instances with credentials configured +-- keep the feature visible after the upgrade. Admins who want it off +-- flip the switch in `/admin/integrations` (or whichever section +-- mounts the bug-report card). + +ALTER TABLE "app_settings" + ADD COLUMN "bug_report_enabled" BOOLEAN NOT NULL DEFAULT TRUE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 983dc4afc..4df027029 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -466,6 +466,11 @@ model AppSettings { glitchtipEnvironment String? @map("glitchtip_environment") githubIssueTokenEncrypted String? @map("github_issue_token_encrypted") githubIssueRepo String? @map("github_issue_repo") + // v1.4.3: explicit toggle so an admin can hide the bug-report + // surface without nuking the token+repo config. Default true so + // existing instances with credentials don't accidentally lose + // the feature on upgrade. + bugReportEnabled Boolean @default(true) @map("bug_report_enabled") // Medication reminder thresholds (minutes after windowEnd) reminderLateMinutes Int @default(120) @map("reminder_late_minutes") diff --git a/src/app/api/admin/settings/route.ts b/src/app/api/admin/settings/route.ts index 9ab0671f0..b4155f145 100644 --- a/src/app/api/admin/settings/route.ts +++ b/src/app/api/admin/settings/route.ts @@ -41,6 +41,7 @@ export const GET = apiHandler(async () => { bugReportConfigured: Boolean( settings?.githubIssueRepo && settings?.githubIssueTokenEncrypted, ), + bugReportEnabled: settings?.bugReportEnabled ?? true, reminderLateMinutes: settings?.reminderLateMinutes ?? 120, reminderMissedMinutes: settings?.reminderMissedMinutes ?? 240, moodLogGlobal: settings?.moodLogGlobal ?? true, @@ -73,6 +74,7 @@ export const PUT = apiHandler(async (request: NextRequest) => { "umamiEnabled", "glitchtipEnabled", "moodLogGlobal", + "bugReportEnabled", ] as const; for (const field of booleanFields) { if (data[field] !== undefined) { @@ -220,6 +222,7 @@ export const PUT = apiHandler(async (request: NextRequest) => { bugReportConfigured: Boolean( settings.githubIssueRepo && settings.githubIssueTokenEncrypted, ), + bugReportEnabled: settings.bugReportEnabled, reminderLateMinutes: settings.reminderLateMinutes, reminderMissedMinutes: settings.reminderMissedMinutes, moodLogGlobal: settings.moodLogGlobal, diff --git a/src/app/api/bugreport/route.ts b/src/app/api/bugreport/route.ts index aa1839e27..5d1e4a3d9 100644 --- a/src/app/api/bugreport/route.ts +++ b/src/app/api/bugreport/route.ts @@ -36,9 +36,18 @@ export const POST = apiHandler(async (request: NextRequest) => { select: { githubIssueTokenEncrypted: true, githubIssueRepo: true, + bugReportEnabled: true, }, }); + // v1.4.3: explicit toggle. When the admin disables bug reports we + // return 503 immediately — the client already renders a "not + // configured" UI for that status, so the report button gracefully + // disappears without needing additional client-side wiring. + if (appSettings && appSettings.bugReportEnabled === false) { + return apiError("Bug reports are disabled by the administrator", 503); + } + let configuredToken: string | null = null; if (appSettings?.githubIssueTokenEncrypted) { try { diff --git a/src/components/admin/_shared.tsx b/src/components/admin/_shared.tsx index c45b35ffc..f1b5dc4e7 100644 --- a/src/components/admin/_shared.tsx +++ b/src/components/admin/_shared.tsx @@ -70,6 +70,7 @@ export interface AdminSettings { glitchtipEnvironment: string | null; bugReportRepo: string | null; bugReportConfigured: boolean; + bugReportEnabled: boolean; reminderLateMinutes: number; reminderMissedMinutes: number; } diff --git a/src/components/admin/bug-report-section.tsx b/src/components/admin/bug-report-section.tsx index d16e1c023..b0cd9a392 100644 --- a/src/components/admin/bug-report-section.tsx +++ b/src/components/admin/bug-report-section.tsx @@ -6,6 +6,7 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { Switch } from "@/components/ui/switch"; import { useTranslations } from "@/lib/i18n/context"; import { PasswordInput, useAdminSettings, useUpdateSettings } from "./_shared"; @@ -21,6 +22,7 @@ export function BugReportSection({ id }: { id: string }) { const bugReportRepoValue = bugReportRepoDraft ?? settings?.bugReportRepo ?? ""; const configured = settings?.bugReportConfigured ?? false; + const enabled = settings?.bugReportEnabled ?? true; function saveBugReportSettings() { const payload: Record = { @@ -38,6 +40,10 @@ export function BugReportSection({ id }: { id: string }) { }); } + function toggleEnabled(next: boolean) { + updateSettings.mutate({ bugReportEnabled: next }); + } + return (
+
+
+

+ {t("admin.bugReportEnabledLabel")} +

+

+ {t("admin.bugReportEnabledDescription")} +

+
+ +
+
+ {/* v1.4.3: aligned with the Withings/moodLog disconnect + buttons — outline + text-destructive so the same action + reads the same everywhere in Settings. */} diff --git a/src/lib/validations/admin.ts b/src/lib/validations/admin.ts index 243f85b19..feb3ca36e 100644 --- a/src/lib/validations/admin.ts +++ b/src/lib/validations/admin.ts @@ -71,6 +71,7 @@ export const adminSettingsSchema = z .optional(), bugReportToken: z.string().optional(), clearBugReportToken: z.boolean().optional(), + bugReportEnabled: z.boolean().optional(), reminderLateMinutes: z.number().int().min(15).max(480).optional(), reminderMissedMinutes: z.number().int().min(30).max(720).optional(), moodLogGlobal: z.boolean().optional(),