diff --git a/frontend/src/app/recruitment/announcement/page.tsx b/frontend/src/app/recruitment/announcement/page.tsx new file mode 100644 index 0000000..c402ac7 --- /dev/null +++ b/frontend/src/app/recruitment/announcement/page.tsx @@ -0,0 +1,87 @@ +'use client' + +import AnnouncementCard from '@/components/recruitment/announcement/AnnouncementCard' +import { AnnouncementDataType } from '@/utils/types' +import { useInfiniteQuery } from '@tanstack/react-query' +import { useEffect } from 'react' + +export default function Announcement() { + const accessToken = + typeof window !== 'undefined' ? sessionStorage.getItem('accessToken') : null + const persistToken = + typeof window !== 'undefined' ? localStorage.getItem('persistToken') : null + + const getData = async ({ pageParam = 1 }) => { + const params = new URLSearchParams() + params.set('page', String(pageParam)) + params.set('size', '12') + params.set('direction', 'ASC') + + const res = await fetch( + `${process.env.NEXT_PUBLIC_BASE_URL}/jobs?${params.toString()}`, + { + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${accessToken || persistToken}`, + }, + }, + ) + + if (!res.ok) { + const data = await res.json() + throw new Error(data.message) + } + + const data = await res.json() + return data.data + } + + const { data, fetchNextPage, hasNextPage, isFetchingNextPage, status } = + useInfiniteQuery(['announcement'], getData, { + getNextPageParam: (lastPage, pages) => + lastPage.length < 12 ? undefined : pages.length + 1, + }) + + useEffect(() => { + if (!isFetchingNextPage) { + const handleScroll = () => { + if ( + window.innerHeight + document.documentElement.scrollTop !== + document.documentElement.offsetHeight + ) + return + fetchNextPage() + } + window.addEventListener('scroll', handleScroll) + } + }, [fetchNextPage, isFetchingNextPage]) + + if (status === 'loading') { + return Loading... + } + + if (status === 'error') { + return Error fetching data + } + + return ( +
+
+ 신입 채용 공고 +
+ {data.pages.map((group, i) => ( +
+ {group.map((item: AnnouncementDataType) => ( +
+ +
+ ))} +
+ ))} + {hasNextPage && isFetchingNextPage && Loading more...} +
+ ) +} diff --git a/frontend/src/components/recruitment/announcement/AnnouncementCard.tsx b/frontend/src/components/recruitment/announcement/AnnouncementCard.tsx new file mode 100644 index 0000000..b9df98a --- /dev/null +++ b/frontend/src/components/recruitment/announcement/AnnouncementCard.tsx @@ -0,0 +1,48 @@ +import { AnnouncementDataType } from '@/utils/types' + +type PropsType = { + item: AnnouncementDataType +} + +export default function AnnouncementCard({ item }: PropsType) { + const extractMonthAndDay = (dateString: string): string => { + if (dateString === '9999-12-31T00:00:00') { + return '상시채용' + } + + const date = new Date(dateString) + + const month = date.getMonth() + 1 + const day = date.getDate() + + return `~ ${month.toString().padStart(2, '0')}/${day + .toString() + .padStart(2, '0')} 까지` + } + + return ( +
+
+ {item.companyName} +
+
+

+ {item.title} +

+ + 채용공고 바로가기 + +
+
+ + {extractMonthAndDay(item.expirationDate)} + +
+
+ ) +} diff --git a/frontend/src/utils/atoms.ts b/frontend/src/utils/atoms.ts index 5c4b849..51e34e2 100644 --- a/frontend/src/utils/atoms.ts +++ b/frontend/src/utils/atoms.ts @@ -117,7 +117,7 @@ const recruitfilterState = atom({ const directionState = atom({ key: 'directionState', - default: 'ASC', + default: 'DESC', }) export { diff --git a/frontend/src/utils/types.ts b/frontend/src/utils/types.ts index 190de1d..2da687c 100644 --- a/frontend/src/utils/types.ts +++ b/frontend/src/utils/types.ts @@ -18,3 +18,11 @@ export type MultipleFilterType = { } export type DirectionType = 'ASC' | 'DESC' + +export type AnnouncementDataType = { + id: number + companyName: string + title: string + url: string + expirationDate: string +}