feat(dimentorin): backoffice admin panel (dashboard, user mgmt, session mgmt, content/roadmap, feedback, settings)
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { api } from '../';
|
||||
import {
|
||||
TAdminSessionListResponse,
|
||||
TAdminSessionsParams,
|
||||
TArticleDetail,
|
||||
TArticleListItem,
|
||||
TArticleListParams,
|
||||
@@ -16,8 +18,12 @@ import {
|
||||
TMentorRegisterRequest,
|
||||
TMentorStats,
|
||||
TPayment,
|
||||
TRoadmapItem,
|
||||
TRoadmapRequest,
|
||||
TSessionFeedbackRequest,
|
||||
TSessionListResponse,
|
||||
TUpdateSessionStatusRequest,
|
||||
TUpdateSessionStatusResponse,
|
||||
} from '../../types/dimentorin';
|
||||
import { TResponseMessage } from '../../types/common';
|
||||
|
||||
@@ -263,3 +269,66 @@ export const postChat = async (
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
|
||||
// ---- Admin Backoffice: sessions + status + roadmap ----
|
||||
export const getAdminSessions = async (
|
||||
params?: TAdminSessionsParams
|
||||
): Promise<TAdminSessionListResponse> => {
|
||||
const { data } = await api({
|
||||
method: 'GET',
|
||||
url: '/dimentorin/admin/sessions',
|
||||
params,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const putUpdateSessionStatus = async (
|
||||
id: string,
|
||||
payload: TUpdateSessionStatusRequest
|
||||
): Promise<TUpdateSessionStatusResponse> => {
|
||||
const { data } = await api({
|
||||
method: 'PUT',
|
||||
url: `/dimentorin/sessions/update/${id}/status`,
|
||||
data: payload,
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
export const getRoadmapList = async (): Promise<TRoadmapItem[]> => {
|
||||
const { data } = await api({
|
||||
method: 'GET',
|
||||
url: '/landing/cms/roadmap',
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
export const postCreateRoadmap = async (
|
||||
payload: TRoadmapRequest
|
||||
): Promise<TResponseMessage> => {
|
||||
const { data } = await api({
|
||||
method: 'POST',
|
||||
url: '/landing/cms/roadmap/create',
|
||||
data: payload,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const patchUpdateRoadmap = async (
|
||||
id: string,
|
||||
payload: TRoadmapRequest
|
||||
): Promise<TResponseMessage> => {
|
||||
const { data } = await api({
|
||||
method: 'PATCH',
|
||||
url: `/landing/cms/roadmap/update/${id}`,
|
||||
data: payload,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const deleteRoadmap = async (id: string): Promise<void> => {
|
||||
await api({
|
||||
method: 'DELETE',
|
||||
url: `/landing/cms/roadmap/delete/${id}`,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1 +1,100 @@
|
||||
export {};
|
||||
import { api } from '../';
|
||||
import {
|
||||
TActivateUserRequest,
|
||||
TCreateUserRequest,
|
||||
TUpdateUserRequest,
|
||||
TUserDetail,
|
||||
TUserListParams,
|
||||
TUserListResponse,
|
||||
} from '../../types/users';
|
||||
import { TRolesListItem } from '../../types/roles';
|
||||
import { TResponseMessage } from '../../types/common';
|
||||
|
||||
export const getUserMe = async (): Promise<TUserDetail> => {
|
||||
const { data } = await api({
|
||||
method: 'GET',
|
||||
url: '/iam/users/me',
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
export const putUpdateUserMe = async (
|
||||
payload: TUpdateUserRequest
|
||||
): Promise<TResponseMessage> => {
|
||||
const { data } = await api({
|
||||
method: 'PUT',
|
||||
url: '/iam/users/update/me',
|
||||
data: payload,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getUsersList = async (
|
||||
params?: TUserListParams
|
||||
): Promise<TUserListResponse> => {
|
||||
const { data } = await api({
|
||||
method: 'GET',
|
||||
url: '/iam/users',
|
||||
params,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getUserById = async (id: string): Promise<TUserDetail> => {
|
||||
const { data } = await api({
|
||||
method: 'GET',
|
||||
url: `/iam/users/detail/${id}`,
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
export const postCreateUser = async (
|
||||
payload: TCreateUserRequest
|
||||
): Promise<TUserDetail> => {
|
||||
const { data } = await api({
|
||||
method: 'POST',
|
||||
url: '/iam/users/create',
|
||||
data: payload,
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
export const putUpdateUser = async (
|
||||
id: string,
|
||||
payload: TUpdateUserRequest
|
||||
): Promise<TResponseMessage> => {
|
||||
const { data } = await api({
|
||||
method: 'PUT',
|
||||
url: `/iam/users/update/${id}`,
|
||||
data: payload,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const putActivateUser = async (
|
||||
id: string,
|
||||
payload: TActivateUserRequest
|
||||
): Promise<TResponseMessage> => {
|
||||
const { data } = await api({
|
||||
method: 'PUT',
|
||||
url: `/iam/users/activate/${id}`,
|
||||
data: payload,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const deleteUser = async (id: string): Promise<TResponseMessage> => {
|
||||
const { data } = await api({
|
||||
method: 'DELETE',
|
||||
url: `/iam/users/delete/${id}`,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getRolesList = async (): Promise<TRolesListItem[]> => {
|
||||
const { data } = await api({
|
||||
method: 'GET',
|
||||
url: '/iam/roles',
|
||||
});
|
||||
return data.data;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useMutation, useQuery, useQueryClient, UseQueryResult } from '@tanstack/react-query';
|
||||
import {
|
||||
deleteRoadmap,
|
||||
getAdminSessions,
|
||||
getArticleById,
|
||||
getArticleBySlug,
|
||||
getArticleCategories,
|
||||
@@ -12,12 +14,15 @@ import {
|
||||
getMentorStats,
|
||||
getMentors,
|
||||
getMyPayments,
|
||||
getMaterialById,
|
||||
getMaterialById,
|
||||
getMaterialBySlug,
|
||||
getMaterialCategories,
|
||||
getMaterialsList,
|
||||
getMySessions,
|
||||
getRoadmapList,
|
||||
patchUpdateRoadmap,
|
||||
postChat,
|
||||
postCreateRoadmap,
|
||||
getPaymentById,
|
||||
getSessionPayments,
|
||||
postBookSession,
|
||||
@@ -26,12 +31,15 @@ import {
|
||||
postRefreshPayment,
|
||||
postRegisterMentor,
|
||||
postSessionFeedback,
|
||||
putUpdateSessionStatus,
|
||||
} from '../../api/dimentorin';
|
||||
import {
|
||||
TAdminSessionListResponse,
|
||||
TAdminSessionsParams,
|
||||
TArticleDetail,
|
||||
TArticleListItem,
|
||||
TArticleListParams,
|
||||
TBookSessionRequest,
|
||||
TBookSessionRequest,
|
||||
TChatRequest,
|
||||
TChatResponse,
|
||||
TMaterialDetail,
|
||||
@@ -44,8 +52,12 @@ import {
|
||||
TMentorRegisterRequest,
|
||||
TMentorStats,
|
||||
TPayment,
|
||||
TRoadmapItem,
|
||||
TRoadmapRequest,
|
||||
TSessionFeedbackRequest,
|
||||
TSessionListResponse,
|
||||
TUpdateSessionStatusRequest,
|
||||
TUpdateSessionStatusResponse,
|
||||
} from '../../types/dimentorin';
|
||||
import { TResponseError, TResponseMessage } from '../../types/common';
|
||||
|
||||
@@ -307,3 +319,66 @@ export const usePostChat = () => {
|
||||
};
|
||||
|
||||
export type { TResponseMessage };
|
||||
|
||||
|
||||
// ---- Admin Backoffice ----
|
||||
export const useGetAdminSessions = (
|
||||
params?: TAdminSessionsParams
|
||||
): UseQueryResult<TAdminSessionListResponse, TResponseError> => {
|
||||
return useQuery({
|
||||
queryKey: ['admin-sessions', params],
|
||||
queryFn: async () => await getAdminSessions(params),
|
||||
});
|
||||
};
|
||||
|
||||
export const usePutUpdateSessionStatus = () => {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<
|
||||
TUpdateSessionStatusResponse,
|
||||
TResponseError,
|
||||
{ id: string; payload: TUpdateSessionStatusRequest }
|
||||
>({
|
||||
mutationFn: async ({ id, payload }) =>
|
||||
await putUpdateSessionStatus(id, payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['admin-sessions'] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetRoadmapList = (): UseQueryResult<TRoadmapItem[], TResponseError> => {
|
||||
return useQuery({
|
||||
queryKey: ['admin-roadmap'],
|
||||
queryFn: async () => await getRoadmapList(),
|
||||
});
|
||||
};
|
||||
|
||||
export const usePostCreateRoadmap = () => {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TResponseMessage, TResponseError, TRoadmapRequest>({
|
||||
mutationFn: async (payload) => await postCreateRoadmap(payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['admin-roadmap'] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const usePatchUpdateRoadmap = () => {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TResponseMessage, TResponseError, { id: string; payload: TRoadmapRequest }>({
|
||||
mutationFn: async ({ id, payload }) => await patchUpdateRoadmap(id, payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['admin-roadmap'] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteRoadmap = () => {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<void, TResponseError, string>({
|
||||
mutationFn: async (id) => await deleteRoadmap(id),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['admin-roadmap'] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1 +1,108 @@
|
||||
export {};
|
||||
import { useMutation, useQuery, useQueryClient, UseQueryResult } from '@tanstack/react-query';
|
||||
import {
|
||||
deleteUser,
|
||||
getRolesList,
|
||||
getUserById,
|
||||
getUserMe,
|
||||
getUsersList,
|
||||
postCreateUser,
|
||||
putActivateUser,
|
||||
putUpdateUser,
|
||||
putUpdateUserMe,
|
||||
} from '../../api/users';
|
||||
import {
|
||||
TActivateUserRequest,
|
||||
TCreateUserRequest,
|
||||
TUpdateUserRequest,
|
||||
TUserDetail,
|
||||
TUserListParams,
|
||||
TUserListResponse,
|
||||
} from '../../types/users';
|
||||
import { TRolesListItem } from '../../types/roles';
|
||||
import { TResponseError, TResponseMessage } from '../../types/common';
|
||||
|
||||
const USERS_KEY = ['admin-users'];
|
||||
const ROLES_KEY = ['admin-roles'];
|
||||
|
||||
export const useGetUsersList = (
|
||||
params?: TUserListParams
|
||||
): UseQueryResult<TUserListResponse, TResponseError> => {
|
||||
return useQuery({
|
||||
queryKey: [...USERS_KEY, params],
|
||||
queryFn: async () => await getUsersList(params),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetUserById = (
|
||||
id: string
|
||||
): UseQueryResult<TUserDetail, TResponseError> => {
|
||||
return useQuery({
|
||||
queryKey: [...USERS_KEY, 'detail', id],
|
||||
queryFn: async () => await getUserById(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
|
||||
export const usePostCreateUser = () => {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TUserDetail, TResponseError, TCreateUserRequest>({
|
||||
mutationFn: async (payload) => await postCreateUser(payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: USERS_KEY });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const usePutUpdateUser = () => {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TResponseMessage, TResponseError, { id: string; payload: TUpdateUserRequest }>({
|
||||
mutationFn: async ({ id, payload }) => await putUpdateUser(id, payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: USERS_KEY });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const usePutActivateUser = () => {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TResponseMessage, TResponseError, { id: string; payload: TActivateUserRequest }>({
|
||||
mutationFn: async ({ id, payload }) => await putActivateUser(id, payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: USERS_KEY });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useDeleteUser = () => {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TResponseMessage, TResponseError, string>({
|
||||
mutationFn: async (id) => await deleteUser(id),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: USERS_KEY });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetUserMe = (): UseQueryResult<TUserDetail, TResponseError> => {
|
||||
return useQuery({
|
||||
queryKey: [USERS_KEY, 'me'],
|
||||
queryFn: async () => await getUserMe(),
|
||||
});
|
||||
};
|
||||
|
||||
export const usePutUpdateUserMe = () => {
|
||||
const qc = useQueryClient();
|
||||
return useMutation<TResponseMessage, TResponseError, TUpdateUserRequest>({
|
||||
mutationFn: async (payload) => await putUpdateUserMe(payload),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: USERS_KEY });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetRolesList = (): UseQueryResult<TRolesListItem[], TResponseError> => {
|
||||
return useQuery({
|
||||
queryKey: ROLES_KEY,
|
||||
queryFn: async () => await getRolesList(),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -229,3 +229,72 @@ export type TChatResponse = {
|
||||
answer: string;
|
||||
sources: TChatSource[];
|
||||
};
|
||||
|
||||
|
||||
// ---- Admin Backoffice ----
|
||||
export type TAdminSession = {
|
||||
id: string;
|
||||
mentor_id: string;
|
||||
mentor_fullname: string | null;
|
||||
mentee_id: string;
|
||||
mentee_fullname: string | null;
|
||||
mentee_email: string | null;
|
||||
topic: string;
|
||||
scheduled_at: string;
|
||||
duration_minutes: number;
|
||||
meeting_link: string | null;
|
||||
session_type: string;
|
||||
status: string;
|
||||
rating: number | null;
|
||||
feedback: string | null;
|
||||
feedback_submitted_at: string | null;
|
||||
created_at: string;
|
||||
};
|
||||
|
||||
export type TAdminSessionListResponse = {
|
||||
data: TAdminSession[];
|
||||
meta: {
|
||||
page: number;
|
||||
per_page: number;
|
||||
total: number;
|
||||
total_pages?: number;
|
||||
has_next?: boolean;
|
||||
has_prev?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type TAdminSessionsParams = {
|
||||
page?: number;
|
||||
per_page?: number;
|
||||
status?: string;
|
||||
};
|
||||
|
||||
export type TUpdateSessionStatusRequest = {
|
||||
status: string;
|
||||
meeting_link?: string | null;
|
||||
};
|
||||
|
||||
export type TUpdateSessionStatusResponse = {
|
||||
id: string;
|
||||
status: string;
|
||||
meeting_link: string | null;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
// ---- CMS Roadmap ----
|
||||
export type TRoadmapItem = {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string;
|
||||
status: string; // upcoming | in_progress | shipped
|
||||
votes: number;
|
||||
is_deleted: boolean;
|
||||
created_at: string;
|
||||
updated_at?: string;
|
||||
};
|
||||
|
||||
export type TRoadmapRequest = {
|
||||
title: string;
|
||||
description: string;
|
||||
status: string;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,87 @@
|
||||
import { TRoleDetailItem } from '../roles';
|
||||
export type TUserRole = {
|
||||
id: string;
|
||||
name: string;
|
||||
permissions: Array<{ id: string; name: string }>;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type TUserItem = {
|
||||
id: string;
|
||||
avatar: string;
|
||||
birthdate: string;
|
||||
email: string;
|
||||
role: string; // role NAME in list view
|
||||
fullname: string;
|
||||
gender: string;
|
||||
email: string;
|
||||
avatar: string | null;
|
||||
is_active: boolean;
|
||||
phone_number: string;
|
||||
role: TRoleDetailItem;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type TUserDetail = {
|
||||
id: string;
|
||||
role: TUserRole;
|
||||
fullname: string;
|
||||
legal_name: string | null;
|
||||
email: string;
|
||||
avatar: string | null;
|
||||
is_active: boolean;
|
||||
profile_extension: unknown | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
export type TUserListParams = {
|
||||
page?: number;
|
||||
per_page?: number;
|
||||
search?: string;
|
||||
sort_by?: string;
|
||||
order?: string;
|
||||
filter?: string;
|
||||
filter_by?: string;
|
||||
};
|
||||
|
||||
export type TUserListResponse = {
|
||||
data: TUserItem[];
|
||||
meta: {
|
||||
page: number;
|
||||
per_page: number;
|
||||
total: number;
|
||||
total_pages?: number;
|
||||
has_next?: boolean;
|
||||
has_prev?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
export type TCreateUserRequest = {
|
||||
email: string;
|
||||
password: string;
|
||||
fullname: string;
|
||||
is_active?: boolean;
|
||||
role_id: string;
|
||||
avatar?: string | null;
|
||||
};
|
||||
|
||||
export type TUpdateUserRequest = {
|
||||
email?: string;
|
||||
password?: string;
|
||||
fullname?: string;
|
||||
legal_name?: string;
|
||||
is_active?: boolean;
|
||||
avatar?: string | null;
|
||||
role_id?: string;
|
||||
};
|
||||
|
||||
export type TActivateUserRequest = {
|
||||
is_active: boolean;
|
||||
};
|
||||
|
||||
export type TUsersMeProfile = {
|
||||
id: string;
|
||||
fullname: string;
|
||||
email: string;
|
||||
avatar: string | null;
|
||||
is_active: boolean;
|
||||
role: TUserRole;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user