Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chage #78

Merged
merged 2 commits into from
Aug 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
112 changes: 112 additions & 0 deletions src/app/(sidebar)/my-recruit/[id]/components/DueDateDropDown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { recruitScheduleStageList } from '@/app/(sidebar)/my-recruit/constant';
import { TouchButton } from '@/components/TouchButton';
import { Icon } from '@/system/components';
import { color } from '@/system/token/color';
import { Spacing } from '@/system/utils/Spacing';
import { cn } from '@/utils';
import { immer } from '@/utils/immer';
import clsx from 'clsx';
import { format } from 'date-fns/format';
import { useState } from 'react';
import { usePostRecruitSchedule } from '../../api/usePostRecruitSchedule';
import { AddIcon } from '../../containers/components/DueDateDialog/AddIcon';
import { Form } from '../../containers/components/DueDateDialog/Form';

interface DueDateDropDownProps {
id: number;
title?: string;
close: () => void;
}

const DEFAULT_FORM = {
recruitScheduleStage: recruitScheduleStageList[0],
deadLine: undefined,
};

export function DueDateDropDown({ id, title, close }: DueDateDropDownProps) {
const { mutate: postRecruitSchedule } = usePostRecruitSchedule();
const [scheduleList, setScheduleList] = useState<
Array<{
recruitScheduleStage: string;
deadLine?: Date;
}>
>([DEFAULT_FORM]);

const handleDelete = (index: number) => {
const newList = [...scheduleList];
newList.splice(index, 1);
setScheduleList(newList);
};

const save = () => {
scheduleList.forEach((schedule) => {
if (schedule.deadLine == null) {
return;
}
postRecruitSchedule({
id,
recruitScheduleStage: schedule.recruitScheduleStage,
deadLine: format(schedule.deadLine, 'yyyy-MM-dd'),
});
});
close();
};

return (
<div className="p-20" key={scheduleList.length}>
<div className="flex items-center w-314">
{title && (
<>
<Icon name="folderFill" size={20} color={color.neutral95} />
<Spacing size={4} direction="row" />
</>
)}
<span
className={cn('text-body1 font-semibold overflow-hidden text-ellipsis line-clamp-1', title ? 'flex-1' : '')}>
{title ? `${title}의 공고 일정 등록하기` : '공고 일정 등록하기'}
</span>
</div>
<Spacing size={4} />
<span className={clsx('text-caption1 text-neutral-35', title ? '' : 'block text-start')}>
일정을 등록하면 잊지 않도록 알려드릴게요!
</span>
<Spacing size={24} />
{/* 마감일 입력 */}
<div className="flex w-full flex-col gap-[8px]">
{scheduleList.map((schedule, index) => (
<Form
key={index}
currentRecruitStage={schedule.recruitScheduleStage}
selectedDate={schedule.deadLine != null ? new Date(schedule.deadLine) : schedule.deadLine}
hasDeleteButton={index !== 0}
hasArrow={title == null}
onStageClick={(stage) => {
const newSchedule = immer(scheduleList);
newSchedule[index].recruitScheduleStage = stage;
setScheduleList([...newSchedule]);
}}
onDeadLineClick={(date) => {
const newSchedule = immer(scheduleList);
newSchedule[index].deadLine = date;
setScheduleList([...newSchedule]);
}}
onDeleteClick={() => handleDelete(index)}
/>
))}
</div>
<Spacing size={16} />
<TouchButton
onClick={() => setScheduleList([...scheduleList, DEFAULT_FORM])}
className="hover:bg-neutral-3 w-full h-46 gap-[4px] rounded-[6px] py-[13px] flex items-center justify-center">
<AddIcon />
<span className="text-neutral-40 text-label2 font-medium">일정 추가</span>
</TouchButton>
<Spacing size={8} />
<TouchButton
onClick={save}
className="bg-neutral-95 w-full h-46 gap-[4px] rounded-[6px] text-white text-body2 font-semibold py-[13px]">
저장하기
</TouchButton>
</div>
);
}
133 changes: 70 additions & 63 deletions src/app/(sidebar)/my-recruit/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,25 @@ import { cn } from '@/utils';
import { DragEndEvent } from '@dnd-kit/core';
import { AnimatePresence, motion } from 'framer-motion';
import { useRouter } from 'next/navigation';
import { Suspense, useState } from 'react';
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/DueDateDialog';
import { DetailContent } from './components/DetailContent';
import DetailHeader from './components/DetailHeader';
import { DueDateDropDown } from './components/DueDateDropDown';

