From b5e4c57e9375913e1e3be722a32b8019f7f267cc Mon Sep 17 00:00:00 2001 From: Zak Bearman Date: Sun, 6 Sep 2026 17:42:28 +1200 Subject: [PATCH] Simplify settings workspace and fix responsive admin controls --- frontend/app/admin/SettingField.tsx | 72 ++ frontend/app/admin/SettingsPage.tsx | 907 +++--------------------- frontend/app/admin/SettingsRegion.tsx | 13 + frontend/app/admin/config.css | 106 +++ frontend/app/admin/configNavigation.ts | 34 + frontend/app/admin/diagnostics/page.tsx | 14 +- frontend/app/admin/page.tsx | 376 ++-------- frontend/app/globals.css | 5 +- frontend/app/layout.tsx | 1 + frontend/app/ui/AdminShell.tsx | 2 +- frontend/app/ui/AdminSidebar.tsx | 103 +-- frontend/app/ui/WorkspaceNavigation.tsx | 19 +- scripts/review_settings_ui.cjs | 164 +++++ 13 files changed, 600 insertions(+), 1216 deletions(-) create mode 100644 frontend/app/admin/SettingField.tsx create mode 100644 frontend/app/admin/SettingsRegion.tsx create mode 100644 frontend/app/admin/config.css create mode 100644 frontend/app/admin/configNavigation.ts create mode 100644 scripts/review_settings_ui.cjs diff --git a/frontend/app/admin/SettingField.tsx b/frontend/app/admin/SettingField.tsx new file mode 100644 index 0000000..c0e40a8 --- /dev/null +++ b/frontend/app/admin/SettingField.tsx @@ -0,0 +1,72 @@ +'use client' + +export type AdminSetting = { key: string; value: string | null; isSet: boolean; source: string; sensitive: boolean } +type Option = { value: string; label: string } +type Props = { + setting: AdminSetting + label: string + value: string + help?: string + placeholder?: string + boolean?: boolean + numeric?: boolean + multiline?: boolean + options?: Option[] + optionsUnavailable?: boolean + onChange: (value: string) => void +} + +const SELECTS: Record = { + log_level: ['DEBUG', 'INFO', 'WARNING', 'ERROR'].map((value) => ({ value, label: value })), + issue_confirmation_contact_attempts: Array.from({ length: 11 }, (_, index) => ({ value: String(index), label: index === 0 ? 'None — close when fixed' : String(index) })), + issue_confirmation_interval_unit: ['days', 'weeks', 'months'].map((value) => ({ value, label: value[0].toUpperCase() + value.slice(1) })), + artwork_cache_mode: [{ value: 'remote', label: 'Load from the internet' }, { value: 'cache', label: 'Store locally' }], + site_banner_tone: ['info', 'warning', 'error', 'maintenance'].map((value) => ({ value, label: value[0].toUpperCase() + value.slice(1) })), + magent_notify_push_provider: ['ntfy', 'gotify', 'pushover', 'webhook', 'telegram', 'discord'].map((value) => ({ value, label: value })), + requests_data_source: [{ value: 'always_js', label: 'Read directly from Seerr' }, { value: 'prefer_cache', label: 'Use saved requests' }], +} + +export default function SettingField(props: Props) { + const { setting, label, value, help, placeholder, onChange } = props + const id = `setting-${setting.key}` + const options = props.options ?? SELECTS[setting.key] ?? (setting.key === 'log_http_client_level' || setting.key === 'log_background_sync_level' ? SELECTS.log_level : undefined) + const selectedOptions = options && value && !options.some((option) => option.value === value) + ? [{ value, label: `Current selection (${value})` }, ...options] : options + const isTime = setting.key === 'requests_full_sync_time' || setting.key === 'requests_cleanup_time' + const zeroAllowed = setting.key === 'log_file_backup_count' + const minimum = zeroAllowed ? 0 : 1 + const maximum = setting.key === 'issue_confirmation_interval_value' ? 365 : setting.key.endsWith('_port') ? 65535 : undefined + const aria = { id, name: setting.key, 'aria-describedby': help ? `${id}-help` : undefined } + + if (props.boolean) { + return ( +
+
{help &&

{help}

}
+ onChange(String(event.target.checked))} /> +
+ ) + } + + return ( +
+ + {props.optionsUnavailable ? ( + + ) : selectedOptions ? ( + + ) : props.multiline ? ( +