Refactor useAuth hook, settings components, and docs for formatting and readability improvements

- Consolidated multi-line arguments into single lines where appropriate in `useAuth`.
- Improved spacing and readability in data processing across components (`ProfileSettingsForm`, `PasswordChangeForm`, `SessionCard`).
- Applied consistent table and markdown formatting in design system docs (e.g., `README.md`, `08-ai-guidelines.md`, `00-quick-start.md`).
- Updated code snippets to ensure adherence to Prettier rules and streamlined JSX structures.
This commit is contained in:
2025-11-10 11:03:45 +01:00
parent 464a6140c4
commit 96df7edf88
208 changed files with 4056 additions and 4556 deletions

View File

@@ -21,9 +21,7 @@ jest.mock('sonner', () => ({
},
}));
const mockUseBulkUserAction = useBulkUserAction as jest.MockedFunction<
typeof useBulkUserAction
>;
const mockUseBulkUserAction = useBulkUserAction as jest.MockedFunction<typeof useBulkUserAction>;
describe('BulkActionToolbar', () => {
const mockBulkActionMutate = jest.fn();
@@ -50,9 +48,7 @@ describe('BulkActionToolbar', () => {
/>
);
expect(
screen.queryByTestId('bulk-action-toolbar')
).not.toBeInTheDocument();
expect(screen.queryByTestId('bulk-action-toolbar')).not.toBeInTheDocument();
});
it('renders when one user is selected', () => {
@@ -163,9 +159,7 @@ describe('BulkActionToolbar', () => {
/>
);
expect(
screen.getByRole('button', { name: /Activate/ })
).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Activate/ })).toBeInTheDocument();
});
it('renders deactivate button', () => {
@@ -177,9 +171,7 @@ describe('BulkActionToolbar', () => {
/>
);
expect(
screen.getByRole('button', { name: /Deactivate/ })
).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Deactivate/ })).toBeInTheDocument();
});
it('renders delete button', () => {
@@ -191,9 +183,7 @@ describe('BulkActionToolbar', () => {
/>
);
expect(
screen.getByRole('button', { name: /Delete/ })
).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Delete/ })).toBeInTheDocument();
});
it('disables buttons when action is pending', () => {
@@ -258,9 +248,7 @@ describe('BulkActionToolbar', () => {
await waitFor(() => {
expect(screen.getByText('Activate Users')).toBeInTheDocument();
expect(
screen.getByText(/Are you sure you want to activate 3 users\?/)
).toBeInTheDocument();
expect(screen.getByText(/Are you sure you want to activate 3 users\?/)).toBeInTheDocument();
});
});
@@ -302,9 +290,7 @@ describe('BulkActionToolbar', () => {
await waitFor(() => {
expect(screen.getByText('Delete Users')).toBeInTheDocument();
expect(
screen.getByText(/Are you sure you want to delete 5 users\?/)
).toBeInTheDocument();
expect(screen.getByText(/Are you sure you want to delete 5 users\?/)).toBeInTheDocument();
});
});
@@ -322,9 +308,7 @@ describe('BulkActionToolbar', () => {
await user.click(activateButton);
await waitFor(() => {
expect(
screen.getByText(/Are you sure you want to activate 1 user\?/)
).toBeInTheDocument();
expect(screen.getByText(/Are you sure you want to activate 1 user\?/)).toBeInTheDocument();
});
});
});
@@ -372,9 +356,7 @@ describe('BulkActionToolbar', () => {
/>
);
expect(
screen.queryByTestId('bulk-action-toolbar')
).not.toBeInTheDocument();
expect(screen.queryByTestId('bulk-action-toolbar')).not.toBeInTheDocument();
});
it('handles large selection counts', () => {
@@ -382,9 +364,7 @@ describe('BulkActionToolbar', () => {
<BulkActionToolbar
selectedCount={100}
onClearSelection={mockOnClearSelection}
selectedUserIds={Array.from({ length: 100 }, (_, i) =>
String(i + 1)
)}
selectedUserIds={Array.from({ length: 100 }, (_, i) => String(i + 1))}
/>
);

View File

@@ -28,12 +28,8 @@ jest.mock('sonner', () => ({
},
}));
const mockUseActivateUser = useActivateUser as jest.MockedFunction<
typeof useActivateUser
>;
const mockUseDeactivateUser = useDeactivateUser as jest.MockedFunction<
typeof useDeactivateUser
>;
const mockUseActivateUser = useActivateUser as jest.MockedFunction<typeof useActivateUser>;
const mockUseDeactivateUser = useDeactivateUser as jest.MockedFunction<typeof useDeactivateUser>;
const mockUseDeleteUser = useDeleteUser as jest.MockedFunction<typeof useDeleteUser>;
describe('UserActionMenu', () => {
@@ -76,13 +72,7 @@ describe('UserActionMenu', () => {
describe('Menu Rendering', () => {
it('renders menu trigger button', () => {
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -92,13 +82,7 @@ describe('UserActionMenu', () => {
it('shows menu items when opened', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -110,13 +94,7 @@ describe('UserActionMenu', () => {
it('shows deactivate option for active user', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -131,13 +109,7 @@ describe('UserActionMenu', () => {
const user = userEvent.setup();
const inactiveUser = { ...mockUser, is_active: false };
render(
<UserActionMenu
user={inactiveUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -150,13 +122,7 @@ describe('UserActionMenu', () => {
it('shows delete option', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -172,13 +138,7 @@ describe('UserActionMenu', () => {
const user = userEvent.setup();
const mockOnEdit = jest.fn();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={mockOnEdit}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={mockOnEdit} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -195,13 +155,7 @@ describe('UserActionMenu', () => {
const user = userEvent.setup();
const mockOnEdit = jest.fn();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={mockOnEdit}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={mockOnEdit} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -222,13 +176,7 @@ describe('UserActionMenu', () => {
const user = userEvent.setup();
const inactiveUser = { ...mockUser, is_active: false };
render(
<UserActionMenu
user={inactiveUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -247,13 +195,7 @@ describe('UserActionMenu', () => {
const user = userEvent.setup();
const inactiveUser = { ...mockUser, is_active: false };
render(
<UserActionMenu
user={inactiveUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -264,9 +206,7 @@ describe('UserActionMenu', () => {
await user.click(activateButton);
await waitFor(() => {
expect(toast.success).toHaveBeenCalledWith(
'Test User has been activated successfully.'
);
expect(toast.success).toHaveBeenCalledWith('Test User has been activated successfully.');
});
});
@@ -276,13 +216,7 @@ describe('UserActionMenu', () => {
mockActivateMutate.mockRejectedValueOnce(new Error('Network error'));
render(
<UserActionMenu
user={inactiveUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -302,13 +236,7 @@ describe('UserActionMenu', () => {
it('shows confirmation dialog when deactivate is clicked', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -320,22 +248,14 @@ describe('UserActionMenu', () => {
expect(screen.getByText('Deactivate User')).toBeInTheDocument();
expect(
screen.getByText(
/Are you sure you want to deactivate Test User\?/
)
screen.getByText(/Are you sure you want to deactivate Test User\?/)
).toBeInTheDocument();
});
it('shows confirmation dialog when deactivate is clicked', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -357,13 +277,7 @@ describe('UserActionMenu', () => {
it('disables deactivate option for current user', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={true}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={true} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -380,13 +294,7 @@ describe('UserActionMenu', () => {
it('shows confirmation dialog when delete is clicked', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -397,24 +305,14 @@ describe('UserActionMenu', () => {
await user.click(deleteButton);
expect(screen.getByText('Delete User')).toBeInTheDocument();
expect(
screen.getByText(/Are you sure you want to delete Test User\?/)
).toBeInTheDocument();
expect(
screen.getByText(/This action cannot be undone\./)
).toBeInTheDocument();
expect(screen.getByText(/Are you sure you want to delete Test User\?/)).toBeInTheDocument();
expect(screen.getByText(/This action cannot be undone\./)).toBeInTheDocument();
});
it('deletes user when confirmed', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -435,13 +333,7 @@ describe('UserActionMenu', () => {
it('cancels deletion when cancel is clicked', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -460,13 +352,7 @@ describe('UserActionMenu', () => {
it('shows success toast on deletion', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -480,22 +366,14 @@ describe('UserActionMenu', () => {
await user.click(confirmButton);
await waitFor(() => {
expect(toast.success).toHaveBeenCalledWith(
'Test User has been deleted successfully.'
);
expect(toast.success).toHaveBeenCalledWith('Test User has been deleted successfully.');
});
});
it('disables delete option for current user', async () => {
const user = userEvent.setup();
render(
<UserActionMenu
user={mockUser}
isCurrentUser={true}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={true} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -510,13 +388,7 @@ describe('UserActionMenu', () => {
describe('User Name Display', () => {
it('displays full name when last name is provided', async () => {
render(
<UserActionMenu
user={mockUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={mockUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -528,11 +400,7 @@ describe('UserActionMenu', () => {
const userWithoutLastName = { ...mockUser, last_name: null };
render(
<UserActionMenu
user={userWithoutLastName}
isCurrentUser={false}
onEdit={jest.fn()}
/>
<UserActionMenu user={userWithoutLastName} isCurrentUser={false} onEdit={jest.fn()} />
);
const menuButton = screen.getByRole('button', {
@@ -549,13 +417,7 @@ describe('UserActionMenu', () => {
mockActivateMutate.mockRejectedValueOnce(new Error('Custom error'));
render(
<UserActionMenu
user={inactiveUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',
@@ -576,13 +438,7 @@ describe('UserActionMenu', () => {
mockActivateMutate.mockRejectedValueOnce('String error');
render(
<UserActionMenu
user={inactiveUser}
isCurrentUser={false}
onEdit={jest.fn()}
/>
);
render(<UserActionMenu user={inactiveUser} isCurrentUser={false} onEdit={jest.fn()} />);
const menuButton = screen.getByRole('button', {
name: 'Actions for Test User',

View File

@@ -11,9 +11,7 @@ import type { User, PaginationMeta } from '@/lib/api/hooks/useAdmin';
// Mock UserActionMenu component
jest.mock('@/components/admin/users/UserActionMenu', () => ({
UserActionMenu: ({ user, isCurrentUser }: any) => (
<button data-testid={`action-menu-${user.id}`}>
Actions {isCurrentUser && '(current)'}
</button>
<button data-testid={`action-menu-${user.id}`}>Actions {isCurrentUser && '(current)'}</button>
),
}));
@@ -139,25 +137,15 @@ describe('UserListTable', () => {
describe('Empty State', () => {
it('shows empty message when no users', () => {
render(
<UserListTable
{...defaultProps}
users={[]}
pagination={{ ...mockPagination, total: 0 }}
/>
<UserListTable {...defaultProps} users={[]} pagination={{ ...mockPagination, total: 0 }} />
);
expect(
screen.getByText('No users found. Try adjusting your filters.')
).toBeInTheDocument();
expect(screen.getByText('No users found. Try adjusting your filters.')).toBeInTheDocument();
});
it('does not render pagination when no users', () => {
render(
<UserListTable
{...defaultProps}
users={[]}
pagination={{ ...mockPagination, total: 0 }}
/>
<UserListTable {...defaultProps} users={[]} pagination={{ ...mockPagination, total: 0 }} />
);
expect(screen.queryByText(/Showing/)).not.toBeInTheDocument();
@@ -168,9 +156,7 @@ describe('UserListTable', () => {
it('renders search input', () => {
render(<UserListTable {...defaultProps} />);
const searchInput = screen.getByPlaceholderText(
'Search by name or email...'
);
const searchInput = screen.getByPlaceholderText('Search by name or email...');
expect(searchInput).toBeInTheDocument();
});
@@ -178,9 +164,7 @@ describe('UserListTable', () => {
const user = userEvent.setup();
render(<UserListTable {...defaultProps} />);
const searchInput = screen.getByPlaceholderText(
'Search by name or email...'
);
const searchInput = screen.getByPlaceholderText('Search by name or email...');
await user.type(searchInput, 'alice');
@@ -222,8 +206,8 @@ describe('UserListTable', () => {
// Find "All Users" in the filter dropdown (not the heading)
const selectTriggers = screen.getAllByRole('combobox');
const userTypeFilter = selectTriggers.find(trigger =>
within(trigger).queryByText('All Users') !== null
const userTypeFilter = selectTriggers.find(
(trigger) => within(trigger).queryByText('All Users') !== null
);
expect(userTypeFilter).toBeInTheDocument();
@@ -298,11 +282,7 @@ describe('UserListTable', () => {
it('disables select all checkbox when no users', () => {
render(
<UserListTable
{...defaultProps}
users={[]}
pagination={{ ...mockPagination, total: 0 }}
/>
<UserListTable {...defaultProps} users={[]} pagination={{ ...mockPagination, total: 0 }} />
);
const selectAllCheckbox = screen.getByLabelText('Select all users');
@@ -433,11 +413,7 @@ describe('UserListTable', () => {
it('does not render pagination when no users', () => {
render(
<UserListTable
{...defaultProps}
users={[]}
pagination={{ ...mockPagination, total: 0 }}
/>
<UserListTable {...defaultProps} users={[]} pagination={{ ...mockPagination, total: 0 }} />
);
expect(screen.queryByText(/Showing/)).not.toBeInTheDocument();

View File

@@ -36,9 +36,7 @@ jest.mock('@/lib/api/hooks/useAdmin', () => ({
jest.mock('@/components/admin/users/UserListTable', () => ({
UserListTable: ({ onEditUser, onSelectUser, selectedUsers }: any) => (
<div data-testid="user-list-table">
<button onClick={() => onEditUser({ id: '1', first_name: 'Test' })}>
Edit User
</button>
<button onClick={() => onEditUser({ id: '1', first_name: 'Test' })}>Edit User</button>
<button onClick={() => onSelectUser('1')}>Select User 1</button>
<div data-testid="selected-count">{selectedUsers.length}</div>
</div>
@@ -67,13 +65,9 @@ jest.mock('@/components/admin/users/BulkActionToolbar', () => ({
}));
const mockUseRouter = useRouter as jest.MockedFunction<typeof useRouter>;
const mockUseSearchParams = useSearchParams as jest.MockedFunction<
typeof useSearchParams
>;
const mockUseSearchParams = useSearchParams as jest.MockedFunction<typeof useSearchParams>;
const mockUseAuth = useAuth as jest.MockedFunction<typeof useAuth>;
const mockUseAdminUsers = useAdminUsers as jest.MockedFunction<
typeof useAdminUsers
>;
const mockUseAdminUsers = useAdminUsers as jest.MockedFunction<typeof useAdminUsers>;
// Import mutation hooks for mocking
const {
@@ -207,9 +201,7 @@ describe('UserManagementContent', () => {
});
const renderWithProviders = (ui: React.ReactElement) => {
return render(
<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>
);
return render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>);
};
describe('Component Rendering', () => {
@@ -217,17 +209,13 @@ describe('UserManagementContent', () => {
renderWithProviders(<UserManagementContent />);
expect(screen.getByText('All Users')).toBeInTheDocument();
expect(
screen.getByText('Manage user accounts and permissions')
).toBeInTheDocument();
expect(screen.getByText('Manage user accounts and permissions')).toBeInTheDocument();
});
it('renders create user button', () => {
renderWithProviders(<UserManagementContent />);
expect(
screen.getByRole('button', { name: /Create User/i })
).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Create User/i })).toBeInTheDocument();
});
it('renders UserListTable component', () => {
@@ -239,17 +227,13 @@ describe('UserManagementContent', () => {
it('does not render dialog initially', () => {
renderWithProviders(<UserManagementContent />);
expect(
screen.queryByTestId('user-form-dialog')
).not.toBeInTheDocument();
expect(screen.queryByTestId('user-form-dialog')).not.toBeInTheDocument();
});
it('does not render bulk toolbar initially', () => {
renderWithProviders(<UserManagementContent />);
expect(
screen.queryByTestId('bulk-action-toolbar')
).not.toBeInTheDocument();
expect(screen.queryByTestId('bulk-action-toolbar')).not.toBeInTheDocument();
});
});
@@ -280,9 +264,7 @@ describe('UserManagementContent', () => {
await user.click(closeButton);
await waitFor(() => {
expect(
screen.queryByTestId('user-form-dialog')
).not.toBeInTheDocument();
expect(screen.queryByTestId('user-form-dialog')).not.toBeInTheDocument();
});
});
});
@@ -311,9 +293,7 @@ describe('UserManagementContent', () => {
await user.click(closeButton);
await waitFor(() => {
expect(
screen.queryByTestId('user-form-dialog')
).not.toBeInTheDocument();
expect(screen.queryByTestId('user-form-dialog')).not.toBeInTheDocument();
});
});
});
@@ -338,9 +318,7 @@ describe('UserManagementContent', () => {
await waitFor(() => {
expect(screen.getByTestId('bulk-action-toolbar')).toBeInTheDocument();
expect(screen.getByTestId('bulk-selected-count')).toHaveTextContent(
'1'
);
expect(screen.getByTestId('bulk-selected-count')).toHaveTextContent('1');
});
});
@@ -362,9 +340,7 @@ describe('UserManagementContent', () => {
await waitFor(() => {
expect(screen.getByTestId('selected-count')).toHaveTextContent('0');
expect(
screen.queryByTestId('bulk-action-toolbar')
).not.toBeInTheDocument();
expect(screen.queryByTestId('bulk-action-toolbar')).not.toBeInTheDocument();
});
});
@@ -521,9 +497,7 @@ describe('UserManagementContent', () => {
await user.click(selectButton);
await waitFor(() => {
expect(screen.getByTestId('bulk-selected-count')).toHaveTextContent(
'1'
);
expect(screen.getByTestId('bulk-selected-count')).toHaveTextContent('1');
});
});
});