From 254a2f154de0a1001b8d42df19c62c3ec236f5e1 Mon Sep 17 00:00:00 2001 From: Hafid Nur <73023445+hafidnrzs@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:51:43 +0700 Subject: [PATCH] feat(backoffice): hackathon dashboard integration --- .../(protected)/hackathon-dashboard/page.tsx | 38 ++++++++++- libs/service/src/api/admin/index.ts | 51 +++++++++++++++ libs/service/src/api/index.ts | 13 +++- libs/service/src/types/admin/index.ts | 64 +++++++++++++++++++ libs/service/src/types/index.ts | 1 + 5 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 libs/service/src/api/admin/index.ts create mode 100644 libs/service/src/types/admin/index.ts diff --git a/apps/backoffice/src/app/(protected)/hackathon-dashboard/page.tsx b/apps/backoffice/src/app/(protected)/hackathon-dashboard/page.tsx index c9262c8..9fc79f3 100644 --- a/apps/backoffice/src/app/(protected)/hackathon-dashboard/page.tsx +++ b/apps/backoffice/src/app/(protected)/hackathon-dashboard/page.tsx @@ -1,7 +1,35 @@ import { BackofficeWrapper } from '@imphnen-frontend-service/ui/organisms'; import { FC, ReactElement } from 'react'; +import { useQuery } from '@tanstack/react-query'; +import { + getAdminUsers, + getAdminTeams, + getAdminSubmissions, +} from '@imphnen-frontend-service/service'; export const HackathonDashboardPage: FC = (): ReactElement => { + // Fetch total participants + const { data: usersData } = useQuery({ + queryKey: ['admin-users-count'], + queryFn: () => getAdminUsers({ page: 1, limit: 1 }), + }); + + // Fetch total teams + const { data: teamsData } = useQuery({ + queryKey: ['admin-teams-count'], + queryFn: () => getAdminTeams({ page: 1, limit: 1 }), + }); + + // Fetch total submissions + const { data: submissionsData } = useQuery({ + queryKey: ['admin-submissions-count'], + queryFn: () => getAdminSubmissions({ page: 1, limit: 1 }), + }); + + const totalParticipants = usersData?.meta?.total_data ?? 0; + const totalTeams = teamsData?.meta?.total_data ?? 0; + const totalSubmissions = submissionsData?.meta?.total_data ?? 0; + return (

Dashboard

@@ -10,18 +38,22 @@ export const HackathonDashboardPage: FC = (): ReactElement => { {/* Participant */}

- 1261 + {totalParticipants}

Total Participants

{/* Team */}
-

206

+

+ {totalTeams} +

Total Teams

{/* Project Submitted */}
-

0

+

+ {totalSubmissions} +

Total Project Submitted

diff --git a/libs/service/src/api/admin/index.ts b/libs/service/src/api/admin/index.ts new file mode 100644 index 0000000..23b331a --- /dev/null +++ b/libs/service/src/api/admin/index.ts @@ -0,0 +1,51 @@ +import { api } from '../index'; +import type { + TAdminUsersResponse, + TAdminTeamsResponse, + TAdminSubmissionsResponse, +} from '../../types/admin'; + +const ADMIN_BASE_URL = '/admin'; + +// Admin Users +export const getAdminUsers = async (params?: { + page?: number; + limit?: number; + search?: string; + city?: string; +}) => { + const response = await api.get( + `${ADMIN_BASE_URL}/users`, + { params } + ); + return response.data; +}; + +// Admin Teams +export const getAdminTeams = async (params?: { + page?: number; + limit?: number; + search?: string; + city?: string; + visibility?: string; +}) => { + const response = await api.get( + `${ADMIN_BASE_URL}/teams`, + { params } + ); + return response.data; +}; + +// Admin Submissions +export const getAdminSubmissions = async (params?: { + page?: number; + limit?: number; + search?: string; + status?: string; +}) => { + const response = await api.get( + `${ADMIN_BASE_URL}/submissions`, + { params } + ); + return response.data; +}; diff --git a/libs/service/src/api/index.ts b/libs/service/src/api/index.ts index b6f6c8c..b6c0c2d 100644 --- a/libs/service/src/api/index.ts +++ b/libs/service/src/api/index.ts @@ -6,6 +6,7 @@ export * from './users'; export * from './mentors'; export * from './upload'; export * from './hackathon'; +export * from './admin'; // Common API response wrapper interface export interface ApiResponse { @@ -20,7 +21,9 @@ const getSessionTokenFromCookies = () => { if (typeof document === 'undefined') return null; const cookies = document.cookie.split(';'); - const tokenCookie = cookies.find(cookie => cookie.trim().startsWith(`${TOKEN_KEY}=`)); + const tokenCookie = cookies.find((cookie) => + cookie.trim().startsWith(`${TOKEN_KEY}=`) + ); if (!tokenCookie) return null; @@ -32,13 +35,17 @@ const getSessionTokenFromCookies = () => { } }; -const setSessionTokenToCookies = (tokenData: { token: { access_token: string; refresh_token: string } }) => { +const setSessionTokenToCookies = (tokenData: { + token: { access_token: string; refresh_token: string }; +}) => { if (typeof document === 'undefined') return; const expires = new Date(); expires.setDate(expires.getDate() + 7); - document.cookie = `${TOKEN_KEY}=${encodeURIComponent(JSON.stringify(tokenData))}; expires=${expires.toUTCString()}; path=/; secure; samesite=strict`; + document.cookie = `${TOKEN_KEY}=${encodeURIComponent( + JSON.stringify(tokenData) + )}; expires=${expires.toUTCString()}; path=/; secure; samesite=strict`; }; const removeSessionTokenFromCookies = () => { diff --git a/libs/service/src/types/admin/index.ts b/libs/service/src/types/admin/index.ts new file mode 100644 index 0000000..7b762fd --- /dev/null +++ b/libs/service/src/types/admin/index.ts @@ -0,0 +1,64 @@ +export type TAdminMetaResponse = { + page: number; + per_page: number; + total_data: number; + total_page: number; +}; + +export type TAdminListResponse = { + data: T[]; + meta: TAdminMetaResponse; +}; + +// Admin Users +export type TAdminUserItem = { + id: string; + email: string; + fullname: string; + avatar: string | null; + phone_number: string | null; + location: string | null; + bio: string; + skills: string[]; + is_active: boolean; + created_at: string; + updated_at: string; +}; + +export type TAdminUsersResponse = TAdminListResponse; + +// Admin Teams +export type TAdminTeamItem = { + id: string; + name: string; + description: string; + city: string; + visibility: string; + logo: string | null; + banner: string | null; + leader_id: string; + created_at: string; + updated_at: string; +}; + +export type TAdminTeamsResponse = TAdminListResponse; + +// Admin Submissions +export type TAdminSubmissionItem = { + id: string; + team_id: string; + project_name: string; + description: string; + repository_url: string; + demo_url: string | null; + presentation_url: string | null; + screenshots: string[]; + status: string; + submitted_at: string; + submitted_by: string; + created_at: string; + updated_at: string; +}; + +export type TAdminSubmissionsResponse = + TAdminListResponse; diff --git a/libs/service/src/types/index.ts b/libs/service/src/types/index.ts index fb6c790..fa54611 100644 --- a/libs/service/src/types/index.ts +++ b/libs/service/src/types/index.ts @@ -5,3 +5,4 @@ export * from './roles'; export * from './permissions'; export * from './mentors'; export * from './teams'; +export * from './admin';