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

@@ -114,7 +114,10 @@ async function refreshAccessToken(): Promise<string> {
// Only redirect to login when not already on an auth route
if (typeof window !== 'undefined') {
const currentPath = window.location.pathname;
const onAuthRoute = currentPath === '/login' || currentPath === '/register' || currentPath.startsWith('/password-reset');
const onAuthRoute =
currentPath === '/login' ||
currentPath === '/register' ||
currentPath.startsWith('/password-reset');
if (!onAuthRoute) {
const returnUrl = currentPath ? `?returnUrl=${encodeURIComponent(currentPath)}` : '';
window.location.href = `/login${returnUrl}`;
@@ -144,7 +147,12 @@ client.instance.interceptors.request.use(
// Do not attach Authorization header for auth endpoints
const url = requestConfig.url || '';
const isAuthEndpoint = url.includes('/auth/login') || url.includes('/auth/register') || url.includes('/auth/refresh') || url.includes('/auth/password') || url.includes('/password');
const isAuthEndpoint =
url.includes('/auth/login') ||
url.includes('/auth/register') ||
url.includes('/auth/refresh') ||
url.includes('/auth/password') ||
url.includes('/password');
// Add Authorization header if token exists and not hitting auth endpoints
if (accessToken && requestConfig.headers && !isAuthEndpoint) {
@@ -188,7 +196,11 @@ client.instance.interceptors.response.use(
// Handle 401 Unauthorized - Token expired
if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
const url = originalRequest.url || '';
const isAuthEndpoint = url.includes('/auth/login') || url.includes('/auth/register') || url.includes('/auth/password') || url.includes('/password');
const isAuthEndpoint =
url.includes('/auth/login') ||
url.includes('/auth/register') ||
url.includes('/auth/password') ||
url.includes('/password');
// If the 401 is from auth endpoints, do not attempt refresh
if (isAuthEndpoint) {

View File

@@ -15,48 +15,48 @@ export interface APIErrorResponse {
// Error code to user-friendly message mapping
export const ERROR_MESSAGES: Record<string, string> = {
// Authentication errors (AUTH_xxx)
'AUTH_001': 'Invalid email or password',
'AUTH_002': 'Account is inactive',
'AUTH_003': 'Invalid or expired token',
'AUTH_004': 'Session expired. Please login again',
AUTH_001: 'Invalid email or password',
AUTH_002: 'Account is inactive',
AUTH_003: 'Invalid or expired token',
AUTH_004: 'Session expired. Please login again',
// User errors (USER_xxx)
'USER_001': 'User not found',
'USER_002': 'This email is already registered',
'USER_003': 'Invalid user data',
'USER_004': 'Cannot delete your own account',
USER_001: 'User not found',
USER_002: 'This email is already registered',
USER_003: 'Invalid user data',
USER_004: 'Cannot delete your own account',
// Validation errors (VAL_xxx)
'VAL_001': 'Invalid input. Please check your data',
'VAL_002': 'Email format is invalid',
'VAL_003': 'Password does not meet requirements',
'VAL_004': 'Required field is missing',
VAL_001: 'Invalid input. Please check your data',
VAL_002: 'Email format is invalid',
VAL_003: 'Password does not meet requirements',
VAL_004: 'Required field is missing',
// Organization errors (ORG_xxx)
'ORG_001': 'Organization name already exists',
'ORG_002': 'Organization not found',
'ORG_003': 'Cannot delete organization with members',
ORG_001: 'Organization name already exists',
ORG_002: 'Organization not found',
ORG_003: 'Cannot delete organization with members',
// Permission errors (PERM_xxx)
'PERM_001': 'Insufficient permissions',
'PERM_002': 'Admin access required',
'PERM_003': 'Cannot perform this action',
PERM_001: 'Insufficient permissions',
PERM_002: 'Admin access required',
PERM_003: 'Cannot perform this action',
// Rate limiting (RATE_xxx)
'RATE_001': 'Too many requests. Please try again later',
RATE_001: 'Too many requests. Please try again later',
// Session errors (SESSION_xxx)
'SESSION_001': 'Session not found',
'SESSION_002': 'Cannot revoke current session',
'SESSION_003': 'Session expired',
SESSION_001: 'Session not found',
SESSION_002: 'Cannot revoke current session',
SESSION_003: 'Session expired',
// Generic errors
'NETWORK_ERROR': 'Network error. Please check your connection',
'SERVER_ERROR': 'A server error occurred. Please try again later',
'UNKNOWN': 'An unexpected error occurred',
'FORBIDDEN': "You don't have permission to perform this action",
'NOT_FOUND': 'The requested resource was not found',
'RATE_LIMIT': 'Too many requests. Please slow down',
NETWORK_ERROR: 'Network error. Please check your connection',
SERVER_ERROR: 'A server error occurred. Please try again later',
UNKNOWN: 'An unexpected error occurred',
FORBIDDEN: "You don't have permission to perform this action",
NOT_FOUND: 'The requested resource was not found',
RATE_LIMIT: 'Too many requests. Please slow down',
};
/**
@@ -150,7 +150,8 @@ export function parseAPIError(error: unknown): APIError[] {
return [
{
code: 'SERVER_ERROR',
message: ERROR_MESSAGES['SERVER_ERROR'] || 'A server error occurred. Please try again later',
message:
ERROR_MESSAGES['SERVER_ERROR'] || 'A server error occurred. Please try again later',
},
];
}

View File

@@ -11,31 +11,31 @@
'use client';
import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import {
type AddMemberRequest,
adminActivateUser,
adminAddOrganizationMember,
adminBulkUserAction,
adminCreateOrganization,
adminCreateUser,
adminDeactivateUser,
adminDeleteOrganization,
adminDeleteUser,
adminGetOrganization,
adminListOrganizationMembers,
adminListOrganizations,
adminListSessions,
adminListUsers,
adminRemoveOrganizationMember,
adminUpdateOrganization,
adminUpdateUser,
type OrganizationCreate,
type OrganizationUpdate,
type UserCreate,
type UserUpdate,
type AddMemberRequest,
adminActivateUser,
adminAddOrganizationMember,
adminBulkUserAction,
adminCreateOrganization,
adminCreateUser,
adminDeactivateUser,
adminDeleteOrganization,
adminDeleteUser,
adminGetOrganization,
adminListOrganizationMembers,
adminListOrganizations,
adminListSessions,
adminListUsers,
adminRemoveOrganizationMember,
adminUpdateOrganization,
adminUpdateUser,
type OrganizationCreate,
type OrganizationUpdate,
type UserCreate,
type UserUpdate,
} from '@/lib/api/client';
import {useAuth} from '@/lib/auth/AuthContext';
import { useAuth } from '@/lib/auth/AuthContext';
/**
* Constants for admin hooks
@@ -81,7 +81,11 @@ export function useAdminStats() {
}
// Type assertion: if no error, response has data
const usersData = (usersResponse as { data: { data: Array<{ is_active: boolean }>; pagination: { total: number } } }).data;
const usersData = (
usersResponse as {
data: { data: Array<{ is_active: boolean }>; pagination: { total: number } };
}
).data;
const users = usersData?.data || [];
const totalUsers = usersData?.pagination?.total || 0;
const activeUsers = users.filter((u) => u.is_active).length;
@@ -282,13 +286,7 @@ export function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
userId,
userData,
}: {
userId: string;
userData: UserUpdate;
}) => {
mutationFn: async ({ userId, userData }: { userId: string; userData: UserUpdate }) => {
const response = await adminUpdateUser({
path: { user_id: userId },
body: userData,
@@ -509,13 +507,7 @@ export function useUpdateOrganization() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
orgId,
orgData,
}: {
orgId: string;
orgData: OrganizationUpdate;
}) => {
mutationFn: async ({ orgId, orgData }: { orgId: string; orgData: OrganizationUpdate }) => {
const response = await adminUpdateOrganization({
path: { org_id: orgId },
body: orgData,
@@ -603,11 +595,7 @@ export function useGetOrganization(orgId: string | null) {
* @param limit - Number of records per page
* @returns Paginated list of organization members
*/
export function useOrganizationMembers(
orgId: string | null,
page = 1,
limit = DEFAULT_PAGE_LIMIT
) {
export function useOrganizationMembers(orgId: string | null, page = 1, limit = DEFAULT_PAGE_LIMIT) {
const { user } = useAuth();
return useQuery({
@@ -642,13 +630,7 @@ export function useAddOrganizationMember() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
orgId,
memberData,
}: {
orgId: string;
memberData: AddMemberRequest;
}) => {
mutationFn: async ({ orgId, memberData }: { orgId: string; memberData: AddMemberRequest }) => {
const response = await adminAddOrganizationMember({
path: { org_id: orgId },
body: memberData,
@@ -681,13 +663,7 @@ export function useRemoveOrganizationMember() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async ({
orgId,
userId,
}: {
orgId: string;
userId: string;
}) => {
mutationFn: async ({ orgId, userId }: { orgId: string; userId: string }) => {
const response = await adminRemoveOrganizationMember({
path: { org_id: orgId, user_id: userId },
throwOnError: false,

View File

@@ -121,12 +121,7 @@ export function useLogin(onSuccess?: () => void) {
const { access_token, refresh_token, user, expires_in } = data;
// Update auth store with user and tokens
await setAuth(
user as User,
access_token,
refresh_token || '',
expires_in
);
await setAuth(user as User, access_token, refresh_token || '', expires_in);
// Invalidate and refetch user data
queryClient.invalidateQueries({ queryKey: authKeys.all });
@@ -199,12 +194,7 @@ export function useRegister(onSuccess?: () => void) {
const { access_token, refresh_token, user, expires_in } = data;
// Update auth store with user and tokens (auto-login)
await setAuth(
user as User,
access_token,
refresh_token || '',
expires_in
);
await setAuth(user as User, access_token, refresh_token || '', expires_in);
// Invalidate and refetch user data
queryClient.invalidateQueries({ queryKey: authKeys.all });

View File

@@ -34,11 +34,7 @@ export function useUpdateProfile(onSuccess?: (message: string) => void) {
const setUser = useAuth((state) => state.setUser);
return useMutation({
mutationFn: async (data: {
first_name?: string;
last_name?: string;
email?: string;
}) => {
mutationFn: async (data: { first_name?: string; last_name?: string; email?: string }) => {
const response = await updateCurrentUser({
body: data,
throwOnError: false,
@@ -52,11 +48,7 @@ export function useUpdateProfile(onSuccess?: (message: string) => void) {
const responseData = (response as { data: unknown }).data;
// Validate response is a user object
if (
typeof responseData !== 'object' ||
responseData === null ||
!('id' in responseData)
) {
if (typeof responseData !== 'object' || responseData === null || !('id' in responseData)) {
throw new Error('Invalid profile update response: missing user data');
}