diff --git a/src/app/(sidebar)/(my-info)/containers/Onboarding/LogoLeaf.tsx b/src/app/(sidebar)/(my-info)/containers/Onboarding/LogoLeaf.tsx new file mode 100644 index 00000000..500d93d7 --- /dev/null +++ b/src/app/(sidebar)/(my-info)/containers/Onboarding/LogoLeaf.tsx @@ -0,0 +1,36 @@ +export function LogoLeaf() { + return ( + + + + + + + + ); +} diff --git a/src/app/(sidebar)/(my-info)/containers/Onboarding/OnboardingDialog.tsx b/src/app/(sidebar)/(my-info)/containers/Onboarding/OnboardingDialog.tsx index 1c065ec1..4202ee44 100644 --- a/src/app/(sidebar)/(my-info)/containers/Onboarding/OnboardingDialog.tsx +++ b/src/app/(sidebar)/(my-info)/containers/Onboarding/OnboardingDialog.tsx @@ -1,13 +1,15 @@ -import { Dialog, DialogContent, DialogTrigger } from '@/system/components/Dialog/Dialog'; +import { Dialog, DialogContent } from '@/system/components/Dialog/Dialog'; import { Logo } from './Logo'; import { Spacing } from '@/system/utils/Spacing'; import { AnimateSlide } from '@/system/utils/AnimateSlide/AnimateSlide'; import { TouchButton } from '@/components/TouchButton'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { If } from '@/system/utils/If'; import { cn } from '@/utils'; -import { motion } from 'framer-motion'; +import { motion, useAnimationControls } from 'framer-motion'; import { color } from '@/system/token/color'; +import { LogoLeaf } from './LogoLeaf'; +import { Icon } from '@/system/components'; const MAX_INDEX = 3; @@ -18,6 +20,7 @@ interface OnboardingDialogProps { export function OnboardingDialog({}: OnboardingDialogProps) { const [step, setStep] = useState<'text' | 'card' | 'finish'>('text'); const [currentIndex, setCurrentIndex] = useState(0); + const controls = useAnimationControls(); const onNextClick = () => { if (currentIndex < MAX_INDEX) { @@ -27,11 +30,29 @@ export function OnboardingDialog({}: OnboardingDialogProps) { setStep('card'); }; + useEffect(() => { + if (step === 'card') { + const interval = setInterval(() => controls.start('wiggle'), 100); + + const timeout = setTimeout(() => { + clearInterval(interval); + setStep('finish'); + }, 3000); + return () => clearTimeout(timeout); + } + }, [step]); + return ( diff --git a/src/app/(sidebar)/(my-info)/page.tsx b/src/app/(sidebar)/(my-info)/page.tsx index 800945dd..1eb90ea6 100644 --- a/src/app/(sidebar)/(my-info)/page.tsx +++ b/src/app/(sidebar)/(my-info)/page.tsx @@ -13,6 +13,7 @@ import { If } from '@/system/utils/If'; import { motion } from 'framer-motion'; import { InfoCardSkeleton } from './components/InfoCardSkeleton'; import { AsyncBoundaryWithQuery } from '@/lib'; +import { Onboarding } from './containers/Onboarding/Onboarding'; export default function MyInfo() { const [showHeader, setShowHeader] = useState(false); @@ -112,6 +113,9 @@ export default function MyInfo() { pendingFallback={}> + + + ); diff --git a/src/app/(sidebar)/my-recruit/[id]/page.tsx b/src/app/(sidebar)/my-recruit/[id]/page.tsx index 88b1207f..b6f8b4b8 100644 --- a/src/app/(sidebar)/my-recruit/[id]/page.tsx +++ b/src/app/(sidebar)/my-recruit/[id]/page.tsx @@ -13,7 +13,7 @@ import { useState } from 'react'; import { useDeleteRecruit } from '../api/useDeleteRecruit'; import { usePostCardToRecruit } from '../api/usePostCardToRecruit'; import { RightSidebar } from '../containers/RightSidebar/RightSidebar'; -import { DueDateDialog } from '../containers/components/DueDateDialog'; +import { DueDateDialog } from '../containers/components/DueDateDialog/DueDateDialog'; import { DetailContent } from './components/DetailContent'; import DetailHeader from './components/DetailHeader'; @@ -59,7 +59,7 @@ export default function CompanyDetail({ params: { id: recruitId } }: { params: { - {}} /> + {}} /> diff --git a/src/app/(sidebar)/my-recruit/api/useDeleteRecruitSchedule.ts b/src/app/(sidebar)/my-recruit/api/useDeleteRecruitSchedule.ts new file mode 100644 index 00000000..f9a257e9 --- /dev/null +++ b/src/app/(sidebar)/my-recruit/api/useDeleteRecruitSchedule.ts @@ -0,0 +1,25 @@ +import { http } from '@/apis/http'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { GET_RECRUIT_SCHEDULE } from '@/app/(sidebar)/my-recruit/api/useGetRecruitSchedule'; + +interface Request { + id: number; + recruitScheduleId: number; +} + +function deleteRecruitSchedule({ id, recruitScheduleId }: Request) { + return http.delete({ url: `/recruits/${id}/recruit-schedule/${recruitScheduleId}` }); +} + +export function useDeleteRecruitSchedule() { + const queryClient = useQueryClient(); + + const mutate = useMutation({ + mutationFn: (data: Request) => deleteRecruitSchedule(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [GET_RECRUIT_SCHEDULE] }); + }, + }); + + return mutate; +} diff --git a/src/app/(sidebar)/my-recruit/api/useGetRecruitSchedule.ts b/src/app/(sidebar)/my-recruit/api/useGetRecruitSchedule.ts new file mode 100644 index 00000000..a0074168 --- /dev/null +++ b/src/app/(sidebar)/my-recruit/api/useGetRecruitSchedule.ts @@ -0,0 +1,30 @@ +import { http } from '@/apis/http'; +import { useSuspenseQuery } from '@tanstack/react-query'; + +interface Request { + id: number; +} + +interface ScheduleType { + id: number; + recruitScheduleStage: string; + deadLine: string; +} + +export const GET_RECRUIT_SCHEDULE = 'get-recruit-schedule'; + +function getRecruitSchedule({ id }: Request) { + return http.get({ url: `/recruits/${id}/recruit-schedule` }); +} + +export function useGetRecruitSchedule({ id }: Request) { + const result = useSuspenseQuery({ + queryKey: [GET_RECRUIT_SCHEDULE, id], + queryFn: async () => { + const res = await getRecruitSchedule({ id }); + return res.data; + }, + }); + + return result; +} diff --git a/src/app/(sidebar)/my-recruit/api/usePostRecruitSchedule.ts b/src/app/(sidebar)/my-recruit/api/usePostRecruitSchedule.ts new file mode 100644 index 00000000..027325f3 --- /dev/null +++ b/src/app/(sidebar)/my-recruit/api/usePostRecruitSchedule.ts @@ -0,0 +1,26 @@ +import { http } from '@/apis/http'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { GET_RECRUIT_SCHEDULE } from '@/app/(sidebar)/my-recruit/api/useGetRecruitSchedule'; + +interface Request { + id: number; + recruitScheduleStage: string; + deadLine: string; +} + +function postRecruitSchedule({ id, ...data }: Request) { + return http.post({ url: `/recruits/${id}/recruit-schedule`, data }); +} + +export function usePostRecruitSchedule() { + const queryClient = useQueryClient(); + + const mutate = useMutation({ + mutationFn: (data: Request) => postRecruitSchedule(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [GET_RECRUIT_SCHEDULE] }); + }, + }); + + return mutate; +} diff --git a/src/app/(sidebar)/my-recruit/api/usePutRecruitScheduleDeadline.ts b/src/app/(sidebar)/my-recruit/api/usePutRecruitScheduleDeadline.ts new file mode 100644 index 00000000..578ea7de --- /dev/null +++ b/src/app/(sidebar)/my-recruit/api/usePutRecruitScheduleDeadline.ts @@ -0,0 +1,26 @@ +import { http } from '@/apis/http'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { GET_RECRUIT_SCHEDULE } from '@/app/(sidebar)/my-recruit/api/useGetRecruitSchedule'; + +interface Request { + id: number; + recruitScheduleId: number; + deadLine: string; +} + +export function putRecruitScheduleDeadline({ id, recruitScheduleId, deadLine }: Request) { + return http.put({ url: `/recruits/${id}/recruit-schedule/${recruitScheduleId}/deadLine`, data: { deadLine } }); +} + +export function usePutRecruitScheduleDeadline() { + const queryClient = useQueryClient(); + + const mutate = useMutation({ + mutationFn: (data: Request) => putRecruitScheduleDeadline(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [GET_RECRUIT_SCHEDULE] }); + }, + }); + + return mutate; +} diff --git a/src/app/(sidebar)/my-recruit/api/usePutRecruitScheduleStage.ts b/src/app/(sidebar)/my-recruit/api/usePutRecruitScheduleStage.ts new file mode 100644 index 00000000..398feb29 --- /dev/null +++ b/src/app/(sidebar)/my-recruit/api/usePutRecruitScheduleStage.ts @@ -0,0 +1,29 @@ +import { http } from '@/apis/http'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { GET_RECRUIT_SCHEDULE } from '@/app/(sidebar)/my-recruit/api/useGetRecruitSchedule'; + +interface Request { + id: number; + recruitScheduleId: number; + recruitScheduleStage: string; +} + +export function putRecruitScheduleStage({ id, recruitScheduleId, recruitScheduleStage }: Request) { + return http.put({ + url: `/recruits/${id}/recruit-schedule/${recruitScheduleId}/stage`, + data: { recruitScheduleStage }, + }); +} + +export function usePutRecruitScheduleStage() { + const queryClient = useQueryClient(); + + const mutate = useMutation({ + mutationFn: (data: Request) => putRecruitScheduleStage(data), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [GET_RECRUIT_SCHEDULE] }); + }, + }); + + return mutate; +} diff --git a/src/app/(sidebar)/my-recruit/components/DraggableInfoCard.tsx b/src/app/(sidebar)/my-recruit/components/DraggableInfoCard.tsx index 62dbcaef..b1973e4b 100644 --- a/src/app/(sidebar)/my-recruit/components/DraggableInfoCard.tsx +++ b/src/app/(sidebar)/my-recruit/components/DraggableInfoCard.tsx @@ -1,12 +1,20 @@ import { InfoCard } from '@/components/InfoCard'; +import { useDndContext } from '@/lib/dnd-kit/dnd-kit'; +import { If } from '@/system/utils/If'; import { ComponentProps } from 'react'; export function DraggableInfoCard(props: ComponentProps) { + const { over } = useDndContext(); + + const overTitle = over?.data.current?.title; + return (
-
- {props.title || '제목을 입력해주세요'} -
+ +
+ {`내 공고 '${overTitle}' 폴더로 복사 중`} +
+
); diff --git a/src/app/(sidebar)/my-recruit/constant.ts b/src/app/(sidebar)/my-recruit/constant.ts index 44751ea5..47f23a31 100644 --- a/src/app/(sidebar)/my-recruit/constant.ts +++ b/src/app/(sidebar)/my-recruit/constant.ts @@ -4,7 +4,7 @@ export const recruitStatusList = [ { variant: 'text', text: '지원 준비' }, { variant: 'text', text: '지원 완료' }, { variant: 'border' }, - { variant: 'text', text: '서류 마감' }, + { variant: 'text', text: '서류 통과' }, { variant: 'text', text: '서류 탈락' }, { variant: 'border' }, { variant: 'text', text: '면접 통과' }, diff --git a/src/app/(sidebar)/my-recruit/containers/AllRecruitment/AllRecruitList.tsx b/src/app/(sidebar)/my-recruit/containers/AllRecruitment/AllRecruitList.tsx index a7da8f9e..63b397e9 100644 --- a/src/app/(sidebar)/my-recruit/containers/AllRecruitment/AllRecruitList.tsx +++ b/src/app/(sidebar)/my-recruit/containers/AllRecruitment/AllRecruitList.tsx @@ -38,7 +38,10 @@ export function AllRecruitList({ selectedSeason }: Props) { present: (
{allRecruits.map((cardInfo) => ( - + { setCardsPerRow(Math.max(1, Math.floor(contentRect.width / (MIN_CARD_WIDTH + CARD_GAP)))); }); @@ -55,7 +54,7 @@ export function ProgressingRecruitList() {
{recruitCardForShow.map((cardInfo) => ( {}} onRecruitDelete={deleteRecruit} diff --git a/src/app/(sidebar)/my-recruit/containers/components/Card/BoxCard.tsx b/src/app/(sidebar)/my-recruit/containers/components/Card/BoxCard.tsx index a64b9a39..44902679 100644 --- a/src/app/(sidebar)/my-recruit/containers/components/Card/BoxCard.tsx +++ b/src/app/(sidebar)/my-recruit/containers/components/Card/BoxCard.tsx @@ -6,8 +6,9 @@ import { MoreButton } from '@/app/(sidebar)/my-recruit/containers/components/Car import { StatusButton } from '@/app/(sidebar)/my-recruit/containers/components/Card/common/StatusButton'; import { Dialog } from '@/system/components/Dialog/ShadcnDialog'; import Link from 'next/link'; -import { DueDateDialog } from '../DueDateDialog'; +import { DueDateDialog } from '../DueDateDialog/DueDateDialog'; import { RecruitCard } from '@/app/(sidebar)/my-recruit/type'; +import { useRouter } from 'next/navigation'; interface BoxCardProps extends RecruitCard { onRecruitDelete: (id: number) => void; @@ -27,46 +28,49 @@ export function BoxCard({ onRecruitStatusChange, onRecruitDelete, }: BoxCardProps) { + const router = useRouter(); const minWidth = MIN_CARD_WIDTH; return ( - -
-
- {nearestSchedule == null ? ( - - - 공고 일정을 등록해주세요 - - - - onDuedateAppend()} /> - - - ) : ( - <> -
- - - {nearestSchedule.recruitScheduleStage} D-{dday(nearestSchedule.deadLine)} - -
- onRecruitDelete(id)} /> - - )} -
-
-
-
{season}
+
+
+ {nearestSchedule == null ? ( + + + 공고 일정을 등록해주세요 + + + + onDuedateAppend()} /> + + + ) : ( + <> +
+ + + {nearestSchedule.recruitScheduleStage} D-{dday(nearestSchedule.deadLine)} + +
+ onRecruitDelete(id)} /> + + )} +
+
router.push(`/my-recruit/${id}`)}> +
+
{season}
+
e.stopPropagation()}> onRecruitStatusChange(id, status)} />
- - {title}
+ + {title}
- +
); } diff --git a/src/app/(sidebar)/my-recruit/containers/components/Card/RowCard.tsx b/src/app/(sidebar)/my-recruit/containers/components/Card/RowCard.tsx index d0d5aed7..9c27c98d 100644 --- a/src/app/(sidebar)/my-recruit/containers/components/Card/RowCard.tsx +++ b/src/app/(sidebar)/my-recruit/containers/components/Card/RowCard.tsx @@ -9,6 +9,7 @@ import { StatusButton } from './common/StatusButton'; import { cn } from '@/utils'; import { RecruitCard } from '@/app/(sidebar)/my-recruit/type'; import { MoreButton } from './common/MoreButton'; +import { useRouter } from 'next/navigation'; type RowCardProps = RecruitCard & { highlighted?: boolean; @@ -26,6 +27,7 @@ export function RowCard({ onRecruitDelete, onRecruitStatusChange, }: RowCardProps) { + const router = useRouter(); const isOutOfDate = nearestSchedule != null && dday(nearestSchedule.deadLine) < 0; const pointerEventsClassName = isOutOfDate ? 'pointer-events-none' : 'pointer-events-auto'; @@ -35,38 +37,38 @@ export function RowCard({ .otherwise(() => 'bg-neutral-95'); return ( - -
-
-
-
- {season} - - -
- - - {recruitStatus} D-{dday(nearestSchedule?.deadLine!)} - -
-
- - {title} -
-
- onRecruitStatusChange(id, status)} - /> - - onRecruitDelete(id)} /> -
+
router.push(`/my-recruit/${id}`)}> +
+
+
+ {season} + + +
+ + + {recruitStatus} D-{dday(nearestSchedule?.deadLine!)} + +
+
+ + {title} +
+
e.stopPropagation()}> + onRecruitStatusChange(id, status)} + /> + + onRecruitDelete(id)} />
- +
); } diff --git a/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog.tsx b/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog.tsx deleted file mode 100644 index 46fb9394..00000000 --- a/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { recruitStatusList } from '@/app/(sidebar)/my-recruit/constant'; -import { Button, Dropdown, Icon } from '@/system/components'; -import { Calendar } from '@/system/components/Calendar/Calendar'; -import { Popover, PopoverContent, PopoverTrigger } from '@/system/components/Popover/Popover'; -import { color } from '@/system/token/color'; -import { Spacing } from '@/system/utils/Spacing'; -import { cn } from '@/utils'; -import clsx from 'clsx'; -import { format } from 'date-fns/format'; -import { motion } from 'framer-motion'; -import { useState } from 'react'; -import { recruitScheduleStageList } from '../../constant'; - -interface DueDateDialogProps { - title?: string; - onDuedateAppend: () => void; -} - -export function DueDateDialog({ title }: DueDateDialogProps) { - const [selectedDate, setSelectedDate] = useState(); - const [currentRecruitStage, setCurrentRecruitStage] = useState(recruitStatusList[3].text); - const [dueDateList, setDueDateList] = useState< - Array<{ - recruitScheduleStage: string | null; - deadLine: `${number}-${number}-${number}` | null; - }> - >([]); - - const isDateSelected = selectedDate != null; - const activatedAddButton = - dueDateList.length !== 0 && dueDateList[0].deadLine != null && dueDateList[0].recruitScheduleStage != null; - - return ( -
-
- {title && ( - <> - - - - )} - - {title ? `${title}의 공고 일정 등록하기` : '공고 일정 등록하기'} - -
- - - 일정을 등록하면 잊지 않도록 알려드릴게요! - - - {/* 마감일 입력 */} -
- - -
- {currentRecruitStage} - {title && } -
-
- - {recruitScheduleStageList.map((item, index) => ( - setCurrentRecruitStage(item)}> - {item} - - ))} - -
- - - - - - {isDateSelected ? format(selectedDate, 'yyyy.mm.dd') : '마감일을 선택해주세요'} - - - - - - - -
- - -
- ); -} diff --git a/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog/DueDateDialog.tsx b/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog/DueDateDialog.tsx new file mode 100644 index 00000000..3913d356 --- /dev/null +++ b/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog/DueDateDialog.tsx @@ -0,0 +1,100 @@ +import { Button, Icon } from '@/system/components'; +import { color } from '@/system/token/color'; +import { Spacing } from '@/system/utils/Spacing'; +import { cn } from '@/utils'; +import clsx from 'clsx'; +import { useEffect, useState } from 'react'; +import { useGetRecruitSchedule } from '../../../api/useGetRecruitSchedule'; +import { Form } from '@/app/(sidebar)/my-recruit/containers/components/DueDateDialog/Form'; +import { If } from '@/system/utils/If'; +import { useQueryClient } from '@tanstack/react-query'; +import { GET_ALL_RECRUITS_KEY } from '../../../api/useGetAllRecruits'; +import { GET_PROGRESSING_RECRUITS_KEY } from '../../../api/useGetProgressingRecruits'; +import { useDeleteRecruitSchedule } from '../../../api/useDeleteRecruitSchedule'; + +interface DueDateDialogProps { + id: number; + title?: string; + onDuedateAppend: () => void; +} + +export function DueDateDialog({ id, title }: DueDateDialogProps) { + const [additonalScheduleForm, setAdditionalScheduleForm] = useState(false); + const scheduleList = useGetRecruitSchedule({ id }).data; + + const { mutate: deleteRecruitSchedule } = useDeleteRecruitSchedule(); + + const activatedAddButton = + additonalScheduleForm === false && + scheduleList.length !== 0 && + scheduleList[0].deadLine != null && + scheduleList[0].recruitScheduleStage != null; + + const queryClient = useQueryClient(); + useEffect(() => { + return () => { + queryClient.invalidateQueries({ queryKey: [GET_PROGRESSING_RECRUITS_KEY] }); + queryClient.invalidateQueries({ queryKey: [GET_ALL_RECRUITS_KEY] }); + }; + }, []); + + return ( +
+
+ {title && ( + <> + + + + )} + + {title ? `${title}의 공고 일정 등록하기` : '공고 일정 등록하기'} + +
+ + + 일정을 등록하면 잊지 않도록 알려드릴게요! + + + {/* 마감일 입력 */} +
+ {scheduleList.length === 0 ? ( +
+ ) : ( + scheduleList.map((schedule, index) => ( + deleteRecruitSchedule({ id, recruitScheduleId: schedule.id })} + /> + )) + )} + + setAdditionalScheduleForm(false)} + /> + +
+ + +
+ ); +} diff --git a/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog/Form.tsx b/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog/Form.tsx new file mode 100644 index 00000000..4d210bd7 --- /dev/null +++ b/src/app/(sidebar)/my-recruit/containers/components/DueDateDialog/Form.tsx @@ -0,0 +1,129 @@ +import { Dropdown, Icon } from '@/system/components'; +import { Calendar } from '@/system/components/Calendar/Calendar'; +import { Popover, PopoverContent, PopoverTrigger } from '@/system/components/Popover/Popover'; +import { color } from '@/system/token/color'; +import clsx from 'clsx'; +import { format } from 'date-fns/format'; +import { motion } from 'framer-motion'; +import { recruitScheduleStageList } from '@/app/(sidebar)/my-recruit/constant'; +import { useState } from 'react'; +import { usePostRecruitSchedule } from '../../../api/usePostRecruitSchedule'; +import { usePutRecruitScheduleDeadline } from '../../../api/usePutRecruitScheduleDeadline'; +import { usePutRecruitScheduleStage } from '../../../api/usePutRecruitScheduleStage'; +import { If } from '@/system/utils/If'; + +interface Props { + id?: number; + recruitId: number; + deadLine?: string; + recruitScheduleStage?: string; + hasDeleteButton?: boolean; + hasArrow: boolean; + onDeleteClick?: () => void; +} + +// 매번 서버요청 안하도록 리팩토링 +export function Form({ + id, + recruitId, + hasArrow, + deadLine, + recruitScheduleStage, + hasDeleteButton, + onDeleteClick, +}: Props) { + const [selectedDate, setSelectedDate] = useState(deadLine != null ? new Date(deadLine) : undefined); + const [currentRecruitStage, setCurrentRecruitStage] = useState( + recruitScheduleStage ?? recruitScheduleStageList[0], + ); + + const isDateSelected = selectedDate != null; + const { mutate: postRecruitSchedule } = usePostRecruitSchedule(); + const { mutate: putRecruitScheduleDeadline } = usePutRecruitScheduleDeadline(); + const { mutate: putRecruitScheduleState } = usePutRecruitScheduleStage(); + + const handleStageClick = (item: string) => { + if (id != null) { + putRecruitScheduleState({ id: recruitId, recruitScheduleId: id, recruitScheduleStage: item }); + return; + } + setCurrentRecruitStage(item); + if (selectedDate == null) { + return; + } + postRecruitSchedule({ + id: recruitId, + deadLine: format(selectedDate, 'yyyy-MM-dd'), + recruitScheduleStage: currentRecruitStage, + }); + }; + + const handleDeadlineClick = (date?: Date) => { + if (date == null) { + return; + } + if (id != null) { + putRecruitScheduleDeadline({ id: recruitId, recruitScheduleId: id, deadLine: format(date, 'yyyy-MM-dd') }); + return; + } + setSelectedDate(date); + postRecruitSchedule({ + id: recruitId, + deadLine: format(date, 'yyyy-MM-dd'), + recruitScheduleStage: currentRecruitStage, + }); + }; + + return ( +
+ + +
+ {currentRecruitStage} + {hasArrow && } +
+
+ + {recruitScheduleStageList.map((item, index) => ( + handleStageClick(item)}> + {item} + + ))} + +
+
+ + + + + + {isDateSelected ? format(selectedDate, 'yyyy.MM.dd') : '마감일을 선택해주세요'} + + + + + + + + + + +
+
+ ); +} diff --git a/src/container/Sidebar/Sidebar.tsx b/src/container/Sidebar/Sidebar.tsx index f7b87500..e237431f 100644 --- a/src/container/Sidebar/Sidebar.tsx +++ b/src/container/Sidebar/Sidebar.tsx @@ -67,12 +67,12 @@ export function Sidebar() { diff --git a/src/hooks/useScroll.ts b/src/hooks/useScroll.ts index 97f04be2..a5a93ae1 100644 --- a/src/hooks/useScroll.ts +++ b/src/hooks/useScroll.ts @@ -3,7 +3,6 @@ import { RefObject, useEffect } from 'react'; export function useScroll(target: RefObject, callback: (topOffsetY: number) => void) { useEffect(() => { const element = target.current ?? window; - console.log(element); const callback2 = () => { const topOffsetY = target.current?.scrollTop ?? window.scrollY; diff --git a/src/lib/dnd-kit/Droppable.tsx b/src/lib/dnd-kit/Droppable.tsx index c0d65697..d4fdd05e 100644 --- a/src/lib/dnd-kit/Droppable.tsx +++ b/src/lib/dnd-kit/Droppable.tsx @@ -6,11 +6,12 @@ import { motion, useAnimationControls } from 'framer-motion'; interface DroppableProps { id: string | number; children?: ReactNode; + dataForOverlay?: any; } -export function Droppable({ id, children }: DroppableProps) { +export function Droppable({ id, children, dataForOverlay }: DroppableProps) { const animationControl = useAnimationControls(); - const { setNodeRef } = useDroppable({ id }); + const { setNodeRef } = useDroppable({ id, data: dataForOverlay }); const { selectedId } = useDndAdditionalContext(); useEffect(() => { diff --git a/src/system/components/Dialog/Dialog.tsx b/src/system/components/Dialog/Dialog.tsx index 483a3e03..a30cc878 100644 --- a/src/system/components/Dialog/Dialog.tsx +++ b/src/system/components/Dialog/Dialog.tsx @@ -43,10 +43,10 @@ const DialogContent = React.forwardRef< )} {...props}> {children} - + {/* Close - + */} )); diff --git a/src/system/components/Icon/Icon.tsx b/src/system/components/Icon/Icon.tsx index 68835c71..86f904fc 100644 --- a/src/system/components/Icon/Icon.tsx +++ b/src/system/components/Icon/Icon.tsx @@ -44,6 +44,7 @@ import { Trash } from './SVG/Trash'; import { Unlink } from './SVG/Unlink'; import { Up } from './SVG/Up'; import { WorkFill } from './SVG/WorkFill'; +import { Downloads } from './SVG/Downloads'; import { X } from './SVG/X'; const iconMap = { @@ -89,6 +90,7 @@ const iconMap = { rocket: Rocket, logoOnly: LogoOnly, workFill: WorkFill, + download: Downloads, arrowUp: ArrowUp, logout: Logout, profileFill: ProfileFill, diff --git a/src/system/components/Icon/SVG/Close.tsx b/src/system/components/Icon/SVG/Close.tsx index 05f4524e..0cc8c4cd 100644 --- a/src/system/components/Icon/SVG/Close.tsx +++ b/src/system/components/Icon/SVG/Close.tsx @@ -2,9 +2,9 @@ import { IconBaseType } from '@/system/components/Icon/SVG/type'; export function Close({ size, color }: IconBaseType) { return ( - - - + + + ); } diff --git a/src/system/components/Icon/SVG/Downloads.tsx b/src/system/components/Icon/SVG/Downloads.tsx new file mode 100644 index 00000000..5e0e0a91 --- /dev/null +++ b/src/system/components/Icon/SVG/Downloads.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { IconBaseType } from './type'; + +export function Downloads({ size, color }: IconBaseType) { + return ( + + + + + ); +}