From 538f9ec69b085adc2e3704ea28832ecf0d6569b5 Mon Sep 17 00:00:00 2001 From: jlightner Date: Fri, 3 Apr 2026 04:21:08 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20Replaced=20all=2028=20JS=20hover=20hand?= =?UTF-8?q?lers=20in=20Settings.tsx=20with=20CSS=20utilit=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - "src/frontend/src/pages/Settings.tsx" - "src/frontend/src/styles/global.css" - "src/frontend/src/components/Skeleton.tsx" GSD-Task: S02/T03 --- src/frontend/src/components/Skeleton.tsx | 59 ++++++ src/frontend/src/pages/Settings.tsx | 257 +++-------------------- src/frontend/src/styles/global.css | 30 +++ 3 files changed, 117 insertions(+), 229 deletions(-) diff --git a/src/frontend/src/components/Skeleton.tsx b/src/frontend/src/components/Skeleton.tsx index b110f98..d1cad8a 100644 --- a/src/frontend/src/components/Skeleton.tsx +++ b/src/frontend/src/components/Skeleton.tsx @@ -174,6 +174,65 @@ export function SkeletonSystem() { ); } +/** Skeleton for the settings page. */ +export function SkeletonSettings() { + return ( +
+ + + {/* General section */} +
+
+ + +
+
+ {Array.from({ length: 4 }).map((_, i) => ( +
+ + +
+ ))} +
+
+ + {/* Platform Settings section */} +
+
+ + +
+ +
+ + {/* Format Profiles section */} +
+
+ + +
+ +
+ + {/* Notifications section */} +
+
+ + +
+ +
+
+ ); +} + /** Skeleton for the channels list page. */ export function SkeletonChannelsList({ rows = 4 }: { rows?: number }) { return ( diff --git a/src/frontend/src/pages/Settings.tsx b/src/frontend/src/pages/Settings.tsx index fc0d1a5..fa763de 100644 --- a/src/frontend/src/pages/Settings.tsx +++ b/src/frontend/src/pages/Settings.tsx @@ -25,6 +25,7 @@ import { Modal } from '../components/Modal'; import { FormatProfileForm, type FormatProfileFormValues } from '../components/FormatProfileForm'; import { PlatformSettingsForm, type PlatformSettingsFormValues } from '../components/PlatformSettingsForm'; import { NotificationForm, type NotificationFormValues } from '../components/NotificationForm'; +import { SkeletonSettings } from '../components/Skeleton'; import type { FormatProfile, PlatformSettings } from '@shared/types/index'; // ── Badge styles ── @@ -40,17 +41,6 @@ const badgeBase: React.CSSProperties = { letterSpacing: '0.04em', }; -const iconButtonBase: React.CSSProperties = { - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - width: 28, - height: 28, - borderRadius: 'var(--radius-sm)', - color: 'var(--text-muted)', - transition: 'color var(--transition-fast), background-color var(--transition-fast)', -}; - // ── Component ── export function SettingsPage() { @@ -297,15 +287,7 @@ export function SettingsPage() { onClick={(e) => { e.stopPropagation(); setEditingPlatform(row.platform); }} title={`Edit ${row.label} settings`} aria-label={`Edit ${row.label} settings`} - style={iconButtonBase} - onMouseEnter={(e) => { - e.currentTarget.style.color = 'var(--accent)'; - e.currentTarget.style.backgroundColor = 'var(--accent-subtle)'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.color = 'var(--text-muted)'; - e.currentTarget.style.backgroundColor = 'transparent'; - }} + className="btn-icon btn-icon-edit" > @@ -479,15 +461,7 @@ export function SettingsPage() { onClick={(e) => { e.stopPropagation(); setEditingProfile(p); }} title="Edit profile" aria-label={`Edit ${p.name}`} - style={iconButtonBase} - onMouseEnter={(e) => { - e.currentTarget.style.color = 'var(--accent)'; - e.currentTarget.style.backgroundColor = 'var(--accent-subtle)'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.color = 'var(--text-muted)'; - e.currentTarget.style.backgroundColor = 'transparent'; - }} + className="btn-icon btn-icon-edit" > @@ -496,15 +470,7 @@ export function SettingsPage() { onClick={(e) => { e.stopPropagation(); setDeletingProfile(p); }} title="Delete profile" aria-label={`Delete ${p.name}`} - style={iconButtonBase} - onMouseEnter={(e) => { - e.currentTarget.style.color = 'var(--danger)'; - e.currentTarget.style.backgroundColor = 'var(--danger-bg)'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.color = 'var(--text-muted)'; - e.currentTarget.style.backgroundColor = 'transparent'; - }} + className="btn-icon btn-icon-delete" > @@ -587,18 +553,8 @@ export function SettingsPage() { title="Send test notification" aria-label={`Test ${n.name}`} disabled={result === 'loading'} - style={{ - ...iconButtonBase, - opacity: result === 'loading' ? 0.5 : 1, - }} - onMouseEnter={(e) => { - e.currentTarget.style.color = 'var(--success)'; - e.currentTarget.style.backgroundColor = 'var(--success-bg)'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.color = 'var(--text-muted)'; - e.currentTarget.style.backgroundColor = 'transparent'; - }} + className="btn-icon btn-icon-test" + style={{ opacity: result === 'loading' ? 0.5 : 1 }} > {result === 'loading' ? @@ -611,15 +567,7 @@ export function SettingsPage() { onClick={(e) => { e.stopPropagation(); setEditingNotification(n); }} title="Edit channel" aria-label={`Edit ${n.name}`} - style={iconButtonBase} - onMouseEnter={(e) => { - e.currentTarget.style.color = 'var(--accent)'; - e.currentTarget.style.backgroundColor = 'var(--accent-subtle)'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.color = 'var(--text-muted)'; - e.currentTarget.style.backgroundColor = 'transparent'; - }} + className="btn-icon btn-icon-edit" > @@ -629,15 +577,7 @@ export function SettingsPage() { onClick={(e) => { e.stopPropagation(); setDeletingNotification(n); }} title="Delete channel" aria-label={`Delete ${n.name}`} - style={iconButtonBase} - onMouseEnter={(e) => { - e.currentTarget.style.color = 'var(--danger)'; - e.currentTarget.style.backgroundColor = 'var(--danger-bg)'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.color = 'var(--text-muted)'; - e.currentTarget.style.backgroundColor = 'transparent'; - }} + className="btn-icon btn-icon-delete" > @@ -652,12 +592,7 @@ export function SettingsPage() { // ── Loading state ── if (profilesLoading) { - return ( -
- - Loading settings... -
- ); + return ; } // ── Error state ── @@ -683,17 +618,7 @@ export function SettingsPage() { @@ -792,22 +709,8 @@ export function SettingsPage() { onClick={handleCopyApiKey} title={copySuccess ? 'Copied!' : 'Copy to clipboard'} aria-label="Copy API key to clipboard" - style={{ - ...iconButtonBase, - color: copySuccess ? 'var(--success)' : 'var(--text-muted)', - }} - onMouseEnter={(e) => { - if (!copySuccess) { - e.currentTarget.style.color = 'var(--accent)'; - e.currentTarget.style.backgroundColor = 'var(--accent-subtle)'; - } - }} - onMouseLeave={(e) => { - if (!copySuccess) { - e.currentTarget.style.color = 'var(--text-muted)'; - e.currentTarget.style.backgroundColor = 'transparent'; - } - }} + className="btn-icon btn-icon-edit" + style={copySuccess ? { color: 'var(--success)' } : undefined} > {copySuccess ? : } @@ -817,15 +720,7 @@ export function SettingsPage() { onClick={() => setShowRegenerateConfirm(true)} title="Regenerate API key" aria-label="Regenerate API key" - style={iconButtonBase} - onMouseEnter={(e) => { - e.currentTarget.style.color = 'var(--warning)'; - e.currentTarget.style.backgroundColor = 'var(--warning-bg)'; - }} - onMouseLeave={(e) => { - e.currentTarget.style.color = 'var(--text-muted)'; - e.currentTarget.style.backgroundColor = 'transparent'; - }} + className="btn-icon btn-icon-warning" > @@ -911,17 +806,10 @@ export function SettingsPage() {