diff --git a/app/src/components/navigation/UserMenu.tsx b/app/src/components/navigation/UserMenu.tsx index b89f703..3981b73 100644 --- a/app/src/components/navigation/UserMenu.tsx +++ b/app/src/components/navigation/UserMenu.tsx @@ -18,6 +18,7 @@ import { useAuth } from '../../contexts/AuthContext'; import AccountSettings from '../settings/account/AccountSettings'; import AdminDashboard from '../settings/admin/AdminDashboard'; import { UserRole } from '@/types/models'; +import { getHoverStyle } from '@/utils/themeStyles'; const UserMenu: React.FC = () => { const [accountSettingsOpened, setAccountSettingsOpened] = @@ -75,15 +76,7 @@ const UserMenu: React.FC = () => { }} px="sm" py="xs" - style={(theme: any) => ({ - borderRadius: theme.radius.sm, - '&:hover': { - backgroundColor: - theme.colorScheme === 'dark' - ? theme.colors.dark[5] - : theme.colors.gray[0], - }, - })} + style={(theme) => getHoverStyle(theme)} > @@ -99,15 +92,7 @@ const UserMenu: React.FC = () => { }} px="sm" py="xs" - style={(theme: any) => ({ - borderRadius: theme.radius.sm, - '&:hover': { - backgroundColor: - theme.colorScheme === 'dark' - ? theme.colors.dark[5] - : theme.colors.gray[0], - }, - })} + style={(theme) => getHoverStyle(theme)} > @@ -121,15 +106,7 @@ const UserMenu: React.FC = () => { px="sm" py="xs" color="red" - style={(theme: any) => ({ - borderRadius: theme.radius.sm, - '&:hover': { - backgroundColor: - theme.colorScheme === 'dark' - ? theme.colors.dark[5] - : theme.colors.gray[0], - }, - })} + style={(theme) => getHoverStyle(theme)} > diff --git a/app/src/components/navigation/WorkspaceSwitcher.tsx b/app/src/components/navigation/WorkspaceSwitcher.tsx index 41f6fc7..a0b05b1 100644 --- a/app/src/components/navigation/WorkspaceSwitcher.tsx +++ b/app/src/components/navigation/WorkspaceSwitcher.tsx @@ -20,6 +20,10 @@ import { useModalContext } from '../../contexts/ModalContext'; import { listWorkspaces } from '../../api/workspace'; import CreateWorkspaceModal from '../modals/workspace/CreateWorkspaceModal'; import type { Workspace } from '@/types/models'; +import { + getConditionalColor, + getWorkspacePaperStyle, +} from '@/utils/themeStyles'; const WorkspaceSwitcher: React.FC = () => { const { currentWorkspace, switchWorkspace } = useWorkspace(); @@ -111,18 +115,9 @@ const WorkspaceSwitcher: React.FC = () => { key={workspace.name} p="xs" withBorder - style={(theme: any) => ({ - backgroundColor: isSelected - ? theme.colors.blue[ - theme.colorScheme === 'dark' ? 8 : 1 - ] - : undefined, - borderColor: isSelected - ? theme.colors.blue[ - theme.colorScheme === 'dark' ? 7 : 5 - ] - : undefined, - })} + style={(theme) => + getWorkspacePaperStyle(theme, isSelected) + } > { size="sm" fw={500} truncate - c={ - isSelected - ? (theme as any).colors.blue[ - (theme as any).colorScheme === 'dark' - ? 0 - : 9 - ] - : undefined - } + c={isSelected ? 'blue' : 'inherit'} > {workspace.name} {new Date( workspace.createdAt @@ -170,11 +151,7 @@ const WorkspaceSwitcher: React.FC = () => { { e.stopPropagation(); setSettingsModalVisible(true); diff --git a/app/src/components/settings/account/AccountSettings.tsx b/app/src/components/settings/account/AccountSettings.tsx index 8fc9e0f..5464429 100644 --- a/app/src/components/settings/account/AccountSettings.tsx +++ b/app/src/components/settings/account/AccountSettings.tsx @@ -22,6 +22,7 @@ import { type SettingsAction, SettingsActionType, } from '@/types/models'; +import { getAccordionStyles } from '@/utils/themeStyles'; interface AccountSettingsProps { opened: boolean; @@ -202,25 +203,7 @@ const AccountSettings: React.FC = ({ ({ - control: { - paddingTop: theme.spacing.md, - paddingBottom: theme.spacing.md, - }, - item: { - borderBottom: `1px solid ${ - theme.colorScheme === 'dark' - ? theme.colors.dark[4] - : theme.colors.gray[3] - }`, - '&[data-active]': { - backgroundColor: - theme.colorScheme === 'dark' - ? theme.colors.dark[7] - : theme.colors.gray[0], - }, - }, - })} + styles={(theme) => getAccordionStyles(theme)} > Profile diff --git a/app/src/components/settings/workspace/WorkspaceSettings.tsx b/app/src/components/settings/workspace/WorkspaceSettings.tsx index f4065d4..1efcb85 100644 --- a/app/src/components/settings/workspace/WorkspaceSettings.tsx +++ b/app/src/components/settings/workspace/WorkspaceSettings.tsx @@ -23,6 +23,7 @@ import { type SettingsAction, SettingsActionType, } from '@/types/models'; +import { getAccordionStyles } from '@/utils/themeStyles'; // State and reducer for workspace settings interface WorkspaceSettingsState { localSettings: Partial; @@ -157,24 +158,8 @@ const WorkspaceSettings: React.FC = () => { ({ - control: { - paddingTop: theme.spacing.md, - paddingBottom: theme.spacing.md, - }, - item: { - borderBottom: `1px solid ${ - theme.colorScheme === 'dark' - ? theme.colors.dark[4] - : theme.colors.gray[3] - }`, - '&[data-active]': { - backgroundColor: - theme.colorScheme === 'dark' - ? theme.colors.dark[7] - : theme.colors.gray[0], - }, - }, + styles={(theme) => ({ + ...getAccordionStyles(theme), chevron: { '&[data-rotate]': { transform: 'rotate(180deg)', diff --git a/app/src/utils/themeStyles.ts b/app/src/utils/themeStyles.ts new file mode 100644 index 0000000..799c5d8 --- /dev/null +++ b/app/src/utils/themeStyles.ts @@ -0,0 +1,80 @@ +import type { MantineTheme } from '@mantine/core'; + +// For type safety - this property exists on the MantineTheme but may not be in types +interface ThemeWithColorScheme extends MantineTheme { + colorScheme?: 'dark' | 'light'; +} + +// Type-safe hover style function for unstyledButton and similar components +export const getHoverStyle = (theme: MantineTheme) => ({ + borderRadius: theme.radius.sm, + '&:hover': { + backgroundColor: + (theme as ThemeWithColorScheme).colorScheme === 'dark' + ? theme.colors.dark[5] + : theme.colors.gray[0], + }, +}); + +// Type-safe color function for text or components that need conditional colors +export const getConditionalColor = ( + theme: MantineTheme, + isSelected = false +) => { + if (isSelected) { + return (theme as ThemeWithColorScheme).colorScheme === 'dark' + ? theme.colors.blue[2] + : theme.colors.blue[7]; + } + return 'dimmed'; +}; + +// Helper for accordion styling +export const getAccordionStyles = (theme: MantineTheme) => ({ + control: { + paddingTop: theme.spacing.md, + paddingBottom: theme.spacing.md, + }, + item: { + borderBottom: `1px solid ${ + (theme as ThemeWithColorScheme).colorScheme === 'dark' + ? theme.colors.dark[4] + : theme.colors.gray[3] + }`, + '&[data-active]': { + backgroundColor: + (theme as ThemeWithColorScheme).colorScheme === 'dark' + ? theme.colors.dark[7] + : theme.colors.gray[0], + }, + }, +}); + +// Helper for workspace paper styling +export const getWorkspacePaperStyle = ( + theme: MantineTheme, + isSelected: boolean +) => ({ + backgroundColor: isSelected + ? theme.colors.blue[ + (theme as ThemeWithColorScheme).colorScheme === 'dark' ? 8 : 1 + ] + : undefined, + borderColor: isSelected + ? theme.colors.blue[ + (theme as ThemeWithColorScheme).colorScheme === 'dark' ? 7 : 5 + ] + : undefined, +}); + +// Helper for text color based on theme and selection +export const getTextColor = ( + theme: MantineTheme, + isSelected: boolean +): string | null => { + if (!isSelected) return null; + + return (theme as ThemeWithColorScheme).colorScheme === 'dark' + ? theme.colors.blue[0] + : theme.colors.blue[9]; +};