diff --git a/src/app/(sidebar)/my-recruit/containers/AllRecruitment.tsx b/src/app/(sidebar)/my-recruit/containers/AllRecruitment.tsx index 7f456cfc..e6959e93 100644 --- a/src/app/(sidebar)/my-recruit/containers/AllRecruitment.tsx +++ b/src/app/(sidebar)/my-recruit/containers/AllRecruitment.tsx @@ -12,6 +12,8 @@ import { import { motion } from 'framer-motion'; import { color } from '@/system/token/color'; import { Dialog } from '@/system/components/Dialog/ShadcnDialog'; +import { cardList } from '../mock'; +import { RowCard } from './components/Card/RowCard'; export function AllRecruitment() { return ( @@ -43,6 +45,12 @@ export function AllRecruitment() { 등록된 공고가 없어요 + +
+ {cardList.map((cardInfo) => ( + + ))} +
); } diff --git a/src/app/(sidebar)/my-recruit/containers/ProgressingRecruitment.tsx b/src/app/(sidebar)/my-recruit/containers/ProgressingRecruitment.tsx index 9098eb5b..3d31fd3d 100644 --- a/src/app/(sidebar)/my-recruit/containers/ProgressingRecruitment.tsx +++ b/src/app/(sidebar)/my-recruit/containers/ProgressingRecruitment.tsx @@ -3,6 +3,9 @@ import { ShoeIcon } from './components/ShoeIcon'; import { Dialog } from '@/system/components/Dialog/ShadcnDialog'; import { motion } from 'framer-motion'; import { color } from '@/system/token/color'; +import { cardList } from '../mock'; +import { BoxCard } from './components/Card/BoxCard'; +import { TouchButton } from '@/components/TouchButton'; export function ProgressingRecruitment() { return ( @@ -21,6 +24,22 @@ export function ProgressingRecruitment() { 진행중인 공고가 없어요 + +
+ {cardList.map((cardInfo) => ( + + ))} +
+ + +
+ + 더보기 + + + 간략히 보기 + +
); } diff --git a/src/app/(sidebar)/my-recruit/containers/components/Card/BoxCard.tsx b/src/app/(sidebar)/my-recruit/containers/components/Card/BoxCard.tsx new file mode 100644 index 00000000..b3924ead --- /dev/null +++ b/src/app/(sidebar)/my-recruit/containers/components/Card/BoxCard.tsx @@ -0,0 +1,68 @@ +import { Spacing } from '@/components/Spacing'; +import { Icon } from '@/system/components'; +import { color } from '@/system/token/color'; +import { dday } from '@/utils/date'; +import { MoreButton } from '@/app/(sidebar)/my-recruit/containers/components/Card/common/MoreButton'; +import { StatusButton } from '@/app/(sidebar)/my-recruit/containers/components/Card/common/StatusButton'; +import { Dialog } from '@/system/components/Dialog/ShadcnDialog'; +import { DueDateDialog } from '../DueDateDialog'; + +export type ProgressingCardType = { + type: '서류 마감' | '1차 면접' | '2차 면접'; + status: '지원 완료' | '서류 통과' | '서류 탈락'; + dueDate: Date | null; + period: string; + title: string; +}; + +export function BoxCard({ type, title, status, dueDate, period }: ProgressingCardType) { + return ( +
+
+ {dueDate == null ? ( + + + 공고 일정을 등록해주세요 + + + + + + + ) : ( + <> +
+ + + {type} D-{dday(dueDate)} + +
+ + + )} +
+
+
+
{period}
+ +
+ + {title} +
+
+ ); +} + +const statusList = [ + { variant: 'text', text: '지원 준비' }, + { variant: 'text', text: '지원 완료' }, + { variant: 'border' }, + { variant: 'text', text: '서류 통과' }, + { variant: 'text', text: '서류 탈락' }, + { variant: 'border' }, + { variant: 'text', text: '면접 통과' }, + { variant: 'text', text: '면접 탈락' }, + { variant: 'border' }, + { variant: 'text', text: '최종 합격' }, + { variant: 'text', text: '최종 탈락' }, +] as const; diff --git a/src/app/(sidebar)/my-recruit/containers/components/Card/RowCard.tsx b/src/app/(sidebar)/my-recruit/containers/components/Card/RowCard.tsx new file mode 100644 index 00000000..1e9cc852 --- /dev/null +++ b/src/app/(sidebar)/my-recruit/containers/components/Card/RowCard.tsx @@ -0,0 +1,44 @@ +import { If } from '@/components/If'; +import { Spacing } from '@/components/Spacing'; +import { Icon } from '@/system/components'; +import { color } from '@/system/token/color'; +import { dday } from '@/utils/date'; +import { MoreButton } from '@/app/(sidebar)/my-recruit/containers/components/Card/common/MoreButton'; +import { StatusButton } from './common/StatusButton'; + +interface RowCardProps { + type: '서류 마감' | '1차 면접' | '2차 면접'; + status: '지원 완료' | '서류 통과' | '서류 탈락'; + dueDate: Date | null; + period: string; + title: string; +} + +export function RowCard({ type, title, status, dueDate, period }: RowCardProps) { + return ( +
+
+
+
+ {period} + + +
+ + + {type} D-{dday(dueDate!)} + +
+
+ + {title} +
+
+ + + +
+
+
+ ); +} diff --git a/src/app/(sidebar)/my-recruit/containers/components/Card/common/MoreButton.tsx b/src/app/(sidebar)/my-recruit/containers/components/Card/common/MoreButton.tsx new file mode 100644 index 00000000..c81693de --- /dev/null +++ b/src/app/(sidebar)/my-recruit/containers/components/Card/common/MoreButton.tsx @@ -0,0 +1,24 @@ +import { Icon } from '@/system/components'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from '@/system/components/DropdownMenu/DropdownMenu'; +import { color } from '@/system/token/color'; + +export function MoreButton() { + return ( + + + + + + + +
삭제하기
+
+
+
+ ); +} diff --git a/src/app/(sidebar)/my-recruit/containers/components/Card/common/StatusButton.tsx b/src/app/(sidebar)/my-recruit/containers/components/Card/common/StatusButton.tsx new file mode 100644 index 00000000..e643f4cd --- /dev/null +++ b/src/app/(sidebar)/my-recruit/containers/components/Card/common/StatusButton.tsx @@ -0,0 +1,58 @@ +import { SwitchCase } from '@/components/SwitchCase'; +import { Icon } from '@/system/components'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + DropdownMenuSeparator, +} from '@/system/components/DropdownMenu/DropdownMenu'; +import { color } from '@/system/token/color'; +import { cn } from '@/utils'; + +interface Props { + currentStatus: string; +} + +export function StatusButton({ currentStatus }: Props) { + return ( + + +
+ {currentStatus} + +
+
+ + {statusList.map((item) => ( + + {item.text} + + ) : null, + border: , + }} + /> + ))} + +
+ ); +} + +const statusList = [ + { variant: 'text', text: '지원 준비' }, + { variant: 'text', text: '지원 완료' }, + { variant: 'border' }, + { variant: 'text', text: '서류 통과' }, + { variant: 'text', text: '서류 탈락' }, + { variant: 'border' }, + { variant: 'text', text: '면접 통과' }, + { variant: 'text', text: '면접 탈락' }, + { variant: 'border' }, + { variant: 'text', text: '최종 합격' }, + { variant: 'text', text: '최종 탈락' }, +] as const; diff --git a/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog.tsx b/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog.tsx new file mode 100644 index 00000000..50b110a4 --- /dev/null +++ b/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog.tsx @@ -0,0 +1,64 @@ +import { Spacing } from '@/components/Spacing'; +import { Button, Icon } from '@/system/components'; +import { color } from '@/system/token/color'; +import clsx from 'clsx'; +import { motion } from 'framer-motion'; +import { Popover, PopoverContent, PopoverTrigger } from '@/system/components/Popover/Popover'; +import { Calendar } from '@/system/components/Calendar/Calendar'; +import { format } from 'date-fns/format'; +import { useState } from 'react'; + +interface DueDateDialogProps { + title: string; +} + +export function DueDateDialog({ title }: DueDateDialogProps) { + const [selectedDate, setSelectedDate] = useState(); + + const isDateSelected = selectedDate != null; + + return ( +
+
+ + + {title} + 의 공고 일정 등록하기 +
+ + 일정을 등록하면 잊지 않도록 알려드릴게요! + + {/* 마감일 입력 */} +
+ 서류마감 + + + + + + {isDateSelected ? format(selectedDate, 'yyyy.mm.dd') : '마감일을 선택해주세요'} + + + + + + + +
+ + +
+ ); +} diff --git a/src/app/(sidebar)/my-recruit/mock.ts b/src/app/(sidebar)/my-recruit/mock.ts new file mode 100644 index 00000000..e08cfed9 --- /dev/null +++ b/src/app/(sidebar)/my-recruit/mock.ts @@ -0,0 +1,18 @@ +import { ProgressingCardType } from './containers/components/Card/BoxCard'; + +export const cardList: ProgressingCardType[] = [ + { + type: '1차 면접', + status: '서류 통과', + dueDate: null, + period: '2024 상반기', + title: '디프만 15기 디자이너 직군', + }, + { + type: '2차 면접', + status: '지원 완료', + dueDate: new Date('2024-08-20'), + period: '2024 하반기', + title: '2024 네이버 프로덕트 디자이너 신입공채 지원서 제출', + }, +]; diff --git a/src/components/SwitchCase.tsx b/src/components/SwitchCase.tsx new file mode 100644 index 00000000..58aec27c --- /dev/null +++ b/src/components/SwitchCase.tsx @@ -0,0 +1,17 @@ +interface Props { + caseBy: Partial>; + value: Case; + defaultComponent?: JSX.Element | null; +} + +export function SwitchCase({ + value, + caseBy, + defaultComponent: defaultComponent = null, +}: Props) { + if (value == null) { + return defaultComponent; + } + + return caseBy[value] ?? defaultComponent; +} diff --git a/src/system/components/Icon/Icon.tsx b/src/system/components/Icon/Icon.tsx index bff5a2b5..69c4f423 100644 --- a/src/system/components/Icon/Icon.tsx +++ b/src/system/components/Icon/Icon.tsx @@ -23,6 +23,9 @@ import { Link } from './SVG/Link'; import { Unlink } from './SVG/Unlink'; import { Calendar } from './SVG/Calendar'; import { CalendarFill } from './SVG/CalendarFill'; +import { Clover } from './SVG/Clover'; +import { DownChevron } from './SVG/DownChevron'; +import { FolderFill } from './SVG/FolderFill'; const iconMap = { bell: Bell, @@ -30,11 +33,13 @@ const iconMap = { check: Check, division: Division, folder: Folder, + folderFill: FolderFill, logout: Logout, memo: Memo, profile: Profile, profileFill: ProfileFill, rightChevron: RightChevron, + downChevron: DownChevron, search: Search, setting: Setting, down: Down, @@ -49,6 +54,7 @@ const iconMap = { unlink: Unlink, calendar: Calendar, calendarFill: CalendarFill, + clover: Clover, } as const; export interface IconProps extends IconBaseType { diff --git a/src/system/components/Icon/SVG/Clover.tsx b/src/system/components/Icon/SVG/Clover.tsx new file mode 100644 index 00000000..788e93e2 --- /dev/null +++ b/src/system/components/Icon/SVG/Clover.tsx @@ -0,0 +1,14 @@ +import { IconBaseType } from '@/system/components/Icon/SVG/type'; + +export function Clover({ size, color }: IconBaseType) { + return ( + + + + ); +} diff --git a/src/system/components/Icon/SVG/DownChevron.tsx b/src/system/components/Icon/SVG/DownChevron.tsx new file mode 100644 index 00000000..47033995 --- /dev/null +++ b/src/system/components/Icon/SVG/DownChevron.tsx @@ -0,0 +1,9 @@ +import { IconBaseType } from '@/system/components/Icon/SVG/type'; + +export function DownChevron({ color, size }: IconBaseType) { + return ( + + + + ); +} diff --git a/src/system/components/Icon/SVG/FolderFill.tsx b/src/system/components/Icon/SVG/FolderFill.tsx new file mode 100644 index 00000000..3cb4d33a --- /dev/null +++ b/src/system/components/Icon/SVG/FolderFill.tsx @@ -0,0 +1,14 @@ +import { IconBaseType } from '@/system/components/Icon/SVG/type'; + +export function FolderFill({ size, color }: IconBaseType) { + return ( + + + + ); +} diff --git a/src/utils/date.ts b/src/utils/date.ts index 7b5785b3..130b1166 100644 --- a/src/utils/date.ts +++ b/src/utils/date.ts @@ -1,3 +1,5 @@ +import { differenceInDays } from 'date-fns/differenceInDays'; + interface Option { separator?: string; } @@ -11,3 +13,9 @@ export const formatToYYMMDD = (dateString: string, { separator = '' }: Option = return [yy, mm, dd].join(separator); }; + +export const dday = (target: Date) => { + const today = new Date(); + + return differenceInDays(target, today); +};