Sprint 5: User profile menu, preferences, org member management, v0.2.0

- Move Settings from sidebar Admin section to User Profile dropdown menu
- Add User Preferences page (placeholder for future: dark mode, timezone,
  notifications, feature visibility)
- Add Manage Members page for tenant admins to invite/manage board members:
  - List all org members with roles, status, join date, last login
  - Add new members (creates user account + org membership)
  - Change member roles (president, treasurer, secretary, board member,
    property manager, viewer)
  - Remove members (soft-deactivate)
  - Role-gated: only president, admin, treasurer can manage members
- Backend: new org member management endpoints on OrganizationsController
  - GET /organizations/members
  - POST /organizations/members
  - PUT /organizations/members/:id/role
  - DELETE /organizations/members/:id
- Bump version to 0.2.0 MVP_P2 (package.json + Settings page)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-22 16:39:17 -05:00
parent b5861de609
commit ea49b91bb3
10 changed files with 756 additions and 13 deletions

View File

@@ -4,6 +4,9 @@ import {
IconLogout,
IconSwitchHorizontal,
IconChevronDown,
IconSettings,
IconUserCog,
IconUsersGroup,
} from '@tabler/icons-react';
import { Outlet, useNavigate } from 'react-router-dom';
import { useAuthStore } from '../../stores/authStore';
@@ -20,6 +23,9 @@ export function AppLayout() {
navigate('/login');
};
// Tenant admins (president role) can manage org members
const isTenantAdmin = currentOrg?.role === 'president' || currentOrg?.role === 'admin';
return (
<AppShell
header={{ height: 60 }}
@@ -36,7 +42,7 @@ export function AppLayout() {
{currentOrg && (
<Text size="sm" c="dimmed">{currentOrg.name}</Text>
)}
<Menu shadow="md" width={200}>
<Menu shadow="md" width={220}>
<Menu.Target>
<UnstyledButton>
<Group gap="xs">
@@ -49,6 +55,28 @@ export function AppLayout() {
</UnstyledButton>
</Menu.Target>
<Menu.Dropdown>
<Menu.Label>Account</Menu.Label>
<Menu.Item
leftSection={<IconUserCog size={14} />}
onClick={() => navigate('/preferences')}
>
User Preferences
</Menu.Item>
<Menu.Item
leftSection={<IconSettings size={14} />}
onClick={() => navigate('/settings')}
>
Settings
</Menu.Item>
{isTenantAdmin && (
<Menu.Item
leftSection={<IconUsersGroup size={14} />}
onClick={() => navigate('/org-members')}
>
Manage Members
</Menu.Item>
)}
<Menu.Divider />
<Menu.Item
leftSection={<IconSwitchHorizontal size={14} />}
onClick={() => navigate('/select-org')}

View File

@@ -12,7 +12,6 @@ import {
IconShieldCheck,
IconBuildingBank,
IconUsers,
IconSettings,
IconCrown,
IconCategory,
IconChartAreaLine,
@@ -74,12 +73,6 @@ const navSections = [
},
],
},
{
label: 'Admin',
items: [
{ label: 'Settings', icon: IconSettings, path: '/settings' },
],
},
];
export function Sidebar() {