From cb0d001b6581bb600aa40493d4c9112336cbd1cc Mon Sep 17 00:00:00 2001 From: HaJunRyu Date: Sun, 28 Jan 2024 01:17:34 +0900 Subject: [PATCH 1/5] =?UTF-8?q?Feat:=20=EB=AA=A8=EC=A7=91=20=EC=9D=BC?= =?UTF-8?q?=EC=A0=95=20api=20response=EA=B0=92=EC=9D=84=20=EA=B8=B0?= =?UTF-8?q?=EC=A4=80=EC=9C=BC=EB=A1=9C=20=EB=AA=A8=EC=A7=91=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=EB=A5=BC=20=ED=8C=90=EB=8B=A8=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware.ts | 24 +++++-- pages/apply/[platformName].tsx | 70 +++++++++++-------- pages/index.tsx | 31 +++++++- pages/my-page/apply-status.tsx | 14 +++- src/api/services/application.ts | 11 +++ .../common/Footer/Footer.component.tsx | 33 +++++++-- .../MainNavigation.component.tsx | 31 ++++++-- .../ApplyLinkButton.component.tsx | 32 +++++++-- src/types/dto/application.ts | 24 +++++++ src/utils/date.ts | 43 ++++++++---- 10 files changed, 249 insertions(+), 64 deletions(-) diff --git a/middleware.ts b/middleware.ts index 860bf9aa..4b9430b7 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,12 +1,28 @@ -import { HOME_PAGE, PREFIX } from '@/constants'; -import { getRecruitingProgressStatusFromRecruitingPeriod } from '@/utils/date'; +import { CURRENT_GENERATION, HOME_PAGE, PREFIX } from '@/constants'; +import { RecruitSchedules } from '@/types/dto'; +import { + generateRecruitSchedule, + getRecruitingProgressStatusFromRecruitingPeriod, +} from '@/utils/date'; import type { NextRequest } from 'next/server'; import { NextResponse } from 'next/server'; const blockedPaths = Object.values(PREFIX); -export function middleware(request: NextRequest) { - const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod(new Date()); +export async function middleware(request: NextRequest) { + const recruitScheduleResponse = await fetch( + `${process.env.BASE_URL}/api/applications/schedule/${CURRENT_GENERATION}`, + ); + + const { data: recruitSchedules }: { data: RecruitSchedules } = + await recruitScheduleResponse.json(); + + const recruitSchedule = generateRecruitSchedule(recruitSchedules); + + const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod({ + date: new Date(), + recruitSchedule, + }); const { pathname } = request.nextUrl; const isBlockedPath = blockedPaths.find((path) => pathname.includes(path)); diff --git a/pages/apply/[platformName].tsx b/pages/apply/[platformName].tsx index e8e42601..fa068f4e 100644 --- a/pages/apply/[platformName].tsx +++ b/pages/apply/[platformName].tsx @@ -18,7 +18,10 @@ import { Application } from '@/types/dto'; import { GetServerSideProps } from 'next'; import { getSession } from 'next-auth/react'; import { useRouter } from 'next/router'; -import { getRecruitingProgressStatusFromRecruitingPeriod } from '@/utils/date'; +import { + generateRecruitSchedule, + getRecruitingProgressStatusFromRecruitingPeriod, +} from '@/utils/date'; interface ApplyProps { application: Application; @@ -39,40 +42,49 @@ const Apply = ({ application, isSubmitted }: ApplyProps) => { }; export const getServerSideProps: GetServerSideProps = async (context) => { - const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod(new Date()); + try { + const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ + generationNumber: CURRENT_GENERATION, + }); - if (recruitingProgressStatus !== 'IN-RECRUITING') { - return { - redirect: { - permanent: false, - destination: HOME_PAGE, - }, - }; - } + const recruitSchedule = generateRecruitSchedule(recruitScheduleResponse); - const currentApplyPlatform = teamNames[context.params?.platformName as Teams]; + const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod({ + date: new Date(), + recruitSchedule, + }); - if (!currentApplyPlatform) { - return { - redirect: { - permanent: false, - destination: NOT_FOUND_PAGE, - }, - }; - } + if (recruitingProgressStatus !== 'IN-RECRUITING') { + return { + redirect: { + permanent: false, + destination: HOME_PAGE, + }, + }; + } - const session = await getSession(context); + const currentApplyPlatform = teamNames[context.params?.platformName as Teams]; - if (!session) { - return { - redirect: { - permanent: false, - destination: HOME_PAGE, - }, - }; - } + if (!currentApplyPlatform) { + return { + redirect: { + permanent: false, + destination: NOT_FOUND_PAGE, + }, + }; + } + + const session = await getSession(context); + + if (!session) { + return { + redirect: { + permanent: false, + destination: HOME_PAGE, + }, + }; + } - try { const applications = ( await applicationApiService.getApplications({ accessToken: session?.accessToken, diff --git a/pages/index.tsx b/pages/index.tsx index a7b0453b..f9871004 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,3 +1,4 @@ +import { applicationApiService } from '@/api/services'; import { HomeLayout, RecruitingDetailNavigation, @@ -7,22 +8,46 @@ import { RecruitingPeriod, RecruitingRemainder, } from '@/components'; +import { CURRENT_GENERATION } from '@/constants'; import { useAOS } from '@/hooks'; -import { getRecruitingProgressStatusFromRecruitingPeriod } from '@/utils/date'; +import { RecruitSchedule } from '@/types/dto'; +import { + generateRecruitSchedule, + getRecruitingProgressStatusFromRecruitingPeriod, +} from '@/utils/date'; import type { RecruitingProgressStatus } from '@/utils/date'; import { useEffect, useState } from 'react'; const Home = () => { useAOS(); + const [recruitSchedule, setRecruitSchedule] = useState(null); + + useEffect(() => { + const fetchRecruitSchedule = async () => { + const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ + generationNumber: CURRENT_GENERATION, + }); + + setRecruitSchedule(generateRecruitSchedule(recruitScheduleResponse)); + }; + + fetchRecruitSchedule(); + }, []); + const [recruitingProgressStatus, setRecruitingProgressStatus] = useState< RecruitingProgressStatus | 'NOT_INITIALIZED' >('NOT_INITIALIZED'); useEffect(() => { - setRecruitingProgressStatus(getRecruitingProgressStatusFromRecruitingPeriod(new Date())); - }, []); + setRecruitingProgressStatus( + getRecruitingProgressStatusFromRecruitingPeriod({ + date: new Date(), + recruitSchedule, + }), + ); + }, [recruitSchedule]); return ( <> diff --git a/pages/my-page/apply-status.tsx b/pages/my-page/apply-status.tsx index 9fda4978..288f101f 100644 --- a/pages/my-page/apply-status.tsx +++ b/pages/my-page/apply-status.tsx @@ -1,9 +1,10 @@ import { applicationApiService } from '@/api/services'; import { ApplyStatusLayout } from '@/components'; -import { ERROR_PAGE, HOME_PAGE } from '@/constants'; +import { CURRENT_GENERATION, ERROR_PAGE, HOME_PAGE } from '@/constants'; import { Application } from '@/types/dto'; import { sortByGenerationAndTeam } from '@/utils/application'; import { + generateRecruitSchedule, getRecruitingProgressStatusFromRecruitingPeriod, RecruitingProgressStatus, } from '@/utils/date'; @@ -41,7 +42,16 @@ export const getServerSideProps: GetServerSideProps = async (context) => { const applications = sortByGenerationAndTeam(applicationsRes.data); - const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod(new Date()); + const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ + generationNumber: CURRENT_GENERATION, + }); + + const recruitSchedule = generateRecruitSchedule(recruitScheduleResponse); + + const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod({ + date: new Date(), + recruitSchedule, + }); return { props: { applications, recruitingProgressStatus } }; } catch (error) { diff --git a/src/api/services/application.ts b/src/api/services/application.ts index 68c43a4b..ddc5e5b4 100644 --- a/src/api/services/application.ts +++ b/src/api/services/application.ts @@ -7,6 +7,8 @@ import { ConfirmApplicantResponse, CreateMyApplicationRequest, CreateMyApplicationResponse, + RecruitScheduleRequest, + RecruitScheduleResponse, SubmitApplicationRequest, SubmitApplicationResponse, TempSaveApplicationRequest, @@ -86,6 +88,15 @@ class ApplicationApiService extends BaseApiService { .then(BaseApiService.handleResponse) .catch(BaseApiService.handleError); } + + public getRecruitSchedule({ + generationNumber, + }: RecruitScheduleRequest): Promise { + return this.http + .get(`/schedule/${generationNumber}`) + .then(BaseApiService.handleResponse) + .catch(BaseApiService.handleError); + } } export default new ApplicationApiService(); diff --git a/src/components/common/Footer/Footer.component.tsx b/src/components/common/Footer/Footer.component.tsx index 9c4081fa..f028108a 100644 --- a/src/components/common/Footer/Footer.component.tsx +++ b/src/components/common/Footer/Footer.component.tsx @@ -5,16 +5,41 @@ import Behance32 from '@/assets/svg/behance-32.svg'; import Facebook32 from '@/assets/svg/facebook-32.svg'; import Instagram32 from '@/assets/svg/instagram-32.svg'; import { useRouter } from 'next/router'; -import { HOME_PAGE } from '@/constants'; -import { getRecruitingProgressStatusFromRecruitingPeriod } from '@/utils/date'; +import { CURRENT_GENERATION, HOME_PAGE } from '@/constants'; +import { + generateRecruitSchedule, + getRecruitingProgressStatusFromRecruitingPeriod, +} from '@/utils/date'; +import { RecruitSchedule } from '@/types/dto'; +import { useEffect, useState } from 'react'; +import { applicationApiService } from '@/api/services'; import * as Styled from './Footer.styled'; const Footer = () => { const { pathname: currentPage } = useRouter(); - const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod(new Date()); + + const [recruitSchedule, setRecruitSchedule] = useState(null); + + const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod({ + date: new Date(), + recruitSchedule, + }); + + useEffect(() => { + const fetchRecruitSchedule = async () => { + const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ + generationNumber: CURRENT_GENERATION, + }); + + setRecruitSchedule(generateRecruitSchedule(recruitScheduleResponse)); + }; + + fetchRecruitSchedule(); + }, []); + return ( <> - {recruitingProgressStatus === 'PREVIOUS' && null} + {(recruitingProgressStatus === 'PREVIOUS' || recruitingProgressStatus === 'INVALID') && null} {recruitingProgressStatus !== 'PREVIOUS' && ( diff --git a/src/components/common/MainNavigation/MainNavigation.component.tsx b/src/components/common/MainNavigation/MainNavigation.component.tsx index d5b62972..cc1e6820 100644 --- a/src/components/common/MainNavigation/MainNavigation.component.tsx +++ b/src/components/common/MainNavigation/MainNavigation.component.tsx @@ -1,4 +1,4 @@ -import { FAQ_COMMON_PAGE, HOME_PAGE, RECRUIT_DETAILS_ID } from '@/constants'; +import { CURRENT_GENERATION, FAQ_COMMON_PAGE, HOME_PAGE, RECRUIT_DETAILS_ID } from '@/constants'; import { LinkTo, SignInModal, MyPageTab } from '@/components'; import { MouseEventHandler, MutableRefObject, useEffect, useRef, useState } from 'react'; import { useDetectOutsideClick } from '@/hooks'; @@ -6,13 +6,36 @@ import Router, { useRouter } from 'next/router'; import { useSession } from 'next-auth/react'; import ChevronBottom12 from '@/assets/svg/chevron-bottom-12.svg'; import { colors } from '@/styles'; -import { getRecruitingProgressStatusFromRecruitingPeriod } from '@/utils/date'; +import { + generateRecruitSchedule, + getRecruitingProgressStatusFromRecruitingPeriod, +} from '@/utils/date'; +import { RecruitSchedule } from '@/types/dto'; +import { applicationApiService } from '@/api/services'; import * as Styled from './MainNavigation.styled'; const MainNavigation = () => { const session = useSession(); const { pathname: currentPage } = useRouter(); - const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod(new Date()); + + const [recruitSchedule, setRecruitSchedule] = useState(null); + + const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod({ + date: new Date(), + recruitSchedule, + }); + + useEffect(() => { + const fetchRecruitSchedule = async () => { + const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ + generationNumber: CURRENT_GENERATION, + }); + + setRecruitSchedule(generateRecruitSchedule(recruitScheduleResponse)); + }; + + fetchRecruitSchedule(); + }, []); const isSessionLoading = session.status === 'loading'; @@ -46,7 +69,7 @@ const MainNavigation = () => { return ( <> - {recruitingProgressStatus === 'PREVIOUS' && null} + {(recruitingProgressStatus === 'PREVIOUS' || recruitingProgressStatus === 'INVALID') && null} {recruitingProgressStatus !== 'PREVIOUS' && ( <> diff --git a/src/components/recruit/ApplyLinkButton/ApplyLinkButton.component.tsx b/src/components/recruit/ApplyLinkButton/ApplyLinkButton.component.tsx index df081089..63034004 100644 --- a/src/components/recruit/ApplyLinkButton/ApplyLinkButton.component.tsx +++ b/src/components/recruit/ApplyLinkButton/ApplyLinkButton.component.tsx @@ -1,8 +1,14 @@ import { SignInModal } from '@/components'; import { useSession } from 'next-auth/react'; import { useRouter } from 'next/router'; -import { MutableRefObject, useRef, useState } from 'react'; -import { getRecruitingProgressStatusFromRecruitingPeriod } from '@/utils/date'; +import { MutableRefObject, useEffect, useRef, useState } from 'react'; +import { + generateRecruitSchedule, + getRecruitingProgressStatusFromRecruitingPeriod, +} from '@/utils/date'; +import { RecruitSchedule } from '@/types/dto'; +import { applicationApiService } from '@/api/services'; +import { CURRENT_GENERATION } from '@/constants'; import * as Styled from './ApplyLinkButton.styled'; interface ApplyLinkProps { @@ -15,8 +21,26 @@ const ApplyLinkButton = ({ applyPath }: ApplyLinkProps) => { const buttonRef = useRef(null) as MutableRefObject; const [isOpenSignInModal, setIsOpenSignInModal] = useState(false); - const isRecruitingInProgress = - getRecruitingProgressStatusFromRecruitingPeriod(new Date()) === 'IN-RECRUITING'; + const [recruitSchedule, setRecruitSchedule] = useState(null); + + const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod({ + date: new Date(), + recruitSchedule, + }); + + useEffect(() => { + const fetchRecruitSchedule = async () => { + const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ + generationNumber: CURRENT_GENERATION, + }); + + setRecruitSchedule(generateRecruitSchedule(recruitScheduleResponse)); + }; + + fetchRecruitSchedule(); + }, []); + + const isRecruitingInProgress = recruitingProgressStatus === 'IN-RECRUITING'; const handleLinkButtonClick = () => { if (session.status !== 'authenticated') { diff --git a/src/types/dto/application.ts b/src/types/dto/application.ts index 84150e0e..40009327 100644 --- a/src/types/dto/application.ts +++ b/src/types/dto/application.ts @@ -74,6 +74,25 @@ interface UpdateApplication { privacyPolicyAgreed: boolean; } +export type RecruitScheduleEvent = + | 'RECRUITMENT_STARTED' + | 'RECRUITMENT_ENDED' + | 'SCREENING_RESULT_ANNOUNCED' + | 'INTERVIEW_RESULT_ANNOUNCED' + | 'INTERVIEW_START' + | 'INTERVIEW_END' + | 'AFTER_FIRST_SEMINAR_JOIN'; + +export type RecruitSchedule = Record; + +interface RecruitScheduleSchema { + recruitScheduleId: number; + eventName: RecruitScheduleEvent; + eventOccurredAt: string; +} + +export type RecruitSchedules = Array; + // ------------------- From the bottom, API Request and Response interface ------------------- export interface ApplicationsRequest extends BaseRequest {} @@ -106,3 +125,8 @@ export interface SubmitApplicationRequest extends BaseRequest { applicationSubmitRequest: UpdateApplication; } export interface SubmitApplicationResponse extends BaseResponse {} + +export interface RecruitScheduleRequest { + generationNumber: number; +} +export interface RecruitScheduleResponse extends BaseResponse {} diff --git a/src/utils/date.ts b/src/utils/date.ts index f95ba32b..852325b8 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -1,5 +1,6 @@ import { KeyOf } from '@/types'; -import { DAYS, RECRUIT_DATE } from '@/constants'; +import { DAYS } from '@/constants'; +import { RecruitSchedule, RecruitSchedules } from '@/types/dto'; import { objectKeys } from './object'; export type RecruitingProgressStatus = @@ -17,40 +18,48 @@ const getKSTDateFromDate = (date: Date) => { return new Date(utcUnixTime + diffKSTFromUTC); }; -export const getRecruitingProgressStatusFromRecruitingPeriod = ( - date: Date, -): RecruitingProgressStatus => { +export const getRecruitingProgressStatusFromRecruitingPeriod = ({ + date, + recruitSchedule, +}: { + date: Date; + recruitSchedule: RecruitSchedule | null; +}): RecruitingProgressStatus => { const kstDate = getKSTDateFromDate(date); const currentDate = date.getTime() === kstDate.getTime() ? date : kstDate; - if (currentDate < RECRUIT_DATE.RECRUITMENT_START_KST_DATE) { + if (recruitSchedule === null) { + return 'INVALID'; + } + + if (currentDate < recruitSchedule.RECRUITMENT_STARTED) { return 'PREVIOUS'; } if ( - RECRUIT_DATE.RECRUITMENT_START_KST_DATE <= currentDate && - currentDate <= RECRUIT_DATE.RECRUITMENT_END_KST_DATE + recruitSchedule.RECRUITMENT_STARTED <= currentDate && + currentDate <= recruitSchedule.RECRUITMENT_ENDED ) { return 'IN-RECRUITING'; } if ( - RECRUIT_DATE.RECRUITMENT_END_KST_DATE < currentDate && - currentDate < RECRUIT_DATE.SCREENING_RESULT_ANNOUNCED_KST_DATE + recruitSchedule.RECRUITMENT_ENDED < currentDate && + currentDate < recruitSchedule.SCREENING_RESULT_ANNOUNCED ) { return 'END-RECRUITING'; } if ( - RECRUIT_DATE.SCREENING_RESULT_ANNOUNCED_KST_DATE <= currentDate && - currentDate < RECRUIT_DATE.INTERVIEW_RESULT_ANNOUNCED_KST_DATE + recruitSchedule.SCREENING_RESULT_ANNOUNCED <= currentDate && + currentDate < recruitSchedule.INTERVIEW_RESULT_ANNOUNCED ) { return 'AFTER-SCREENING-ANNOUNCED'; } if ( - RECRUIT_DATE.INTERVIEW_RESULT_ANNOUNCED_KST_DATE <= currentDate && - currentDate < RECRUIT_DATE.AFTER_FIRST_SEMINAR_JOIN_KST_DATE + recruitSchedule.INTERVIEW_RESULT_ANNOUNCED <= currentDate && + currentDate < recruitSchedule.AFTER_FIRST_SEMINAR_JOIN ) { return 'AFTER-INTERVIEWING-ANNOUNCED'; } - if (RECRUIT_DATE.AFTER_FIRST_SEMINAR_JOIN_KST_DATE <= currentDate) { + if (recruitSchedule.AFTER_FIRST_SEMINAR_JOIN <= currentDate) { return 'AFTER-FIRST-SEMINAR'; } return 'INVALID'; @@ -89,3 +98,9 @@ export const getDifferenceOfDates = (startDate: Date, endDate: Date): DateDiffer return Object.assign(dateDifference, { [key]: calculatedValueByKey }); }, {} as DateDifference); }; + +export const generateRecruitSchedule = (recruitSchedules: RecruitSchedules) => { + return recruitSchedules.reduce((acc, { eventName, eventOccurredAt }) => { + return { ...acc, [eventName]: new Date(eventOccurredAt) }; + }, {} as RecruitSchedule); +}; From 22b4058c0c0806659c31caf58dc745dc0046d44b Mon Sep 17 00:00:00 2001 From: HaJunRyu Date: Sun, 28 Jan 2024 01:46:56 +0900 Subject: [PATCH 2/5] =?UTF-8?q?Feat:=20=EB=AA=A8=EC=A7=91=EA=B8=B0?= =?UTF-8?q?=EA=B0=84=20=EA=B4=80=EB=A0=A8=20UI=20=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81=EC=9D=84=20api=EC=97=90=20=EC=9D=98=EC=A1=B4=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- middleware.ts | 4 +- pages/index.tsx | 43 +++++++++++-------- pages/recruit/[platformName].tsx | 23 +++++++--- .../RecruitingOpenHero.component.tsx | 9 +++- .../RecruitingPeriod.component.tsx | 15 ++++--- .../RecruitingPeriodDesktop.component.tsx | 15 ++++--- .../RecruitingProcess.component.tsx | 39 +++++++++-------- .../RecruitingRemainder.component.tsx | 13 ++++-- .../ApplyLinkButton.component.tsx | 27 +++--------- .../BottomNavigation.component.tsx | 6 ++- .../RecruitDate/RecruitDate.component.tsx | 14 +++--- src/constants/index.ts | 1 - src/constants/recruit.ts | 9 ---- src/types/dto/application.ts | 4 +- src/utils/date.ts | 6 +-- 15 files changed, 125 insertions(+), 103 deletions(-) delete mode 100644 src/constants/recruit.ts diff --git a/middleware.ts b/middleware.ts index 4b9430b7..b3e33b73 100644 --- a/middleware.ts +++ b/middleware.ts @@ -1,5 +1,5 @@ import { CURRENT_GENERATION, HOME_PAGE, PREFIX } from '@/constants'; -import { RecruitSchedules } from '@/types/dto'; +import { RecruitScheduleArray } from '@/types/dto'; import { generateRecruitSchedule, getRecruitingProgressStatusFromRecruitingPeriod, @@ -14,7 +14,7 @@ export async function middleware(request: NextRequest) { `${process.env.BASE_URL}/api/applications/schedule/${CURRENT_GENERATION}`, ); - const { data: recruitSchedules }: { data: RecruitSchedules } = + const { data: recruitSchedules }: { data: RecruitScheduleArray } = await recruitScheduleResponse.json(); const recruitSchedule = generateRecruitSchedule(recruitSchedules); diff --git a/pages/index.tsx b/pages/index.tsx index f9871004..4d8447a7 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -11,30 +11,23 @@ import { import { CURRENT_GENERATION } from '@/constants'; import { useAOS } from '@/hooks'; -import { RecruitSchedule } from '@/types/dto'; +import { RecruitScheduleArray } from '@/types/dto'; import { generateRecruitSchedule, getRecruitingProgressStatusFromRecruitingPeriod, } from '@/utils/date'; import type { RecruitingProgressStatus } from '@/utils/date'; +import { GetStaticProps } from 'next'; import { useEffect, useState } from 'react'; -const Home = () => { - useAOS(); - - const [recruitSchedule, setRecruitSchedule] = useState(null); +interface HomeProps { + recruitScheduleArray: RecruitScheduleArray; +} - useEffect(() => { - const fetchRecruitSchedule = async () => { - const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ - generationNumber: CURRENT_GENERATION, - }); - - setRecruitSchedule(generateRecruitSchedule(recruitScheduleResponse)); - }; +const Home = ({ recruitScheduleArray }: HomeProps) => { + useAOS(); - fetchRecruitSchedule(); - }, []); + const recruitSchedule = generateRecruitSchedule(recruitScheduleArray); const [recruitingProgressStatus, setRecruitingProgressStatus] = useState< RecruitingProgressStatus | 'NOT_INITIALIZED' @@ -51,13 +44,15 @@ const Home = () => { return ( <> - {recruitingProgressStatus === 'PREVIOUS' && } + {recruitingProgressStatus === 'PREVIOUS' && ( + + )} {recruitingProgressStatus !== 'PREVIOUS' && ( - - - + + + )} @@ -66,3 +61,13 @@ const Home = () => { }; export default Home; + +export const getStaticProps: GetStaticProps = async () => { + const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ + generationNumber: CURRENT_GENERATION, + }); + + return { + props: { recruitScheduleArray: recruitScheduleResponse }, + }; +}; diff --git a/pages/recruit/[platformName].tsx b/pages/recruit/[platformName].tsx index 48ecc5bf..b601afa6 100644 --- a/pages/recruit/[platformName].tsx +++ b/pages/recruit/[platformName].tsx @@ -1,6 +1,6 @@ import { GetStaticPaths, GetStaticProps, NextPage } from 'next'; import { ParsedUrlQuery } from 'querystring'; -import { PlatformKey, platformKeys, platformMap, platforms } from '@/constants'; +import { CURRENT_GENERATION, PlatformKey, platformKeys, platformMap, platforms } from '@/constants'; import parser from '@/utils/editorjs-html'; import { unescape, flow } from 'lodash-es'; import { @@ -16,8 +16,10 @@ import { RecruitEditorContainer, SEO, } from '@/components'; -import { adminApiService } from '@/api/services'; +import { adminApiService, applicationApiService } from '@/api/services'; import { objectKeys } from '@/utils/object'; +import { RecruitScheduleArray } from '@/types/dto'; +import { generateRecruitSchedule } from '@/utils/date'; interface Params extends ParsedUrlQuery { platformName: PlatformKey; @@ -25,10 +27,11 @@ interface Params extends ParsedUrlQuery { interface PlatformProps { platformName: PlatformKey; + recruitScheduleArray: RecruitScheduleArray; html: string; } -const Platform: NextPage = ({ platformName, html }) => { +const Platform: NextPage = ({ platformName, recruitScheduleArray, html }) => { const { name: currentPlatformName, role: currentPlatformRole, @@ -38,6 +41,8 @@ const Platform: NextPage = ({ platformName, html }) => { const otherPlatforms = platforms.filter(({ key }) => key !== platformName); + const recruitSchedule = generateRecruitSchedule(recruitScheduleArray); + return ( = ({ platformName, html }) => { - + - + ); }; @@ -75,6 +83,10 @@ export const getStaticProps: GetStaticProps = async (cont const removeWrongAmpString = (value: string) => value.replace(/&/g, '&'); + const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ + generationNumber: CURRENT_GENERATION, + }); + const { data } = await adminApiService.getRecruitDataFromStorage({ accessToken: process.env.ADMIN_TOKEN, key: platformName, @@ -88,6 +100,7 @@ export const getStaticProps: GetStaticProps = async (cont return { props: { platformName, + recruitScheduleArray: recruitScheduleResponse, html, }, revalidate: 60 * 60 * 24, diff --git a/src/components/home/RecruitingOpenHero/RecruitingOpenHero.component.tsx b/src/components/home/RecruitingOpenHero/RecruitingOpenHero.component.tsx index 340a71d8..c72a19f4 100644 --- a/src/components/home/RecruitingOpenHero/RecruitingOpenHero.component.tsx +++ b/src/components/home/RecruitingOpenHero/RecruitingOpenHero.component.tsx @@ -4,9 +4,14 @@ import GenerationRight from '@/assets/images/generation-right-2x.png'; import Image from 'next/image'; import { RecruitingPeriodDesktop, Lottie } from '@/components'; import { AOS_BASE_DURATION_DISTANCE, AOS_DEFAULT_DURATION, CURRENT_GENERATION } from '@/constants'; +import { RecruitSchedule } from '@/types/dto'; import * as Styled from './RecruitingOpenHero.styled'; -const RecruitingOpenHero = () => { +interface RecruitingOpenHeroProps { + recruitSchedule: RecruitSchedule; +} + +const RecruitingOpenHero = ({ recruitSchedule }: RecruitingOpenHeroProps) => { return ( @@ -54,7 +59,7 @@ const RecruitingOpenHero = () => { > - + ); diff --git a/src/components/home/RecruitingPeriod/RecruitingPeriod.component.tsx b/src/components/home/RecruitingPeriod/RecruitingPeriod.component.tsx index 6c56c64f..6f0f810f 100644 --- a/src/components/home/RecruitingPeriod/RecruitingPeriod.component.tsx +++ b/src/components/home/RecruitingPeriod/RecruitingPeriod.component.tsx @@ -1,16 +1,21 @@ import { Lottie, ScreenReaderOnly } from '@/components'; -import { AOS_BASE_DURATION_DISTANCE, AOS_DEFAULT_DURATION, RECRUIT_DATE } from '@/constants'; +import { AOS_BASE_DURATION_DISTANCE, AOS_DEFAULT_DURATION } from '@/constants'; import computerLottie from '@/assets/lottie/computer.json'; import PeriodArrow from '@/assets/svg/period-arrow.svg'; import fireLottie from '@/assets/lottie/fire.json'; import dayjs from 'dayjs'; +import { RecruitSchedule } from '@/types/dto'; import * as Styled from './RecruitingPeriod.styled'; -const RecruitingPeriod = () => { - const { RECRUITMENT_START_KST_DATE, RECRUITMENT_END_KST_DATE } = RECRUIT_DATE; +interface RecruitingPeriodProps { + recruitSchedule: RecruitSchedule; +} - const DAYJS_RECRUITMENT_START_KST_DATE = dayjs(RECRUITMENT_START_KST_DATE); - const DAYJS_RECRUITMENT_END_KST_DATE = dayjs(RECRUITMENT_END_KST_DATE); +const RecruitingPeriod = ({ recruitSchedule }: RecruitingPeriodProps) => { + const { RECRUITMENT_STARTED, RECRUITMENT_ENDED } = recruitSchedule; + + const DAYJS_RECRUITMENT_START_KST_DATE = dayjs(RECRUITMENT_STARTED); + const DAYJS_RECRUITMENT_END_KST_DATE = dayjs(RECRUITMENT_ENDED); return ( diff --git a/src/components/home/RecruitingPeriodDesktop/RecruitingPeriodDesktop.component.tsx b/src/components/home/RecruitingPeriodDesktop/RecruitingPeriodDesktop.component.tsx index a1d2bb87..779860bb 100644 --- a/src/components/home/RecruitingPeriodDesktop/RecruitingPeriodDesktop.component.tsx +++ b/src/components/home/RecruitingPeriodDesktop/RecruitingPeriodDesktop.component.tsx @@ -1,16 +1,21 @@ -import { AOS_BASE_DURATION_DISTANCE, AOS_DEFAULT_DURATION, RECRUIT_DATE } from '@/constants'; +import { AOS_BASE_DURATION_DISTANCE, AOS_DEFAULT_DURATION } from '@/constants'; import { Lottie, ScreenReaderOnly } from '@/components'; import computerLottie from '@/assets/lottie/computer.json'; import PeriodArrow from '@/assets/svg/period-arrow.svg'; import fireLottie from '@/assets/lottie/fire.json'; import dayjs from 'dayjs'; +import { RecruitSchedule } from '@/types/dto'; import * as Styled from './RecruitingPeriodDesktop.styled'; -const RecruitingPeriodDesktop = () => { - const { RECRUITMENT_START_KST_DATE, RECRUITMENT_END_KST_DATE } = RECRUIT_DATE; +interface RecruitingPeriodDesktopProps { + recruitSchedule: RecruitSchedule; +} - const DAYJS_RECRUITMENT_START_KST_DATE = dayjs(RECRUITMENT_START_KST_DATE); - const DAYJS_RECRUITMENT_END_KST_DATE = dayjs(RECRUITMENT_END_KST_DATE); +const RecruitingPeriodDesktop = ({ recruitSchedule }: RecruitingPeriodDesktopProps) => { + const { RECRUITMENT_STARTED, RECRUITMENT_ENDED } = recruitSchedule; + + const DAYJS_RECRUITMENT_START_KST_DATE = dayjs(RECRUITMENT_STARTED); + const DAYJS_RECRUITMENT_END_KST_DATE = dayjs(RECRUITMENT_ENDED); return ( diff --git a/src/components/home/RecruitingProcess/RecruitingProcess.component.tsx b/src/components/home/RecruitingProcess/RecruitingProcess.component.tsx index 77b4c35a..78a91f76 100644 --- a/src/components/home/RecruitingProcess/RecruitingProcess.component.tsx +++ b/src/components/home/RecruitingProcess/RecruitingProcess.component.tsx @@ -1,26 +1,31 @@ import { ScreenReaderOnly } from '@/components'; -import { CURRENT_GENERATION, DAYS, RECRUIT_DATE } from '@/constants'; +import { CURRENT_GENERATION, DAYS } from '@/constants'; import dayjs from 'dayjs'; +import { RecruitSchedule } from '@/types/dto'; import * as Styled from './RecruitingProcess.styled'; -const RecruitingProcess = () => { +interface RecruitingProcessProps { + recruitSchedule: RecruitSchedule; +} + +const RecruitingProcess = ({ recruitSchedule }: RecruitingProcessProps) => { const { - RECRUITMENT_START_KST_DATE, - RECRUITMENT_END_KST_DATE, - SCREENING_RESULT_ANNOUNCED_KST_DATE, - INTERVIEW_START_KST_DATE, - INTERVIEW_END_KST_DATE, - INTERVIEW_RESULT_ANNOUNCED_KST_DATE, - AFTER_FIRST_SEMINAR_JOIN_KST_DATE, - } = RECRUIT_DATE; + RECRUITMENT_STARTED, + RECRUITMENT_ENDED, + SCREENING_RESULT_ANNOUNCED, + INTERVIEW_START, + INTERVIEW_END, + INTERVIEW_RESULT_ANNOUNCED, + AFTER_FIRST_SEMINAR_JOIN, + } = recruitSchedule; - const DAYJS_RECRUITMENT_START_KST_DATE = dayjs(RECRUITMENT_START_KST_DATE); - const DAYJS_RECRUITMENT_END_KST_DATE = dayjs(RECRUITMENT_END_KST_DATE); - const DAYJS_SCREENING_RESULT_ANNOUNCED_KST_DATE = dayjs(SCREENING_RESULT_ANNOUNCED_KST_DATE); - const DAYJS_INTERVIEW_START_KST_DATE = dayjs(INTERVIEW_START_KST_DATE); - const DAYJS_INTERVIEW_INTERVIEW_END_KST_DATE = dayjs(INTERVIEW_END_KST_DATE); - const DAYJS_INTERVIEW_RESULT_ANNOUNCED_KST_DATE = dayjs(INTERVIEW_RESULT_ANNOUNCED_KST_DATE); - const DAYJS_AFTER_FIRST_SEMINAR_JOIN_KST_DATE = dayjs(AFTER_FIRST_SEMINAR_JOIN_KST_DATE); + const DAYJS_RECRUITMENT_START_KST_DATE = dayjs(RECRUITMENT_STARTED); + const DAYJS_RECRUITMENT_END_KST_DATE = dayjs(RECRUITMENT_ENDED); + const DAYJS_SCREENING_RESULT_ANNOUNCED_KST_DATE = dayjs(SCREENING_RESULT_ANNOUNCED); + const DAYJS_INTERVIEW_START_KST_DATE = dayjs(INTERVIEW_START); + const DAYJS_INTERVIEW_INTERVIEW_END_KST_DATE = dayjs(INTERVIEW_END); + const DAYJS_INTERVIEW_RESULT_ANNOUNCED_KST_DATE = dayjs(INTERVIEW_RESULT_ANNOUNCED); + const DAYJS_AFTER_FIRST_SEMINAR_JOIN_KST_DATE = dayjs(AFTER_FIRST_SEMINAR_JOIN); return ( diff --git a/src/components/home/RecruitingRemainder/RecruitingRemainder.component.tsx b/src/components/home/RecruitingRemainder/RecruitingRemainder.component.tsx index 4fb0cab1..aa294926 100644 --- a/src/components/home/RecruitingRemainder/RecruitingRemainder.component.tsx +++ b/src/components/home/RecruitingRemainder/RecruitingRemainder.component.tsx @@ -4,14 +4,19 @@ import { getDifferenceOfDates } from '@/utils/date'; import type { DateDifference } from '@/utils/date'; import { useState, useEffect } from 'react'; -import { CURRENT_GENERATION, RECRUIT_DATE } from '@/constants'; +import { CURRENT_GENERATION } from '@/constants'; +import { RecruitSchedule } from '@/types/dto'; import * as Styled from './RecruitingRemainder.styled'; -const RecruitingRemainder = () => { +interface RecruitingRemainderProps { + recruitSchedule: RecruitSchedule; +} + +const RecruitingRemainder = ({ recruitSchedule }: RecruitingRemainderProps) => { const router = useRouter(); const [difference, setDifference] = useState(() => - getDifferenceOfDates(new Date(), RECRUIT_DATE.RECRUITMENT_START_KST_DATE), + getDifferenceOfDates(new Date(), recruitSchedule.RECRUITMENT_STARTED), ); const [isPreviousDayOfRecruitingStart, isRunOutTimeOfRecruitingStart] = [ @@ -23,7 +28,7 @@ const RecruitingRemainder = () => { () => { const currentDifference = getDifferenceOfDates( new Date(), - RECRUIT_DATE.RECRUITMENT_START_KST_DATE, + recruitSchedule.RECRUITMENT_STARTED, ); setDifference(currentDifference); }, diff --git a/src/components/recruit/ApplyLinkButton/ApplyLinkButton.component.tsx b/src/components/recruit/ApplyLinkButton/ApplyLinkButton.component.tsx index 63034004..08a95241 100644 --- a/src/components/recruit/ApplyLinkButton/ApplyLinkButton.component.tsx +++ b/src/components/recruit/ApplyLinkButton/ApplyLinkButton.component.tsx @@ -1,45 +1,28 @@ import { SignInModal } from '@/components'; import { useSession } from 'next-auth/react'; import { useRouter } from 'next/router'; -import { MutableRefObject, useEffect, useRef, useState } from 'react'; -import { - generateRecruitSchedule, - getRecruitingProgressStatusFromRecruitingPeriod, -} from '@/utils/date'; +import { MutableRefObject, useRef, useState } from 'react'; +import { getRecruitingProgressStatusFromRecruitingPeriod } from '@/utils/date'; import { RecruitSchedule } from '@/types/dto'; -import { applicationApiService } from '@/api/services'; -import { CURRENT_GENERATION } from '@/constants'; + import * as Styled from './ApplyLinkButton.styled'; interface ApplyLinkProps { applyPath: string; + recruitSchedule: RecruitSchedule; } -const ApplyLinkButton = ({ applyPath }: ApplyLinkProps) => { +const ApplyLinkButton = ({ applyPath, recruitSchedule }: ApplyLinkProps) => { const session = useSession(); const router = useRouter(); const buttonRef = useRef(null) as MutableRefObject; const [isOpenSignInModal, setIsOpenSignInModal] = useState(false); - const [recruitSchedule, setRecruitSchedule] = useState(null); - const recruitingProgressStatus = getRecruitingProgressStatusFromRecruitingPeriod({ date: new Date(), recruitSchedule, }); - useEffect(() => { - const fetchRecruitSchedule = async () => { - const { data: recruitScheduleResponse } = await applicationApiService.getRecruitSchedule({ - generationNumber: CURRENT_GENERATION, - }); - - setRecruitSchedule(generateRecruitSchedule(recruitScheduleResponse)); - }; - - fetchRecruitSchedule(); - }, []); - const isRecruitingInProgress = recruitingProgressStatus === 'IN-RECRUITING'; const handleLinkButtonClick = () => { diff --git a/src/components/recruit/BottomNavigation/BottomNavigation.component.tsx b/src/components/recruit/BottomNavigation/BottomNavigation.component.tsx index c816470b..e2905dc1 100644 --- a/src/components/recruit/BottomNavigation/BottomNavigation.component.tsx +++ b/src/components/recruit/BottomNavigation/BottomNavigation.component.tsx @@ -1,12 +1,14 @@ import { LinkTo, RecruitDate } from '@/components'; import { Platforms } from '@/constants'; +import { RecruitSchedule } from '@/types/dto'; import * as Styled from './BottomNavigation.styled'; interface BottomNavigationProps { platforms: Platforms; + recruitSchedule: RecruitSchedule; } -const BottomNavigation = ({ platforms }: BottomNavigationProps) => { +const BottomNavigation = ({ platforms, recruitSchedule }: BottomNavigationProps) => { return (