Files
HOA_Financial_Platform/frontend/src/pages/settings/SettingsPage.tsx
olsch01 af68304692 feat: sidebar reorg, compact view preference, and UI polish
- Remove redundant Settings link from sidebar (accessible via user menu)
- Move Transactions section below Board Reference for better grouping
- Promote Investment Scenarios to its own top-level sidebar item
- Add Compact View preference with tighter spacing theme
- Wire compact theme into MantineProvider with dynamic switching
- Enable Compact View toggle in both Preferences and Settings pages
- Install missing @simplewebauthn/browser package (lock file update)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 06:39:41 -04:00

195 lines
6.7 KiB
TypeScript

import { useState } from 'react';
import {
Title, Text, Card, Stack, Group, SimpleGrid, Badge, ThemeIcon, Divider,
Tabs, Button, Switch,
} from '@mantine/core';
import {
IconBuilding, IconUser, IconSettings, IconShieldLock,
IconFingerprint, IconLink, IconLogout,
} from '@tabler/icons-react';
import { notifications } from '@mantine/notifications';
import { useAuthStore } from '../../stores/authStore';
import { usePreferencesStore } from '../../stores/preferencesStore';
import { MfaSettings } from './MfaSettings';
import { PasskeySettings } from './PasskeySettings';
import { LinkedAccounts } from './LinkedAccounts';
import api from '../../services/api';
export function SettingsPage() {
const { user, currentOrg } = useAuthStore();
const { compactView, toggleCompactView } = usePreferencesStore();
const [loggingOutAll, setLoggingOutAll] = useState(false);
const handleLogoutEverywhere = async () => {
setLoggingOutAll(true);
try {
await api.post('/auth/logout-everywhere');
notifications.show({ message: 'All other sessions have been logged out', color: 'green' });
} catch {
notifications.show({ message: 'Failed to log out other sessions', color: 'red' });
} finally {
setLoggingOutAll(false);
}
};
return (
<Stack>
<div>
<Title order={2}>Settings</Title>
<Text c="dimmed" size="sm">Organization and account settings</Text>
</div>
<SimpleGrid cols={{ base: 1, md: 2 }}>
{/* Organization Info */}
<Card withBorder padding="lg">
<Group mb="md">
<ThemeIcon color="blue" variant="light" size={40} radius="md">
<IconBuilding size={24} />
</ThemeIcon>
<div>
<Text fw={600} size="lg">Organization</Text>
<Text c="dimmed" size="sm">Current organization details</Text>
</div>
</Group>
<Stack gap="xs">
<Group justify="space-between">
<Text size="sm" c="dimmed">Name</Text>
<Text size="sm" fw={500}>{currentOrg?.name || 'N/A'}</Text>
</Group>
<Group justify="space-between">
<Text size="sm" c="dimmed">Your Role</Text>
<Badge variant="light">{currentOrg?.role || 'N/A'}</Badge>
</Group>
</Stack>
</Card>
{/* User Profile */}
<Card withBorder padding="lg">
<Group mb="md">
<ThemeIcon color="green" variant="light" size={40} radius="md">
<IconUser size={24} />
</ThemeIcon>
<div>
<Text fw={600} size="lg">Your Profile</Text>
<Text c="dimmed" size="sm">Account information</Text>
</div>
</Group>
<Stack gap="xs">
<Group justify="space-between">
<Text size="sm" c="dimmed">Name</Text>
<Text size="sm" fw={500}>{user?.firstName} {user?.lastName}</Text>
</Group>
<Group justify="space-between">
<Text size="sm" c="dimmed">Email</Text>
<Text size="sm" fw={500}>{user?.email}</Text>
</Group>
<Group justify="space-between">
<Text size="sm" c="dimmed">User ID</Text>
<Text size="sm" ff="monospace" c="dimmed">{user?.id?.slice(0, 8)}...</Text>
</Group>
</Stack>
</Card>
{/* System Info */}
<Card withBorder padding="lg">
<Group mb="md">
<ThemeIcon color="violet" variant="light" size={40} radius="md">
<IconSettings size={24} />
</ThemeIcon>
<div>
<Text fw={600} size="lg">System</Text>
<Text c="dimmed" size="sm">Platform information</Text>
</div>
</Group>
<Stack gap="xs">
<Group justify="space-between">
<Text size="sm" c="dimmed">Platform</Text>
<Text size="sm" fw={500}>HOA LedgerIQ</Text>
</Group>
<Group justify="space-between">
<Text size="sm" c="dimmed">Version</Text>
<Badge variant="light">2026.03.17</Badge>
</Group>
<Group justify="space-between">
<Text size="sm" c="dimmed">API</Text>
<Text size="sm" ff="monospace" c="dimmed">/api/docs</Text>
</Group>
<Divider />
<Group justify="space-between">
<div>
<Text size="sm">Compact View</Text>
<Text size="xs" c="dimmed">Reduce spacing in tables and lists</Text>
</div>
<Switch checked={compactView} onChange={toggleCompactView} />
</Group>
</Stack>
</Card>
{/* Sessions */}
<Card withBorder padding="lg">
<Group mb="md">
<ThemeIcon color="orange" variant="light" size={40} radius="md">
<IconLogout size={24} />
</ThemeIcon>
<div>
<Text fw={600} size="lg">Sessions</Text>
<Text c="dimmed" size="sm">Manage active sessions</Text>
</div>
</Group>
<Stack gap="xs">
<Group justify="space-between">
<Text size="sm" c="dimmed">Current Session</Text>
<Badge color="green" variant="light">Active</Badge>
</Group>
<Button
variant="light"
color="orange"
size="sm"
leftSection={<IconLogout size={16} />}
onClick={handleLogoutEverywhere}
loading={loggingOutAll}
mt="xs"
>
Log Out All Other Sessions
</Button>
</Stack>
</Card>
</SimpleGrid>
<Divider my="md" />
{/* Security Settings */}
<div>
<Title order={3} mb="sm">Security</Title>
<Text c="dimmed" size="sm" mb="md">Manage authentication methods and security settings</Text>
</div>
<Tabs defaultValue="mfa">
<Tabs.List>
<Tabs.Tab value="mfa" leftSection={<IconShieldLock size={16} />}>
Two-Factor Auth
</Tabs.Tab>
<Tabs.Tab value="passkeys" leftSection={<IconFingerprint size={16} />}>
Passkeys
</Tabs.Tab>
<Tabs.Tab value="linked" leftSection={<IconLink size={16} />}>
Linked Accounts
</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="mfa" pt="md">
<MfaSettings />
</Tabs.Panel>
<Tabs.Panel value="passkeys" pt="md">
<PasskeySettings />
</Tabs.Panel>
<Tabs.Panel value="linked" pt="md">
<LinkedAccounts />
</Tabs.Panel>
</Tabs>
</Stack>
);
}