diff --git a/apps/web/src/app/(main)/_components/home/Admin/AdminHomeDashboardScreen.tsx b/apps/web/src/app/(main)/_components/home/Admin/AdminHomeDashboardScreen.tsx
deleted file mode 100644
index c560cdcb..00000000
--- a/apps/web/src/app/(main)/_components/home/Admin/AdminHomeDashboardScreen.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import {
- AnnounceCard,
- AnnounceCardProps,
-} from '@web/app/(main)/_components/admin/AnnouncedCard/AnnouncedCard';
-import {
- DocTimeline,
- DocTimelineProps,
-} from '@web/app/(main)/_components/admin/DocTimeline/DocTimeline';
-import {
- OverallProgress,
- Task,
-} from '@web/app/(main)/_components/admin/OverallProgress/OverallProgress';
-import {
- PendingUsers,
- PendingUsersProps,
-} from '@web/app/(main)/_components/admin/PendingUsers/PendingUsers';
-import React from 'react';
-import { Flex } from '@repo/ui/Flex';
-import { AdminHomeHeader } from '@web/app/(main)/_components/admin/AdminHomeHeader/AdminHomeHeader';
-
-const announceData: AnnounceCardProps = {
- title: '현재 진행 중인 공고 - [한국대학생IT경영학회] 큐시즘 32기 학회원 모집',
- totalCount: 100,
- parts: [
- { name: '기획', color: '#FFD8A6', count: 23 },
- { name: '디자인', color: '#D0F2FF', count: 20 },
- { name: '프론트엔드', color: '#B5F5EC', count: 30 },
- { name: '백엔드', color: '#FFE3EC', count: 27 },
- ],
- onViewDetail: () => {
- console.log('지원 현황으로 이동');
- },
-};
-
-const docTimelineData: DocTimelineProps = {
- title: '서류 평가',
- currentMonth: new Date(2025, 4),
- deadlineDays: 3,
- events: [
- { date: '2025-05-04', label: '서류 합격 발표', daysBefore: 3 },
- { date: '2025-05-14', label: '면접 평가', daysBefore: 14 },
- { date: '2025-05-23', label: '최종 발표', daysBefore: 23 },
- ],
- onPrevMonth: () => console.log('이전 달'),
- onNextMonth: () => console.log('다음 달'),
-};
-
-const tasks: Task[] = [
- {
- id: 't1',
- type: '서류',
- title: '서류 평가',
- daysBefore: 3,
- total: 50,
- completed: 15,
- partLabel: '전체',
- },
- {
- id: 't2',
- type: '면접',
- title: '면접 평가',
- daysBefore: 22,
- total: 40,
- completed: 10,
- partLabel: '전체',
- },
-];
-
-const pendingUsersData: PendingUsersProps = {
- deadline: new Date(2025, 4, 1, 23, 59),
- remainingHours: 21,
- users: [
- {
- id: '1',
- name: '김현호',
- avatarUrl:
- 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTpe8Bx-lLSfBQO6Pi22c5nnBpUaJMeV0hi_s9Vf-CFB-pRlj0ezWnKCtf3b9AvkvxRmLo&usqp=CAU',
- },
- {
- id: '2',
- name: '설정원',
- avatarUrl:
- 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQIoLwYArbR32rJwWFNLSWkCxX_JoUSlFHByyZ181guOpXx94XHZQ6eRf9J3eq6ydHM-co&usqp=CAU',
- },
- {
- id: '3',
- name: '이채원',
- avatarUrl:
- 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRXUJbxoZj1I44tPmZhsdoyqImc2pwSSSULcw3hb4U-CeyF--hrVYrmOkv14DGsh5pBYCk&usqp=CAU',
- },
- {
- id: '4',
- name: '서유빈',
- avatarUrl:
- 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSQ2mnsIOyPS475TL65mRbwjka-xlGjqhiuQd_cwQ5h7jT_ynTLbV-kT8PeI_NYQUiW8ZI&usqp=CAU',
- },
- ],
- onRemind: () => {
- console.log('리마인드 알림 발송');
- },
-};
-
-export const AdminHomeDashboardScreen = () => (
-
-
-
-
-
-
-
-
-
-
-
-);
diff --git a/apps/web/src/app/(main)/dashboard/_components/AdminHomeDashboardScreen.tsx b/apps/web/src/app/(main)/dashboard/_components/AdminHomeDashboardScreen.tsx
new file mode 100644
index 00000000..34bededf
--- /dev/null
+++ b/apps/web/src/app/(main)/dashboard/_components/AdminHomeDashboardScreen.tsx
@@ -0,0 +1,177 @@
+'use client';
+
+import React from 'react';
+import { useRouter } from 'next/navigation';
+import { Flex } from '@repo/ui/Flex';
+import { AdminHomeHeader } from '@web/app/(main)/dashboard/_components/admin/AdminHomeHeader/AdminHomeHeader';
+import {
+ AnnounceCard,
+ AnnounceCardProps,
+} from '@web/app/(main)/dashboard/_components/admin/AnnouncedCard/AnnouncedCard';
+import {
+ DocTimeline,
+ DocTimelineProps,
+} from '@web/app/(main)/dashboard/_components/admin/DocTimeline/DocTimeline';
+import {
+ OverallProgress,
+ Task,
+} from '@web/app/(main)/dashboard/_components/admin/OverallProgress/OverallProgress';
+import {
+ PendingUsers,
+ PendingUsersProps,
+} from '@web/app/(main)/dashboard/_components/admin/PendingUsers/PendingUsers';
+import { useCurrentRecruitmentsSummaryQuery } from '@web/store/query/useCurrentRecruitmentsSummaryQuery';
+import {
+ useRecruitmentProgressQuery,
+ RecruitmentProgressDto,
+} from '@web/store/query/useRecruitmentProgressQuery';
+import { useRecruitmentPendingEvaluatorsQuery } from '@web/store/query/useRecruitmentPendingEvaluatorsQuery';
+import type { Tokens } from '@web/api/types';
+
+interface AdminHomeDashboardScreenProps {
+ recruitmentId: number;
+ tokens?: Tokens;
+}
+
+export const AdminHomeDashboardScreen = ({
+ recruitmentId,
+ tokens,
+}: AdminHomeDashboardScreenProps) => {
+ const router = useRouter();
+
+ const summariesQuery = useCurrentRecruitmentsSummaryQuery(tokens);
+ const docProgressQuery = useRecruitmentProgressQuery(
+ recruitmentId,
+ 'DOCUMENT',
+ tokens
+ );
+ const interviewProgressQuery = useRecruitmentProgressQuery(
+ recruitmentId,
+ 'INTERVIEW',
+ tokens
+ );
+ const pendingQuery = useRecruitmentPendingEvaluatorsQuery(
+ recruitmentId,
+ tokens
+ );
+
+ if (
+ !summariesQuery.data ||
+ !docProgressQuery.data ||
+ !interviewProgressQuery.data ||
+ !pendingQuery.data
+ ) {
+ return null;
+ }
+
+ const summaries = summariesQuery.data;
+ const docProgress = docProgressQuery.data;
+ const interviewProgress = interviewProgressQuery.data;
+ const pending = pendingQuery.data;
+
+ console.log('summaries:', summaries);
+ console.log('docProgress:', docProgress);
+ console.log('interviewProgress:', interviewProgress);
+ console.log('pending:', pending);
+ console.log('recruitmentId:', recruitmentId);
+ console.log('tokens:', tokens);
+
+ if (!summaries || summaries.length === 0) return null;
+
+ const summary =
+ summaries.find((s) => s.recruitmentId === recruitmentId) ?? summaries[0]!;
+ const announceData: AnnounceCardProps = {
+ title: `현재 진행 중인 공고 - [${summary.organizationName}] ${summary.title}`,
+ totalCount: summary.totalApplicants,
+ parts: summary.positionCounts.map((p) => ({
+ name: p.positionName,
+ count: p.count,
+ })),
+ onViewDetail: () =>
+ // router.push(
+ // `/admin/recruitments/${recruitmentId}/progress?stage=DOCUMENT`
+ // ),
+ console.log('상세 페이지로 이동'),
+ };
+
+ const allEvents = summary.dDays
+ .filter((e) => e.date !== null)
+ .map((e) => ({
+ date: e.date!.replace(/\//g, '-'),
+ label: e.label,
+ daysBefore: e.daysRemaining,
+ }));
+
+ console.log('allEvents (filtered):', allEvents);
+ console.log('original dDays:', summary.dDays);
+ const docDeadlineEvent = allEvents.find((e) => e.label.includes('서류 마감'));
+ const firstDate = allEvents[0]?.date;
+ const currentMonth = firstDate ? new Date(firstDate) : new Date();
+
+ const docTimelineData: DocTimelineProps = {
+ title: '서류 평가',
+ currentMonth,
+ deadlineDays: docDeadlineEvent?.daysBefore ?? 0,
+ events: allEvents,
+ onPrevMonth: () => console.log('이전 달'),
+ onNextMonth: () => console.log('다음 달'),
+ };
+
+ const makeTasks = (
+ arr: RecruitmentProgressDto[],
+ type: '서류' | '면접'
+ ): Task[] =>
+ arr.map((d) => ({
+ id: `${type}-${d.positionName}`,
+ type,
+ title: `${type} 평가`,
+ daysBefore: d.daysToDeadline,
+ total: d.totalToEvaluate,
+ completed: d.evaluatedCount,
+ partLabel: d.positionName,
+ }));
+
+ const tasks: Task[] = [
+ ...makeTasks(docProgress, '서류'),
+ ...makeTasks(interviewProgress, '면접'),
+ ];
+
+ const deadlineDate = pending.deadline
+ ? new Date(pending.deadline.replace(/\//g, '-'))
+ : new Date();
+
+ const pendingUsersData: PendingUsersProps = {
+ deadline: deadlineDate,
+ remainingDays: pending.daysToDeadline ?? 0,
+ remainingHours: pending.hoursToDeadline ?? 0,
+ remainingMinutes: pending.minutesToDeadline ?? 0,
+ users:
+ pending.users?.map((u) => ({
+ id: u.userId.toString(),
+ name: u.name,
+ avatarUrl: u.profileImageUrl ?? '',
+ })) ?? [],
+ onRemind: () => console.log('리마인드 알림 발송'),
+ };
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/apps/web/src/app/(main)/_components/home/Admin/AdminHomeEmptyScreen.tsx b/apps/web/src/app/(main)/dashboard/_components/AdminHomeEmptyScreen.tsx
similarity index 58%
rename from apps/web/src/app/(main)/_components/home/Admin/AdminHomeEmptyScreen.tsx
rename to apps/web/src/app/(main)/dashboard/_components/AdminHomeEmptyScreen.tsx
index 01766257..98c0a2cc 100644
--- a/apps/web/src/app/(main)/_components/home/Admin/AdminHomeEmptyScreen.tsx
+++ b/apps/web/src/app/(main)/dashboard/_components/AdminHomeEmptyScreen.tsx
@@ -1,7 +1,7 @@
import { Flex } from '@repo/ui/Flex';
-import { AdminHomeHeader } from '@web/app/(main)/_components/admin/AdminHomeHeader/AdminHomeHeader';
-import { NoAnnouncements } from '@web/app/(main)/_components/admin/EmptyState/NoAnnouncements';
-import { NoTasks } from '@web/app/(main)/_components/admin/EmptyState/NoTasks';
+import { AdminHomeHeader } from '@web/app/(main)/dashboard/_components/admin/AdminHomeHeader/AdminHomeHeader';
+import { NoAnnouncements } from '@web/app/(main)/dashboard/_components/admin/EmptyState/NoAnnouncements';
+import { NoTasks } from '@web/app/(main)/dashboard/_components/admin/EmptyState/NoTasks';
import React from 'react';
export const AdminHomeEmptyScreen = () => (
diff --git a/apps/web/src/app/(main)/_components/home/User/UserHomeDashboardScreen.tsx b/apps/web/src/app/(main)/dashboard/_components/UserHomeDashboardScreen.tsx
similarity index 54%
rename from apps/web/src/app/(main)/_components/home/User/UserHomeDashboardScreen.tsx
rename to apps/web/src/app/(main)/dashboard/_components/UserHomeDashboardScreen.tsx
index 87cf8ff4..8f03396b 100644
--- a/apps/web/src/app/(main)/_components/home/User/UserHomeDashboardScreen.tsx
+++ b/apps/web/src/app/(main)/dashboard/_components/UserHomeDashboardScreen.tsx
@@ -2,37 +2,78 @@
import React from 'react';
import { Flex } from '@repo/ui/Flex';
-import { TimelineEvent } from '@web/app/(main)/_components/admin/DocTimeline/DocTimeline';
+import { TimelineEvent } from '@web/app/(main)/dashboard/_components/admin/DocTimeline/DocTimeline';
import {
ReviewItem,
UserDocReviewList,
-} from '@web/app/(main)/_components/user/UserDocReviewList/UserDocReviewList';
+} from '@web/app/(main)/dashboard/_components/user/UserDocReviewList/UserDocReviewList';
import {
InterviewSlot,
ReviewerRole,
UserInterviewReview,
-} from '@web/app/(main)/_components/user/UserInterviewReview/UserInterviewReview';
-import { UserAnnouncementProgress } from '@web/app/(main)/_components/user/UserAnnouncementProgress/UserAnnouncementProgress';
-import { UserHomeHeader } from '@web/app/(main)/_components/user/UserHomeHeader/UserHomeHeader';
+} from '@web/app/(main)/dashboard/_components/user/UserInterviewReview/UserInterviewReview';
+import { UserAnnouncementProgress } from '@web/app/(main)/dashboard/_components/user/UserAnnouncementProgress/UserAnnouncementProgress';
+import { UserHomeHeader } from '@web/app/(main)/dashboard/_components/user/UserHomeHeader/UserHomeHeader';
+import { useOrganizationsMeQuery } from '@web/store/query/useOrganizationsMeQuery';
+import {
+ RecruitmentSummaryDto,
+ useRecruitmentsCurrentSummaryQuery,
+} from '@web/store/query/useRecruitmentsCurrentSummaryQuery';
+import {
+ MyEvaluationItemDto,
+ useMyDocumentEvaluationsQuery,
+} from '@web/store/query/useMyDocumentEvaluationsQuery';
+import { Tokens } from '@web/api/types';
+
+interface UserHomeDashboardScreenProps {
+ tokens: Tokens;
+}
+export default function UserHomeDashboardScreen({
+ tokens,
+}: UserHomeDashboardScreenProps) {
+ const orgsQuery = useOrganizationsMeQuery(tokens);
+ const summaryQuery = useRecruitmentsCurrentSummaryQuery(
+ orgsQuery.data?.[0]?.id ?? -1,
+ tokens
+ );
+
+ const docEvalQuery = useMyDocumentEvaluationsQuery(
+ summaryQuery.data?.[0]?.recruitmentId ?? -1,
+ tokens
+ );
-export const UserHomeDashboardScreen = () => {
- const announcementTitle = '[한국대학생IT경영학회] 큐시즘 32기 학회원 모집';
- const timelineEvents: TimelineEvent[] = [
- { date: '2025-05-04', label: '서류 평가 마감', daysBefore: 3 },
- { date: '2025-04-07', label: '면접 평가 시작', daysBefore: 30 },
- { date: '2025-02-21', label: '최종 발표 시작', daysBefore: 70 },
- ];
+ const orgs = orgsQuery.data;
+ if (!orgs || orgs.length === 0) return null;
+ const organization = orgs[0]!;
- const itemsBefore: ReviewItem[] = [
- { id: '1', part: '기획', name: '장지원' },
- { id: '2', part: '디자인', name: '김하나' },
- { id: '3', part: '백엔드', name: '이영희' },
- ];
- const itemsAfter: ReviewItem[] = [
- { id: '4', part: '기획', name: '박철수' },
- { id: '5', part: '디자인', name: '최민준' },
- { id: '6', part: '백엔드', name: '최수진' },
- ];
+ const summaries = summaryQuery.data;
+ if (!summaries || summaries.length === 0) return null;
+ const summary: RecruitmentSummaryDto = summaries[0]!;
+
+ const docEvals = docEvalQuery.data;
+ if (!docEvals) return null;
+
+ const timelineEvents: TimelineEvent[] = summary.dDays.map((e) => ({
+ date: e.date.replace(/\//g, '-'),
+ label: e.label,
+ daysBefore: e.daysRemaining,
+ }));
+ const announcementTitle = `[${organization.name}] ${summary.title}`;
+
+ const itemsBefore: ReviewItem[] = docEvals.pending.map(
+ (u: MyEvaluationItemDto) => ({
+ id: u.id.toString(),
+ part: u.positionName,
+ name: u.name,
+ })
+ );
+ const itemsAfter: ReviewItem[] = docEvals.done.map(
+ (u: MyEvaluationItemDto) => ({
+ id: u.id.toString(),
+ part: u.positionName,
+ name: u.name,
+ })
+ );
const initialDate = new Date(2025, 4, 12);
const slotsByRole: Record = {
@@ -137,4 +178,4 @@ export const UserHomeDashboardScreen = () => {
);
-};
+}
diff --git a/apps/web/src/app/(main)/_components/home/User/UserHomeEmptyScreen.tsx b/apps/web/src/app/(main)/dashboard/_components/UserHomeEmptyScreen.tsx
similarity index 59%
rename from apps/web/src/app/(main)/_components/home/User/UserHomeEmptyScreen.tsx
rename to apps/web/src/app/(main)/dashboard/_components/UserHomeEmptyScreen.tsx
index 5a2f17dc..67a41219 100644
--- a/apps/web/src/app/(main)/_components/home/User/UserHomeEmptyScreen.tsx
+++ b/apps/web/src/app/(main)/dashboard/_components/UserHomeEmptyScreen.tsx
@@ -2,10 +2,10 @@
import React from 'react';
import { Flex } from '@repo/ui/Flex';
-import { UserHomeHeader } from '@web/app/(main)/_components/user/UserHomeHeader/UserHomeHeader';
-import { NoAnnouncements } from '@web/app/(main)/_components/user/EmptyState/NoAnnouncements';
-import { NoDocs } from '@web/app/(main)/_components/user/EmptyState/NoDocs';
-import { NoInterview } from '@web/app/(main)/_components/user/EmptyState/NoInterviews';
+import { UserHomeHeader } from '@web/app/(main)/dashboard/_components/user/UserHomeHeader/UserHomeHeader';
+import { NoAnnouncements } from '@web/app/(main)/dashboard/_components/user/EmptyState/NoAnnouncements';
+import { NoDocs } from '@web/app/(main)/dashboard/_components/user/EmptyState/NoDocs';
+import { NoInterview } from '@web/app/(main)/dashboard/_components/user/EmptyState/NoInterviews';
export const UserHomeEmptyScreen: React.FC = () => {
return (
diff --git a/apps/web/src/app/(main)/_components/admin/AdminHomeHeader/AdminHomeHeader.tsx b/apps/web/src/app/(main)/dashboard/_components/admin/AdminHomeHeader/AdminHomeHeader.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/AdminHomeHeader/AdminHomeHeader.tsx
rename to apps/web/src/app/(main)/dashboard/_components/admin/AdminHomeHeader/AdminHomeHeader.tsx
diff --git a/apps/web/src/app/(main)/_components/admin/AnnouncedCard/AnnouncedCard.css.ts b/apps/web/src/app/(main)/dashboard/_components/admin/AnnouncedCard/AnnouncedCard.css.ts
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/AnnouncedCard/AnnouncedCard.css.ts
rename to apps/web/src/app/(main)/dashboard/_components/admin/AnnouncedCard/AnnouncedCard.css.ts
diff --git a/apps/web/src/app/(main)/_components/admin/AnnouncedCard/AnnouncedCard.tsx b/apps/web/src/app/(main)/dashboard/_components/admin/AnnouncedCard/AnnouncedCard.tsx
similarity index 99%
rename from apps/web/src/app/(main)/_components/admin/AnnouncedCard/AnnouncedCard.tsx
rename to apps/web/src/app/(main)/dashboard/_components/admin/AnnouncedCard/AnnouncedCard.tsx
index 325f1ae9..848b1115 100644
--- a/apps/web/src/app/(main)/_components/admin/AnnouncedCard/AnnouncedCard.tsx
+++ b/apps/web/src/app/(main)/dashboard/_components/admin/AnnouncedCard/AnnouncedCard.tsx
@@ -9,7 +9,6 @@ import { Tag } from '@repo/ui/Tag';
import { allTagColors, TagColor } from '@repo/utils';
export interface PartCount {
name: string;
- color: string;
count: number;
}
diff --git a/apps/web/src/app/(main)/_components/admin/DocTimeline/DocTimeline.css.ts b/apps/web/src/app/(main)/dashboard/_components/admin/DocTimeline/DocTimeline.css.ts
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/DocTimeline/DocTimeline.css.ts
rename to apps/web/src/app/(main)/dashboard/_components/admin/DocTimeline/DocTimeline.css.ts
diff --git a/apps/web/src/app/(main)/_components/admin/DocTimeline/DocTimeline.tsx b/apps/web/src/app/(main)/dashboard/_components/admin/DocTimeline/DocTimeline.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/DocTimeline/DocTimeline.tsx
rename to apps/web/src/app/(main)/dashboard/_components/admin/DocTimeline/DocTimeline.tsx
diff --git a/apps/web/src/app/(main)/_components/admin/EmptyState/EmptyState.css.ts b/apps/web/src/app/(main)/dashboard/_components/admin/EmptyState/EmptyState.css.ts
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/EmptyState/EmptyState.css.ts
rename to apps/web/src/app/(main)/dashboard/_components/admin/EmptyState/EmptyState.css.ts
diff --git a/apps/web/src/app/(main)/_components/admin/EmptyState/NoAnnouncements.tsx b/apps/web/src/app/(main)/dashboard/_components/admin/EmptyState/NoAnnouncements.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/EmptyState/NoAnnouncements.tsx
rename to apps/web/src/app/(main)/dashboard/_components/admin/EmptyState/NoAnnouncements.tsx
diff --git a/apps/web/src/app/(main)/_components/admin/EmptyState/NoTasks.tsx b/apps/web/src/app/(main)/dashboard/_components/admin/EmptyState/NoTasks.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/EmptyState/NoTasks.tsx
rename to apps/web/src/app/(main)/dashboard/_components/admin/EmptyState/NoTasks.tsx
diff --git a/apps/web/src/app/(main)/_components/admin/OverallProgress/OverallProgress.css.ts b/apps/web/src/app/(main)/dashboard/_components/admin/OverallProgress/OverallProgress.css.ts
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/OverallProgress/OverallProgress.css.ts
rename to apps/web/src/app/(main)/dashboard/_components/admin/OverallProgress/OverallProgress.css.ts
diff --git a/apps/web/src/app/(main)/_components/admin/OverallProgress/OverallProgress.tsx b/apps/web/src/app/(main)/dashboard/_components/admin/OverallProgress/OverallProgress.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/OverallProgress/OverallProgress.tsx
rename to apps/web/src/app/(main)/dashboard/_components/admin/OverallProgress/OverallProgress.tsx
diff --git a/apps/web/src/app/(main)/_components/admin/PendingUsers/PendingUsers.css.ts b/apps/web/src/app/(main)/dashboard/_components/admin/PendingUsers/PendingUsers.css.ts
similarity index 100%
rename from apps/web/src/app/(main)/_components/admin/PendingUsers/PendingUsers.css.ts
rename to apps/web/src/app/(main)/dashboard/_components/admin/PendingUsers/PendingUsers.css.ts
diff --git a/apps/web/src/app/(main)/_components/admin/PendingUsers/PendingUsers.tsx b/apps/web/src/app/(main)/dashboard/_components/admin/PendingUsers/PendingUsers.tsx
similarity index 77%
rename from apps/web/src/app/(main)/_components/admin/PendingUsers/PendingUsers.tsx
rename to apps/web/src/app/(main)/dashboard/_components/admin/PendingUsers/PendingUsers.tsx
index f46009fd..7aa87ed2 100644
--- a/apps/web/src/app/(main)/_components/admin/PendingUsers/PendingUsers.tsx
+++ b/apps/web/src/app/(main)/dashboard/_components/admin/PendingUsers/PendingUsers.tsx
@@ -16,14 +16,44 @@ export interface User {
export interface PendingUsersProps {
deadline: Date;
+ remainingDays?: number;
remainingHours: number;
+ remainingMinutes?: number;
users: User[];
onRemind: () => void;
}
+const formatRemainingTime = (
+ days?: number,
+ hours?: number,
+ minutes?: number
+): string => {
+ const parts: string[] = [];
+
+ if (days && days > 0) {
+ parts.push(`${days}일`);
+ }
+
+ if (hours && hours > 0) {
+ parts.push(`${hours}시간`);
+ }
+
+ if (minutes && minutes > 0) {
+ parts.push(`${minutes}분`);
+ }
+
+ if (parts.length === 0) {
+ return '마감됨';
+ }
+
+ return `${parts.join(' ')} 남음`;
+};
+
export const PendingUsers = ({
deadline,
+ remainingDays,
remainingHours,
+ remainingMinutes,
users,
onRemind,
}: PendingUsersProps) => (
@@ -53,12 +83,12 @@ export const PendingUsers = ({
평가 마감 : {deadline.getFullYear()}.
{(deadline.getMonth() + 1).toString().padStart(2, '0')}.
- {deadline.getDate().toString().padStart(2, '0')}
+ {deadline.getDate().toString().padStart(2, '0')}{' '}
{deadline.getHours().toString().padStart(2, '0')}:{' '}
{deadline.getMinutes().toString().padStart(2, '0')}
- ({remainingHours}시간 남음)
+ ({formatRemainingTime(remainingDays, remainingHours, remainingMinutes)})
diff --git a/apps/web/src/app/(main)/_components/user/EmptyState/EmptyState.css.ts b/apps/web/src/app/(main)/dashboard/_components/user/EmptyState/EmptyState.css.ts
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/EmptyState/EmptyState.css.ts
rename to apps/web/src/app/(main)/dashboard/_components/user/EmptyState/EmptyState.css.ts
diff --git a/apps/web/src/app/(main)/_components/user/EmptyState/NoAnnouncements.tsx b/apps/web/src/app/(main)/dashboard/_components/user/EmptyState/NoAnnouncements.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/EmptyState/NoAnnouncements.tsx
rename to apps/web/src/app/(main)/dashboard/_components/user/EmptyState/NoAnnouncements.tsx
diff --git a/apps/web/src/app/(main)/_components/user/EmptyState/NoDocs.tsx b/apps/web/src/app/(main)/dashboard/_components/user/EmptyState/NoDocs.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/EmptyState/NoDocs.tsx
rename to apps/web/src/app/(main)/dashboard/_components/user/EmptyState/NoDocs.tsx
diff --git a/apps/web/src/app/(main)/_components/user/EmptyState/NoInterviews.tsx b/apps/web/src/app/(main)/dashboard/_components/user/EmptyState/NoInterviews.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/EmptyState/NoInterviews.tsx
rename to apps/web/src/app/(main)/dashboard/_components/user/EmptyState/NoInterviews.tsx
diff --git a/apps/web/src/app/(main)/_components/user/UserAnnouncementProgress/UserAnnouncementProgress.css.ts b/apps/web/src/app/(main)/dashboard/_components/user/UserAnnouncementProgress/UserAnnouncementProgress.css.ts
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/UserAnnouncementProgress/UserAnnouncementProgress.css.ts
rename to apps/web/src/app/(main)/dashboard/_components/user/UserAnnouncementProgress/UserAnnouncementProgress.css.ts
diff --git a/apps/web/src/app/(main)/_components/user/UserAnnouncementProgress/UserAnnouncementProgress.tsx b/apps/web/src/app/(main)/dashboard/_components/user/UserAnnouncementProgress/UserAnnouncementProgress.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/UserAnnouncementProgress/UserAnnouncementProgress.tsx
rename to apps/web/src/app/(main)/dashboard/_components/user/UserAnnouncementProgress/UserAnnouncementProgress.tsx
diff --git a/apps/web/src/app/(main)/_components/user/UserDocReviewList/UserDocReviewList.css.ts b/apps/web/src/app/(main)/dashboard/_components/user/UserDocReviewList/UserDocReviewList.css.ts
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/UserDocReviewList/UserDocReviewList.css.ts
rename to apps/web/src/app/(main)/dashboard/_components/user/UserDocReviewList/UserDocReviewList.css.ts
diff --git a/apps/web/src/app/(main)/_components/user/UserDocReviewList/UserDocReviewList.tsx b/apps/web/src/app/(main)/dashboard/_components/user/UserDocReviewList/UserDocReviewList.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/UserDocReviewList/UserDocReviewList.tsx
rename to apps/web/src/app/(main)/dashboard/_components/user/UserDocReviewList/UserDocReviewList.tsx
diff --git a/apps/web/src/app/(main)/_components/user/UserHomeHeader/UserHomeHeader.tsx b/apps/web/src/app/(main)/dashboard/_components/user/UserHomeHeader/UserHomeHeader.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/UserHomeHeader/UserHomeHeader.tsx
rename to apps/web/src/app/(main)/dashboard/_components/user/UserHomeHeader/UserHomeHeader.tsx
diff --git a/apps/web/src/app/(main)/_components/user/UserInterviewReview/UserInterviewReview.css.ts b/apps/web/src/app/(main)/dashboard/_components/user/UserInterviewReview/UserInterviewReview.css.ts
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/UserInterviewReview/UserInterviewReview.css.ts
rename to apps/web/src/app/(main)/dashboard/_components/user/UserInterviewReview/UserInterviewReview.css.ts
diff --git a/apps/web/src/app/(main)/_components/user/UserInterviewReview/UserInterviewReview.tsx b/apps/web/src/app/(main)/dashboard/_components/user/UserInterviewReview/UserInterviewReview.tsx
similarity index 100%
rename from apps/web/src/app/(main)/_components/user/UserInterviewReview/UserInterviewReview.tsx
rename to apps/web/src/app/(main)/dashboard/_components/user/UserInterviewReview/UserInterviewReview.tsx
diff --git a/apps/web/src/app/(main)/dashboard/admin/page.tsx b/apps/web/src/app/(main)/dashboard/admin/page.tsx
new file mode 100644
index 00000000..c60166c7
--- /dev/null
+++ b/apps/web/src/app/(main)/dashboard/admin/page.tsx
@@ -0,0 +1,26 @@
+import { getServerSideTokens } from '@web/api/serverSideTokens';
+import { ServerFetchBoundary } from '@web/store/query/ServerFetchBoundary';
+import { getQueryClient } from '@web/store/query/getQueryClient';
+import { getCurrentRecruitmentsSummaryQueryOptions } from '@web/store/query/useCurrentRecruitmentsSummaryQuery';
+import { AdminHomeEmptyScreen } from '../_components/AdminHomeEmptyScreen';
+import { AdminHomeDashboardScreen } from '../_components/AdminHomeDashboardScreen';
+
+export default async function AdminDashboardPage() {
+ const tokens = await getServerSideTokens();
+
+ const queryClient = getQueryClient();
+ const summaryOptions = getCurrentRecruitmentsSummaryQueryOptions(tokens);
+
+ const summaries = await queryClient.fetchQuery(summaryOptions);
+
+ if (!summaries.length) {
+ return ;
+ }
+
+ const recruitmentId = summaries[0]?.recruitmentId as number;
+ return (
+
+
+
+ );
+}
diff --git a/apps/web/src/app/(main)/dashboard/user/page.tsx b/apps/web/src/app/(main)/dashboard/user/page.tsx
new file mode 100644
index 00000000..e250eea6
--- /dev/null
+++ b/apps/web/src/app/(main)/dashboard/user/page.tsx
@@ -0,0 +1,47 @@
+import { cookies } from 'next/headers';
+import { getServerSideTokens } from '@web/api/serverSideTokens';
+import { ServerFetchBoundary } from '@web/store/query/ServerFetchBoundary';
+import { getQueryClient } from '@web/store/query/getQueryClient';
+import { getOrganizationsMeQueryOptions } from '@web/store/query/useOrganizationsMeQuery';
+import { getRecruitmentsCurrentSummaryQueryOptions } from '@web/store/query/useRecruitmentsCurrentSummaryQuery';
+import { getMyDocumentEvaluationsQueryOptions } from '@web/store/query/useMyDocumentEvaluationsQuery';
+import { UserHomeEmptyScreen } from '../_components/UserHomeEmptyScreen';
+import UserHomeDashboardScreen from '@web/app/(main)/dashboard/_components/UserHomeDashboardScreen';
+
+export default async function UserDashboardPage() {
+ const roleRaw = (await cookies()).get('role')?.value;
+
+ const tokens = await getServerSideTokens();
+ const queryClient = getQueryClient();
+
+ const orgOptions = getOrganizationsMeQueryOptions(tokens);
+ const orgs = (await queryClient.fetchQuery(orgOptions)) ?? [];
+ if (!orgs.length) {
+ return ;
+ }
+ const organizationId = orgs[0]?.id as number;
+
+ const summaryOptions = getRecruitmentsCurrentSummaryQueryOptions(
+ organizationId,
+ tokens
+ );
+ const summaries = (await queryClient.fetchQuery(summaryOptions)) ?? [];
+ if (!summaries.length) {
+ return ;
+ }
+ const recruitmentId = summaries[0]?.recruitmentId as number;
+
+ const docEvalOptions = getMyDocumentEvaluationsQueryOptions(
+ recruitmentId,
+ tokens
+ );
+ await queryClient.fetchQuery(docEvalOptions);
+
+ return (
+
+
+
+ );
+}
diff --git a/apps/web/src/app/(main)/page.tsx b/apps/web/src/app/(main)/page.tsx
index f57344b4..5e3ef3a9 100644
--- a/apps/web/src/app/(main)/page.tsx
+++ b/apps/web/src/app/(main)/page.tsx
@@ -1,29 +1,12 @@
-'use client';
-import { AdminHomeDashboardScreen } from '@web/app/(main)/_components/home/Admin/AdminHomeDashboardScreen';
-import { AdminHomeEmptyScreen } from '@web/app/(main)/_components/home/Admin/AdminHomeEmptyScreen';
-import { UserHomeDashboardScreen } from '@web/app/(main)/_components/home/User/UserHomeDashboardScreen';
-import { UserHomeEmptyScreen } from '@web/app/(main)/_components/home/User/UserHomeEmptyScreen';
-import { getCookie } from 'cookies-next';
-import { useMemo } from 'react';
+import { cookies } from 'next/headers';
+import { redirect } from 'next/navigation';
-export default function HomePage() {
- const role = useMemo<'admin' | 'user'>(() => {
- const raw = getCookie('role');
- return raw === 'ADMIN' ? 'admin' : 'user';
- }, []);
+export default async function RootPage() {
+ const role = (await cookies()).get('role')?.value;
- // 임시 값
- const adminHasData = true;
- const userHasData = true;
- if (role === 'admin') {
- // 관리자 홈
- return adminHasData ? (
-
- ) : (
-
- );
+ if (role === 'ADMIN') {
+ redirect('/dashboard/admin');
+ } else {
+ redirect('/dashboard/user');
}
-
- // 일반 사용자 홈
- return userHasData ? : ;
}
diff --git a/apps/web/src/store/constants/queryKeys.ts b/apps/web/src/store/constants/queryKeys.ts
index bf5b7100..567dcca7 100644
--- a/apps/web/src/store/constants/queryKeys.ts
+++ b/apps/web/src/store/constants/queryKeys.ts
@@ -42,10 +42,15 @@ export const queryKeys = {
size,
] as const,
},
+ me: () => ['organizations', 'me'] as const,
},
recruitments: {
list: (keyword?: string) => ['recruitments', keyword ?? ''] as const,
slug: (slug: string) => ['recruitments', 'slug', slug] as const,
+ currentSummary: (organizationId: number) =>
+ ['recruitments', organizationId, 'current', 'summary'] as const,
+ myDocumentEvaluations: (recruitmentId: number) =>
+ ['recruitments', recruitmentId, 'my', 'evaluations', 'documents'] as const,
},
recruitment: {
list: () => ['recruitment', 'list'] as const,
@@ -131,4 +136,12 @@ export const queryKeys = {
user: {
myPage: () => ['user', 'myPage'] as const,
},
+ admin: {
+ currentRecruitmentsSummary: () =>
+ ['admin', 'recruitments', 'current', 'summary'] as const,
+ recruitmentProgress: ( recruitmentId: number, stage: 'DOCUMENT' | 'INTERVIEW') =>
+ ['admin', 'recruitments', recruitmentId, 'progress', stage] as const,
+ recruitmentPendingEvaluators: (recruitmentId: number) =>
+ ['admin', 'recruitments', recruitmentId, 'pendingEvaluators'] as const,
+ } as const,
} as const;
diff --git a/apps/web/src/store/query/ServerFetchBoundary.tsx b/apps/web/src/store/query/ServerFetchBoundary.tsx
index fc5f319f..93dff5a3 100644
--- a/apps/web/src/store/query/ServerFetchBoundary.tsx
+++ b/apps/web/src/store/query/ServerFetchBoundary.tsx
@@ -13,24 +13,26 @@ export type FetchOptions<
'queryKey' | 'queryFn' | 'staleTime' | 'gcTime'
>;
-type Props<
- TQueryFnData = unknown,
- TError = Error,
- TData = TQueryFnData,
- TQueryKey extends QueryKey = QueryKey,
-> = {
- fetchOptions:
- | FetchOptions[]
- | FetchOptions;
+// type Props<
+// TQueryFnData = unknown,
+// TError = Error,
+// TData = TQueryFnData,
+// TQueryKey extends QueryKey = QueryKey,
+// > = {
+// fetchOptions:
+// | FetchOptions[]
+// | FetchOptions;
+// children: ReactNode | ReactNode[];
+// };
+
+type AnyFetchOptions = FetchOptions;
+
+type Props = {
+ fetchOptions: AnyFetchOptions | AnyFetchOptions[];
children: ReactNode | ReactNode[];
};
-export async function ServerFetchBoundary<
- TQueryFnData = unknown,
- TError = Error,
- TData = TQueryFnData,
- TQueryKey extends QueryKey = QueryKey,
->({ fetchOptions, children }: Props) {
+export async function ServerFetchBoundary({ fetchOptions, children }: Props) {
const queryClient = getQueryClient();
const options = Array.isArray(fetchOptions) ? fetchOptions : [fetchOptions];
diff --git a/apps/web/src/store/query/useCurrentRecruitmentsSummaryQuery.ts b/apps/web/src/store/query/useCurrentRecruitmentsSummaryQuery.ts
new file mode 100644
index 00000000..c8331fbc
--- /dev/null
+++ b/apps/web/src/store/query/useCurrentRecruitmentsSummaryQuery.ts
@@ -0,0 +1,45 @@
+import type { UseSuspenseQueryOptions } from '@tanstack/react-query';
+import { useSuspenseQuery } from '@tanstack/react-query';
+import { GET } from '@web/api/fetch';
+import { queryKeys } from '@web/store/constants/queryKeys';
+import type { Tokens } from '@web/api/types';
+
+export interface RecruitmentSummaryDto {
+ recruitmentId: number;
+ title: string;
+ dDays: {
+ label: string;
+ date: string;
+ daysRemaining: number;
+ isPassed: boolean;
+ }[];
+ organizationName: string;
+ totalApplicants: number;
+ positionCounts: {
+ positionName: string;
+ count: number;
+ }[];
+}
+
+const STALE_TIME = 1000 * 60;
+
+export function getCurrentRecruitmentsSummaryQueryOptions(
+ tokens?: Tokens
+): UseSuspenseQueryOptions {
+ return {
+ queryKey: queryKeys.admin.currentRecruitmentsSummary(),
+ queryFn: () =>
+ GET(
+ 'api/v1/admin/recruitments/current/summary',
+ undefined,
+ tokens
+ ).then(res => res.result),
+ staleTime: STALE_TIME,
+ };
+}
+
+export function useCurrentRecruitmentsSummaryQuery(tokens?: Tokens) {
+ return useSuspenseQuery(
+ getCurrentRecruitmentsSummaryQueryOptions(tokens)
+ );
+}
\ No newline at end of file
diff --git a/apps/web/src/store/query/useMyDocumentEvaluationsQuery.ts b/apps/web/src/store/query/useMyDocumentEvaluationsQuery.ts
new file mode 100644
index 00000000..35fa14ef
--- /dev/null
+++ b/apps/web/src/store/query/useMyDocumentEvaluationsQuery.ts
@@ -0,0 +1,45 @@
+import type { UseSuspenseQueryOptions } from '@tanstack/react-query';
+import { useSuspenseQuery } from '@tanstack/react-query';
+import { GET } from '@web/api/fetch';
+import { queryKeys } from '@web/store/constants/queryKeys';
+import type { Tokens } from '@web/api/types';
+
+export interface MyEvaluationItemDto {
+ id: number;
+ name: string;
+ email: string;
+ positionName: string;
+ status: string;
+}
+
+export interface MyDocumentEvaluationsDto {
+ pending: MyEvaluationItemDto[];
+ done: MyEvaluationItemDto[];
+}
+
+const STALE_TIME = 1000 * 60;
+
+export function getMyDocumentEvaluationsQueryOptions(
+ recruitmentId: number,
+ tokens: Tokens
+): UseSuspenseQueryOptions {
+ return {
+ queryKey: queryKeys.recruitments.myDocumentEvaluations(recruitmentId),
+ queryFn: () =>
+ GET(
+ `api/v1/recruitments/${recruitmentId}/my/evaluations/documents`,
+ undefined,
+ tokens
+ ).then(res => res.result),
+ staleTime: STALE_TIME,
+ };
+}
+
+export function useMyDocumentEvaluationsQuery(
+ recruitmentId: number,
+ tokens: Tokens
+) {
+ return useSuspenseQuery(
+ getMyDocumentEvaluationsQueryOptions(recruitmentId, tokens)
+ );
+}
\ No newline at end of file
diff --git a/apps/web/src/store/query/useOrganizationsMeQuery.ts b/apps/web/src/store/query/useOrganizationsMeQuery.ts
new file mode 100644
index 00000000..4d728f2e
--- /dev/null
+++ b/apps/web/src/store/query/useOrganizationsMeQuery.ts
@@ -0,0 +1,33 @@
+import type { UseSuspenseQueryOptions } from '@tanstack/react-query';
+import { useSuspenseQuery } from '@tanstack/react-query';
+import { GET } from '@web/api/fetch';
+import { queryKeys } from '@web/store/constants/queryKeys';
+import type { Tokens } from '@web/api/types';
+
+export interface OrganizationDto {
+ id: number;
+ name: string;
+}
+
+const STALE_TIME = 1000 * 60;
+
+export function getOrganizationsMeQueryOptions(
+ tokens: Tokens
+): UseSuspenseQueryOptions {
+ return {
+ queryKey: queryKeys.organization.me(),
+ queryFn: () =>
+ GET(
+ 'api/v1/organizations/me',
+ undefined,
+ tokens
+ ).then(res => res.result),
+ staleTime: STALE_TIME,
+ };
+}
+
+export function useOrganizationsMeQuery(tokens: Tokens) {
+ return useSuspenseQuery(
+ getOrganizationsMeQueryOptions(tokens)
+ );
+}
\ No newline at end of file
diff --git a/apps/web/src/store/query/useRecruitmentPendingEvaluatorsQuery.ts b/apps/web/src/store/query/useRecruitmentPendingEvaluatorsQuery.ts
new file mode 100644
index 00000000..5c3ab1f1
--- /dev/null
+++ b/apps/web/src/store/query/useRecruitmentPendingEvaluatorsQuery.ts
@@ -0,0 +1,45 @@
+import type { UseSuspenseQueryOptions } from '@tanstack/react-query';
+import { useSuspenseQuery } from '@tanstack/react-query';
+import { GET } from '@web/api/fetch';
+import { queryKeys } from '@web/store/constants/queryKeys';
+import type { Tokens } from '@web/api/types';
+
+export interface RecruitmentPendingEvaluatorsDto {
+ stage: 'DOCUMENT' | 'INTERVIEW';
+ deadline: string;
+ daysToDeadline: number;
+ hoursToDeadline: number;
+ minutesToDeadline: number;
+ users: {
+ userId: number;
+ name: string;
+ profileImageUrl: string | null;
+ }[];
+}
+
+const STALE_TIME = 1000 * 60;
+
+export function getRecruitmentPendingEvaluatorsQueryOptions(
+ recruitmentId: number,
+ tokens?: Tokens
+): UseSuspenseQueryOptions {
+ return {
+ queryKey: queryKeys.admin.recruitmentPendingEvaluators(recruitmentId),
+ queryFn: () =>
+ GET(
+ `api/v1/admin/recruitments/${recruitmentId}/pending-evaluators`,
+ undefined,
+ tokens
+ ).then(res => res.result),
+ staleTime: STALE_TIME,
+ };
+}
+
+export function useRecruitmentPendingEvaluatorsQuery(
+ recruitmentId: number,
+ tokens?: Tokens
+) {
+ return useSuspenseQuery(
+ getRecruitmentPendingEvaluatorsQueryOptions(recruitmentId, tokens)
+ );
+}
\ No newline at end of file
diff --git a/apps/web/src/store/query/useRecruitmentProgressQuery.ts b/apps/web/src/store/query/useRecruitmentProgressQuery.ts
new file mode 100644
index 00000000..ac324a88
--- /dev/null
+++ b/apps/web/src/store/query/useRecruitmentProgressQuery.ts
@@ -0,0 +1,43 @@
+import type { UseSuspenseQueryOptions } from '@tanstack/react-query';
+import { useSuspenseQuery } from '@tanstack/react-query';
+import { GET } from '@web/api/fetch';
+import { queryKeys } from '@web/store/constants/queryKeys';
+import type { Tokens } from '@web/api/types';
+
+export interface RecruitmentProgressDto {
+ positionName: string;
+ daysToDeadline: number;
+ totalToEvaluate: number;
+ evaluatedCount: number;
+ notEvaluatedCount: number;
+ progressPercent: number;
+}
+
+const STALE_TIME = 1000 * 60;
+
+export function getRecruitmentProgressQueryOptions(
+ recruitmentId: number,
+ stage: 'DOCUMENT' | 'INTERVIEW',
+ tokens?: Tokens
+): UseSuspenseQueryOptions {
+ return {
+ queryKey: queryKeys.admin.recruitmentProgress(recruitmentId, stage),
+ queryFn: () =>
+ GET(
+ `api/v1/admin/recruitments/${recruitmentId}/progress`,
+ { stage },
+ tokens
+ ).then(res => res.result),
+ staleTime: STALE_TIME,
+ };
+}
+
+export function useRecruitmentProgressQuery(
+ recruitmentId: number,
+ stage: 'DOCUMENT' | 'INTERVIEW',
+ tokens?: Tokens
+) {
+ return useSuspenseQuery(
+ getRecruitmentProgressQueryOptions(recruitmentId, stage, tokens)
+ );
+}
\ No newline at end of file
diff --git a/apps/web/src/store/query/useRecruitmentsCurrentSummaryQuery.ts b/apps/web/src/store/query/useRecruitmentsCurrentSummaryQuery.ts
new file mode 100644
index 00000000..133c8be0
--- /dev/null
+++ b/apps/web/src/store/query/useRecruitmentsCurrentSummaryQuery.ts
@@ -0,0 +1,49 @@
+import type { UseSuspenseQueryOptions } from '@tanstack/react-query';
+import { useSuspenseQuery } from '@tanstack/react-query';
+import { GET } from '@web/api/fetch';
+import { queryKeys } from '@web/store/constants/queryKeys';
+import type { Tokens } from '@web/api/types';
+
+export interface RecruitmentSummaryDto {
+ recruitmentId: number;
+ title: string;
+ dDays: {
+ label: string;
+ date: string;
+ daysRemaining: number;
+ isPassed: boolean;
+ }[];
+ organizationName: string;
+ totalApplicants: number;
+ positionCounts: {
+ positionName: string;
+ count: number;
+ }[];
+}
+
+const STALE_TIME = 1000 * 60;
+
+export function getRecruitmentsCurrentSummaryQueryOptions(
+ organizationId: number,
+ tokens: Tokens
+): UseSuspenseQueryOptions {
+ return {
+ queryKey: queryKeys.recruitments.currentSummary(organizationId),
+ queryFn: () =>
+ GET(
+ `api/v1/recruitments/${organizationId}/current/summary`,
+ undefined,
+ tokens
+ ).then(res => res.result),
+ staleTime: STALE_TIME,
+ };
+}
+
+export function useRecruitmentsCurrentSummaryQuery(
+ organizationId: number,
+ tokens: Tokens
+) {
+ return useSuspenseQuery(
+ getRecruitmentsCurrentSummaryQueryOptions(organizationId, tokens)
+ );
+}
\ No newline at end of file