Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) => (
Expand Down Expand Up @@ -53,12 +83,12 @@ export const PendingUsers = ({
<Text variant="xs_caption_medium" color="grayscale50">
평가 마감 : {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')}
</Text>
<Text variant="xs_caption_medium" color="error">
({remainingHours}시간 남음)
({formatRemainingTime(remainingDays, remainingHours, remainingMinutes)})
</Text>
</div>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
'use client';

import React from 'react';
import { useRouter } from 'next/navigation';
import { Flex } from '@repo/ui/Flex';
import { AdminHomeHeader } from '@web/app/(main)/_components/admin/AdminHomeHeader/AdminHomeHeader';
import {
AnnounceCard,
AnnounceCardProps,
Expand All @@ -14,110 +20,158 @@ 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';
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';

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('지원 현황으로 이동');
},
};
interface AdminHomeDashboardScreenProps {
recruitmentId: number;
tokens?: Tokens;
}

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('다음 달'),
};
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 tasks: Task[] = [
{
id: 't1',
type: '서류',
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: '서류 평가',
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('리마인드 알림 발송');
},
};
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, '면접'),
];

export const AdminHomeDashboardScreen = () => (
<Flex
width="100%"
direction="column"
gap="4rem"
paddingBottom="2.4rem"
paddingLeft="2.4rem"
paddingRight="2.4rem"
paddingTop="2.4rem"
>
<AdminHomeHeader />
<Flex width="100%" direction="column" gap="2rem">
<AnnounceCard {...announceData} />
<Flex width="100%" gap="2rem">
<DocTimeline {...docTimelineData} />
<OverallProgress tasks={tasks} />
<PendingUsers {...pendingUsersData} />
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 (
<Flex
width="100%"
direction="column"
gap="4rem"
paddingLeft="2.4rem"
paddingTop="2.4rem"
paddingRight="2.4rem"
paddingBottom="2.4rem"
>
<AdminHomeHeader />
<Flex width="100%" direction="column" gap="2rem">
<AnnounceCard {...announceData} />
<Flex width="100%" gap="2rem">
<DocTimeline {...docTimelineData} />
<OverallProgress tasks={tasks} />
<PendingUsers {...pendingUsersData} />
</Flex>
</Flex>
</Flex>
</Flex>
);
);
};
Loading