export default function CompanyDetail({ params: { id: recruitId } }: { params: { id: string } }) {
const router = useRouter();
const [sidebarOpened, setSidebarOpened] = useState(false);
const [dueDateDialogOpened, setDueDateDialogOpened] = useState(false);
const { mutate: deleteRecruit } = useDeleteRecruit();
const { mutate: mutatePostCardToRecruit } = usePostCardToRecruit();

const handleCloseDueDateDialog = () => {
setDueDateDialogOpened(!dueDateDialogOpened);
};

const handleDeleteRecruit = () => {
deleteRecruit(parseInt(recruitId, 10));
router.push('/my-recruit');
Expand All @@ -44,75 +49,77 @@ export default function CompanyDetail({ params: { id: recruitId } }: { params: {
<DndContextWithOverlay OverlayElement={InfoCard} onDragEnd={onDragEnd}>
<div className="flex overflow-hidden max-h-[100vh] h-[100vh] bg-white">
<div className="flex-1 mx-auto max-w-[1700px]">
<div className="flex justify-between w-full px-[80px] py-[24px] bg-white border-b-1 border-neutral-5">
<AsyncBoundaryWithQuery>
<AsyncBoundaryWithQuery>
<div className="flex justify-between w-full px-[80px] py-[24px] bg-white border-b-1 border-neutral-5">
<DetailHeader recruitId={recruitId} />
</AsyncBoundaryWithQuery>

<div className={cn('flex items-center gap-[16px]')}>
<Dropdown>
<Dropdown.Trigger asChild>
<TouchButton
layout
whileHover="hover"
aria-label="calendarFill button"
className="relative p-[12px] rounded-[6px] hover:bg-neutral-3 border border-neutral-10">
<Icon name="calendarFill" size={16} color={color.neutral95} />
<motion.div
className="absolute top-full left-1/2 translate-x-[-50%] mt-[4px] w-max px-[10px] py-[4px] rounded-[6px] bg-[#70737C] pointer-events-none"
initial={{ opacity: 0 }}
variants={{ hover: { opacity: 1 } }}>
<Text typography="body1" color="white">
공고 일정 등록
</Text>
</motion.div>
</TouchButton>
</Dropdown.Trigger>
<Dropdown.Content align="end" className="gap-[8px]">
<AsyncBoundaryWithQuery pendingFallback={<></>}>
<DueDateDialog id={Number(recruitId)} />
</AsyncBoundaryWithQuery>
</Dropdown.Content>
</Dropdown>
<div className={cn('flex items-center gap-[16px]')}>
<Dropdown>
<Dropdown.Trigger asChild onClick={() => handleCloseDueDateDialog()}>
<TouchButton
layout
whileHover="hover"
aria-label="calendarFill button"
className="relative p-[12px] rounded-[6px] hover:bg-neutral-3 border border-neutral-10">
<Icon name="calendarFill" size={16} color={color.neutral95} />
<motion.div
className="absolute top-full left-1/2 translate-x-[-50%] mt-[4px] w-max px-[10px] py-[4px] rounded-[6px] bg-[#70737C] pointer-events-none"
initial={{ opacity: 0 }}
variants={{ hover: { opacity: 1 } }}>
<Text typography="body1" color="white">
공고 일정 등록
</Text>
</motion.div>
</TouchButton>
</Dropdown.Trigger>
{dueDateDialogOpened && (
<Dropdown.Content align="end" className="gap-[8px]">
<AsyncBoundaryWithQuery pendingFallback={<></>}>
<DueDateDropDown close={handleCloseDueDateDialog} id={Number(recruitId)} />
</AsyncBoundaryWithQuery>
</Dropdown.Content>
)}
</Dropdown>

<TouchButton
layout
whileHover="hover"
onClick={() => setSidebarOpened(!sidebarOpened)}
className="relative p-[12px] rounded-[6px] hover:bg-neutral-3 border border-neutral-10 bg-white max-h-[42px]"
aria-label="copy button">
<Icon name="copy" size={16} color={color.neutral95} />
<motion.div
className="absolute top-full left-1/2 translate-x-[-50%] mt-[4px] w-max px-[10px] py-[4px] rounded-[6px] bg-[#70737C] pointer-events-none"
initial={{ opacity: 0 }}
variants={{ hover: { opacity: 1 } }}>
<Text typography="label1" color="white">
내 정보 가져오기
</Text>
</motion.div>
</TouchButton>
<TouchButton
layout
whileHover="hover"
onClick={() => setSidebarOpened(!sidebarOpened)}
className="relative p-[12px] rounded-[6px] hover:bg-neutral-3 border border-neutral-10 bg-white max-h-[42px]"
aria-label="copy button">
<Icon name="copy" size={16} color={color.neutral95} />
<motion.div
className="absolute top-full left-1/2 translate-x-[-50%] mt-[4px] w-max px-[10px] py-[4px] rounded-[6px] bg-[#70737C] pointer-events-none"
initial={{ opacity: 0 }}
variants={{ hover: { opacity: 1 } }}>
<Text typography="label1" color="white">
내 정보 가져오기
</Text>
</motion.div>
</TouchButton>

<Dropdown>
<Dropdown.Trigger asChild>
<div className="p-[12px] rounded-[6px]" aria-label="more button">
<Icon name="more" color={color.neutral95} />
</div>
</Dropdown.Trigger>
<Dropdown.Content align="end">
<Dropdown.CheckedItem onClick={handleDeleteRecruit}>
<div className="flex items-center gap-[8px]">
<Icon name="delete" color="#FF5C5C" />
<div className="text-red-50 text-[15px] font-normal">삭제하기</div>
<Dropdown>
<Dropdown.Trigger asChild>
<div className="p-[12px] rounded-[6px]" aria-label="more button">
<Icon name="more" color={color.neutral95} />
</div>
</Dropdown.CheckedItem>
</Dropdown.Content>
</Dropdown>
</Dropdown.Trigger>
<Dropdown.Content align="end">
<Dropdown.CheckedItem onClick={handleDeleteRecruit}>
<div className="flex items-center gap-[8px]">
<Icon name="delete" color="#FF5C5C" />
<div className="text-red-50 text-[15px] font-normal">삭제하기</div>
</div>
</Dropdown.CheckedItem>
</Dropdown.Content>
</Dropdown>
</div>
</div>
</div>
</AsyncBoundaryWithQuery>

<Suspense>
<AsyncBoundaryWithQuery>
<DetailContent recruitId={recruitId} />
</Suspense>
</AsyncBoundaryWithQuery>
</div>
<AnimatePresence>
{sidebarOpened ? <RightSidebar onCloseButtonClick={() => setSidebarOpened(false)} /> : null}
Expand Down
2 changes: 1 addition & 1 deletion src/types/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export interface TagType {
export const INFO_TYPES = ['경험_정리', '자기소개서', '면접_질문'] as const;
export type InfoType = (typeof INFO_TYPES)[number];

export const ANNOUNCEMENT_TYPES = ['서류_준비', '과제_준비', '인터뷰_준비', '내_정보_복사'] as const;
export const ANNOUNCEMENT_TYPES = ['서류_준비', '과제_준비', '면접_준비', '내_정보_복사'] as const;

export const TYPE_LIST = [...INFO_TYPES, ...ANNOUNCEMENT_TYPES] as const;
export type TypeTag = (typeof TYPE_LIST)[number];
Expand Down
Loading