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:
@@ -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) {
|
||||
|
||||
@@ -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',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user