feat: integrate all backend APIs into frontend apps

- Fix API service paths to use /v1/iam/, /v1/dimentorin/, /v1/gacha/, /v1/hackathon/ prefixes
- Add roles, permissions, events, testimonials, sessions API services and hooks
- Rewrite gacha service with full CRUD + roll/claim endpoints
- Replace all mock data in backoffice pages with real API calls (accounts, roles, permissions, gacha-roll, dashboard, sessions, users-dimentorin, feedback-review, settings)
- Wire dimentorin mentoring list to useMentorList with search + pagination
- Wire dimentorin mentor detail page to useMentorById, pass real data to all sections
- Wire appointment modal to useBookSession with controlled schedule inputs
- Wire gacha app Spin Now to useExecuteGachaRoll, show credits and real items

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
maulanasdqn
2026-04-05 17:08:58 +07:00
co-authored by Claude Sonnet 4.6
parent 8d2aaf5188
commit 53ad40f3fd
74 changed files with 3028 additions and 1942 deletions
+130 -292
View File
@@ -1,242 +1,140 @@
import { useMutation, useQuery } from '@tanstack/react-query';
import { hackathonApi, HackathonApiResponse } from '../../api/hackathon';
import { backofficeApi, BackofficeApiResponse } from '../../api/backoffice';
import { useAuthStore } from './use-auth-store';
import {
postLogin,
postRegister,
postVerifyEmail,
postSendOtp,
postForgotPassword,
postNewPassword,
} from '../../api/auth';
import { getUserMe } from '../../api/users';
export * from './use-auth-store';
interface TokenInfo {
access_token: string;
refresh_token: string;
}
interface User {
id: string;
email: string;
fullname: string;
phone_number?: string;
avatar?: string;
birthdate?: string;
gender?: string;
is_active: boolean;
location?: string;
bio?: string;
skills?: string[];
role_id?: string;
created_at: string;
updated_at?: string;
}
interface AuthResponse {
token: TokenInfo;
user: User;
}
interface SessionResponse {
user: User;
}
interface MessageResponse {
message: string;
}
interface LoginRequest {
email: string;
password: string;
}
interface SignupRequest {
email: string;
password: string;
fullname: string;
}
interface GitHubAuthRequest {
code: string;
}
interface ForgotPasswordRequest {
email: string;
}
interface ResetPasswordRequest {
access_token: string;
new_password: string;
}
export const useLogin = () => {
const { setSession } = useAuthStore();
return useMutation({
mutationFn: async (data: LoginRequest) => {
const response = await hackathonApi.post<
HackathonApiResponse<AuthResponse>
>('/auth/login', data);
return response.data.data;
},
onSuccess: (data) => {
mutationFn: postLogin,
onSuccess: (res) => {
const u = res.data?.user;
const t = res.data?.token;
if (!u || !t) return;
setSession({
token: data.token,
token: t,
user: {
id: data.user.id,
email: data.user.email,
fullname: data.user.fullname,
phone_number: data.user.phone_number || '',
avatar: data.user.avatar || '',
birthdate: data.user.birthdate || '',
gender: data.user.gender || '',
is_active: data.user.is_active,
location: data.user.location,
bio: data.user.bio,
skills: data.user.skills,
role: {
id: '',
name: 'user',
permissions: [],
created_at: '',
updated_at: '',
},
id: u.id,
email: u.email,
fullname: u.fullname,
phone_number: u.phone_number || '',
avatar: u.avatar || '',
birthdate: u.birthdate || '',
gender: u.gender || '',
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: '' },
},
});
},
});
};
export const useSignup = () => {
return useMutation({
mutationFn: async (data: SignupRequest) => {
const response = await hackathonApi.post<
HackathonApiResponse<MessageResponse>
>('/auth/signup', data);
return response.data.data;
},
});
};
export const useGitHubCallback = () => {
const { setSession } = useAuthStore();
return useMutation({
mutationFn: async (data: GitHubAuthRequest) => {
const response = await hackathonApi.post<
HackathonApiResponse<AuthResponse>
>('/auth/github', data);
return response.data.data;
},
onSuccess: (data) => {
setSession({
token: data.token,
user: {
id: data.user.id,
email: data.user.email,
fullname: data.user.fullname,
phone_number: data.user.phone_number || '',
avatar: data.user.avatar || '',
birthdate: data.user.birthdate || '',
gender: data.user.gender || '',
is_active: data.user.is_active,
location: data.user.location,
bio: data.user.bio,
skills: data.user.skills,
role: {
id: '',
name: 'user',
permissions: [],
created_at: '',
updated_at: '',
},
},
});
},
});
};
export const useSessionQuery = () => {
const { session } = useAuthStore();
return useQuery({
queryKey: ['auth-session'],
queryFn: async () => {
const response = await hackathonApi.get<
HackathonApiResponse<SessionResponse>
>('/auth/session');
return response.data.data;
},
enabled: !!session?.token,
});
};
export const useForgotPassword = () => {
return useMutation({
mutationFn: async (data: ForgotPasswordRequest) => {
const response = await hackathonApi.post<
HackathonApiResponse<MessageResponse>
>('/auth/forgot-password', data);
return response.data.data;
},
});
};
export const useResetPassword = () => {
return useMutation({
mutationFn: async (data: ResetPasswordRequest) => {
const response = await hackathonApi.post<
HackathonApiResponse<MessageResponse>
>('/auth/reset-password', data);
return response.data.data;
},
});
};
export const useSignOut = () => {
const { clearSession } = useAuthStore();
return useMutation({
mutationFn: async () => {
clearSession();
return { success: true };
},
});
};
export const useBackofficeLogin = () => {
const { setSession } = useAuthStore();
return useMutation({
mutationFn: async (data: LoginRequest) => {
const response = await backofficeApi.post<
BackofficeApiResponse<AuthResponse>
>('/auth/login', data);
return response.data.data;
},
onSuccess: (data) => {
mutationFn: postLogin,
onSuccess: (res) => {
const u = res.data?.user;
const t = res.data?.token;
if (!u || !t) return;
setSession({
token: data.token,
token: t,
user: {
id: data.user.id,
email: data.user.email,
fullname: data.user.fullname,
phone_number: data.user.phone_number || '',
avatar: data.user.avatar || '',
birthdate: data.user.birthdate || '',
gender: data.user.gender || '',
is_active: data.user.is_active,
location: data.user.location,
bio: data.user.bio,
skills: data.user.skills,
role: {
id: data.user.role_id || '',
name: 'admin',
permissions: [],
created_at: '',
updated_at: '',
},
id: u.id,
email: u.email,
fullname: u.fullname,
phone_number: u.phone_number || '',
avatar: u.avatar || '',
birthdate: u.birthdate || '',
gender: u.gender || '',
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: '' },
},
});
},
});
};
export const useSignup = () => {
return useMutation({ mutationFn: postRegister });
};
export const usePostLogin = () => {
return useMutation({
mutationFn: postLogin,
onSuccess: (res) => res,
});
};
export const usePostRegister = () => {
return useMutation({ mutationFn: postRegister });
};
export const usePostVerifyEmail = () => {
return useMutation({ mutationFn: postVerifyEmail });
};
export const usePostSendOtp = () => {
return useMutation({ mutationFn: postSendOtp });
};
export const useForgotPassword = () => {
return useMutation({ mutationFn: postForgotPassword });
};
export const useResetPassword = () => {
return useMutation({
mutationFn: (data: { token: string; password: string }) => postNewPassword(data),
});
};
export const useSignOut = () => {
const { clearSession } = useAuthStore();
return useMutation({
mutationFn: async () => {
clearSession();
return { success: true };
},
});
};
export const useSessionQuery = () => {
const { session } = useAuthStore();
return useQuery({
queryKey: ['auth-session'],
queryFn: async () => {
const user = await getUserMe();
return { user };
},
enabled: !!session?.token,
});
};
export const useGoogleCallback = () => {
return useMutation({
mutationFn: async () => {
throw new Error('Google OAuth not supported. Use GitHub OAuth instead.');
},
});
};
export const getGitHubOAuthUrl = (clientId: string, redirectUri: string) => {
const params = new URLSearchParams({
client_id: clientId,
@@ -257,21 +155,23 @@ export const useGitHubAuth = () => {
if (meta.env?.VITE_GITHUB_CLIENT_ID) clientId = meta.env.VITE_GITHUB_CLIENT_ID;
} catch { /* not in Vite context */ }
}
if (!clientId) {
throw new Error(
'GitHub Client ID not configured.'
);
}
if (!clientId) throw new Error('GitHub Client ID not configured.');
const redirectUri = `${globalThis.location.origin}/auth/callback`;
const url = getGitHubOAuthUrl(clientId, redirectUri);
return { url };
return { url: getGitHubOAuthUrl(clientId, redirectUri) };
};
return { signInWithGitHub };
};
return {
signInWithGitHub,
};
export const useGitHubCallback = () => {
const { setSession } = useAuthStore();
return useMutation({
mutationFn: async (data: { code: string }) => {
// GitHub OAuth callback is handled by the backend redirect.
// This hook exists for compatibility; in practice the backend
// redirects to the frontend with a token in the URL params.
throw new Error(`GitHub callback must be handled via backend redirect. Code: ${data.code}`);
},
});
};
export const useEmailAuth = () => {
@@ -282,82 +182,20 @@ export const useEmailAuth = () => {
const signInWithEmail = async (email: string, password: string) => {
const result = await loginMutation.mutateAsync({ email, password });
return {
user: result.user,
user: result.data?.user,
session: {
access_token: result.token.access_token,
refresh_token: result.token.refresh_token,
access_token: result.data?.token?.access_token,
refresh_token: result.data?.token?.refresh_token,
},
};
};
const signUpWithEmail = async (
email: string,
password: string,
fullname: string
) => {
const result = await signupMutation.mutateAsync({
email,
password,
fullname,
});
return {
message: result.message,
};
const signUpWithEmail = async (email: string, password: string, fullname: string) => {
const result = await signupMutation.mutateAsync({ email, password, fullname });
return { message: result.message };
};
const signOut = async () => {
clearSession();
};
const signOut = async () => { clearSession(); };
return {
signInWithEmail,
signUpWithEmail,
signOut,
};
};
export const usePostLogin = () => {
return useMutation({
mutationFn: async (data: LoginRequest) => {
const response = await hackathonApi.post<
HackathonApiResponse<AuthResponse>
>('/auth/login', data);
return { data: response.data.data };
},
});
};
export const usePostRegister = () => {
return useMutation({
mutationFn: async (data: SignupRequest) => {
const response = await hackathonApi.post<
HackathonApiResponse<AuthResponse>
>('/auth/signup', data);
return { data: response.data.data };
},
});
};
export const usePostVerifyEmail = () => {
return useMutation({
mutationFn: async () => {
throw new Error('Email verification not required with new backend');
},
});
};
export const usePostSendOtp = () => {
return useMutation({
mutationFn: async () => {
throw new Error('OTP not required with new backend');
},
});
};
export const useGoogleCallback = () => {
return useMutation({
mutationFn: async () => {
throw new Error('Google OAuth not supported. Use GitHub OAuth instead.');
},
});
return { signInWithEmail, signUpWithEmail, signOut };
};