feat: align frontend types with unified /me API response

- Add THackathonProfile, TQrProfile, TMentorProfile, TSessionProfile
  and TUsersMeResponse types matching the backend unified /me endpoint
- getUserMe() now accepts optional include[] param for selective
  module loading (?include=hackathon,qr,mentor,sessions)
- useSessionQuery() accepts include[] to control which modules load
- Fix SessionUser storage to use optional fields matching backend
- Remove hardcoded fallback values in useLogin (birthdate, gender)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
maulanasdqn
2026-04-10 13:31:32 +07:00
co-authored by Claude Opus 4.6
parent 53ad40f3fd
commit c6719b19e5
4 changed files with 67 additions and 30 deletions
+6 -2
View File
@@ -4,6 +4,7 @@ import type {
TUsersDetailItem,
TUserCreateRequest,
TUserUpdateRequest,
TUsersMeResponse,
} from '../../types/users';
import type { TApiPaginated, TPaginationParams } from '../../types/common';
@@ -11,13 +12,16 @@ import type { TApiPaginated, TPaginationParams } from '../../types/common';
export type UserDetailResponseDto = TUsersDetailItem;
export type UserUpdateRequestDto = TUserUpdateRequest;
export type TUserMeInclude = 'hackathon' | 'qr' | 'mentor' | 'sessions';
export const getUserList = async (params?: TPaginationParams): Promise<TApiPaginated<TUsersListItem>> => {
const response = await api.get<TApiPaginated<TUsersListItem>>('/v1/iam/users', { params });
return response.data;
};
export const getUserMe = async (): Promise<TUsersDetailItem> => {
const response = await api.get<ApiResponse<TUsersDetailItem>>('/v1/iam/users/me');
export const getUserMe = async (include?: TUserMeInclude[]): Promise<TUsersMeResponse> => {
const params = include?.length ? { include: include.join(',') } : undefined;
const response = await api.get<ApiResponse<TUsersMeResponse>>('/v1/iam/users/me', { params });
return response.data.data;
};
+10 -14
View File
@@ -8,7 +8,7 @@ import {
postForgotPassword,
postNewPassword,
} from '../../api/auth';
import { getUserMe } from '../../api/users';
import { getUserMe, type TUserMeInclude } from '../../api/users';
export * from './use-auth-store';
@@ -27,15 +27,13 @@ export const useLogin = () => {
id: u.id,
email: u.email,
fullname: u.fullname,
phone_number: u.phone_number || '',
avatar: u.avatar || '',
birthdate: u.birthdate || '',
gender: u.gender || '',
phone_number: u.phone_number,
avatar: u.avatar,
is_active: u.is_active,
location: u.location,
bio: u.bio,
skills: u.skills,
role: u.role ?? { id: '', name: 'user', permissions: [], created_at: '', updated_at: '' },
role: u.role ?? { id: '', name: 'user', permissions: [] },
},
});
},
@@ -57,15 +55,13 @@ export const useBackofficeLogin = () => {
id: u.id,
email: u.email,
fullname: u.fullname,
phone_number: u.phone_number || '',
avatar: u.avatar || '',
birthdate: u.birthdate || '',
gender: u.gender || '',
phone_number: u.phone_number,
avatar: u.avatar,
is_active: u.is_active,
location: u.location,
bio: u.bio,
skills: u.skills,
role: u.role ?? { id: '', name: 'admin', permissions: [], created_at: '', updated_at: '' },
role: u.role ?? { id: '', name: 'admin', permissions: [] },
},
});
},
@@ -115,12 +111,12 @@ export const useSignOut = () => {
});
};
export const useSessionQuery = () => {
export const useSessionQuery = (include?: TUserMeInclude[]) => {
const { session } = useAuthStore();
return useQuery({
queryKey: ['auth-session'],
queryKey: ['auth-session', include],
queryFn: async () => {
const user = await getUserMe();
const user = await getUserMe(include);
return { user };
},
enabled: !!session?.token,
+10 -14
View File
@@ -1,26 +1,22 @@
import type { TPermissionItem } from '../types/permissions';
type TRoleItem = {
id: string;
name: string;
created_at: string;
updated_at: string;
permissions: TPermissionItem[];
};
import type { TRoleDetailItem } from '../types/roles';
import type { THackathonProfile, TQrProfile, TMentorProfile } from '../types/users';
type TUserItem = {
id: string;
avatar: string;
birthdate: string;
avatar?: string;
birthdate?: string;
email: string;
fullname: string;
gender: string;
gender?: string;
is_active: boolean;
phone_number: string;
role: TRoleItem;
phone_number?: string;
role: TRoleDetailItem;
bio?: string;
location?: string;
skills?: string[];
hackathon?: THackathonProfile;
qr?: TQrProfile;
mentor?: TMentorProfile;
};
export const SessionUser = {
+41
View File
@@ -76,6 +76,47 @@ export type TUsersDetailItem = {
updated_at: string;
};
// --- Unified /me response types ---
export type THackathonProfile = {
is_admin: boolean;
phone_number?: string;
location?: string;
bio?: string;
skills?: string[] | null;
};
export type TQrProfile = {
role: string;
provider: string;
};
export type TMentorProfile = {
mentor_id: string;
status?: string;
current_company?: string;
current_role?: string;
years_of_experience?: number;
};
export type TSessionProfile = {
id: string;
topic: string;
description?: string;
scheduled_at: string;
duration_minutes: number;
session_type: string;
status: string;
role: string;
};
export type TUsersMeResponse = TUsersDetailItem & {
hackathon?: THackathonProfile;
qr?: TQrProfile;
mentor?: TMentorProfile;
sessions?: TSessionProfile[];
};
export type TUserCreateRequest = {
email: string;
password: string;