diff --git a/frontend/src/components/dashboard/ApplicantCard/ApplicantCard.stories.tsx b/frontend/src/components/dashboard/ApplicantCard/ApplicantCard.stories.tsx index 380ccee99..c0edc3ced 100644 --- a/frontend/src/components/dashboard/ApplicantCard/ApplicantCard.stories.tsx +++ b/frontend/src/components/dashboard/ApplicantCard/ApplicantCard.stories.tsx @@ -95,7 +95,10 @@ export const ApplicantCardDefault: Story = { averageScore: 3.86, isRejected: false, popOverMenuItems, + isSelectMode: false, + isSelected: false, onCardClick: () => console.log('지원자 카드가 클릭되었습니다.'), + onSelectApplicant: () => console.log('지원자 선택 체크박스가 클릭되었습니다.'), }, }; @@ -107,6 +110,9 @@ export const RejectedApplicantCard: Story = { averageScore: 2.23, isRejected: true, popOverMenuItems, + isSelectMode: false, + isSelected: false, onCardClick: () => console.log('지원자 카드가 클릭되었습니다.'), + onSelectApplicant: () => console.log('지원자 선택 체크박스가 클릭되었습니다.'), }, }; diff --git a/frontend/src/components/dashboard/ApplicantCard/index.tsx b/frontend/src/components/dashboard/ApplicantCard/index.tsx index a94632e64..d4def528b 100644 --- a/frontend/src/components/dashboard/ApplicantCard/index.tsx +++ b/frontend/src/components/dashboard/ApplicantCard/index.tsx @@ -11,6 +11,7 @@ import { useDropdown } from '@contexts/DropdownContext'; import type { DropdownItemType } from '@components/_common/molecules/DropdownItemRenderer'; import DropdownItemRenderer from '@components/_common/molecules/DropdownItemRenderer'; +import CheckBox from '@components/_common/atoms/CheckBox'; import S from './style'; interface ApplicantCardProps { @@ -20,7 +21,10 @@ interface ApplicantCardProps { evaluationCount: number; averageScore: number; popOverMenuItems: DropdownItemType[]; + isSelectMode: boolean; + isSelected: boolean; onCardClick: () => void; + onSelectApplicant: (isChecked: boolean) => void; } export default function ApplicantCard({ @@ -30,12 +34,15 @@ export default function ApplicantCard({ evaluationCount, averageScore, popOverMenuItems, + isSelectMode, + isSelected, onCardClick, + onSelectApplicant, }: ApplicantCardProps) { const { isOpen, open, close } = useDropdown(); const optionButtonWrapperRef = useRef(null); - const evaluationString = averageScore ? `★ ${averageScore.toFixed(1)}` : '평가 대기 중'; + const evaluationString = evaluationCount > 0 ? averageScore.toFixed(1) : '―'; const handleClickPopOverButton = (event: React.MouseEvent) => { event.stopPropagation(); @@ -58,6 +65,10 @@ export default function ApplicantCard({ const cardClickHandler = (e: React.MouseEvent) => { e.stopPropagation(); + if (isSelectMode) { + onSelectApplicant(!isSelected); + return; + } onCardClick(); }; @@ -78,13 +89,16 @@ export default function ApplicantCard({ > {name} - - {evaluationString} - + 0} + > + ★ + 0}> + {evaluationString} + + {formatDate(createdAt)} @@ -99,23 +113,33 @@ export default function ApplicantCard({
- - - - - {}} /> - + )} + {!isSelectMode && ( + <> + + + + + + + + )}
diff --git a/frontend/src/components/dashboard/ApplicantCard/style.ts b/frontend/src/components/dashboard/ApplicantCard/style.ts index 2ee4ea5c0..7967d00b7 100644 --- a/frontend/src/components/dashboard/ApplicantCard/style.ts +++ b/frontend/src/components/dashboard/ApplicantCard/style.ts @@ -38,15 +38,25 @@ const CardHeader = styled.h3` margin-bottom: 0; `; -const CardEvaluationFlag = styled.div<{ averageScore: number; evaluationCount: number }>` - width: fit-content; +const CardEvaluationFlag = styled.div<{ averageScore: number; isScoreExists: boolean }>` + width: 4rem; + display: flex; + flex-direction: row; + justify-content: space-between; border-radius: 0.4rem; padding: 0.3rem 0.4rem; margin-left: -0.1rem; ${({ theme }) => theme.colors.text.block}; - ${({ theme, averageScore }) => { + ${({ theme, averageScore, isScoreExists }) => { + if (!isScoreExists) { + return css` + background: ${theme.baseColors.grayscale[300]}; + color: ${theme.colors.text.block}; + `; + } + if (averageScore >= 1 && averageScore < 2) { return css` color: ${theme.baseColors.redscale[500]}; @@ -77,15 +87,18 @@ const CardEvaluationFlag = styled.div<{ averageScore: number; evaluationCount: n return css` background: ${theme.baseColors.grayscale[300]}; - color: ${theme.colors.text.default}; `; }} `; +const CardEvaluationFlagScore = styled.span<{ isScoreExists: boolean }>` + padding-right: ${({ isScoreExists }) => (isScoreExists ? '0' : '0.2rem')}; +`; + const CardInfoContainer = styled.div` display: flex; flex-direction: row; - gap: 2.4rem; + gap: 1.6rem; ${({ theme }) => theme.typography.common.small}; color: ${({ theme }) => theme.baseColors.grayscale[800]}; @@ -108,6 +121,7 @@ const S = { CardDetail, CardHeader, CardEvaluationFlag, + CardEvaluationFlagScore, CardInfoContainer, CardInfo, OptionButtonWrapper, diff --git a/frontend/src/components/dashboard/ProcessColumn/ProcessColumn.stories.tsx b/frontend/src/components/dashboard/ProcessColumn/ProcessColumn.stories.tsx index a3ba899be..99dccc31d 100644 --- a/frontend/src/components/dashboard/ProcessColumn/ProcessColumn.stories.tsx +++ b/frontend/src/components/dashboard/ProcessColumn/ProcessColumn.stories.tsx @@ -2,6 +2,8 @@ import type { Meta, StoryObj } from '@storybook/react'; import { reactRouterParameters, withRouter } from 'storybook-addon-remix-react-router'; import { SpecificApplicantIdProvider } from '@contexts/SpecificApplicnatIdContext'; import { SpecificProcessIdProvider } from '@contexts/SpecificProcessIdContext'; +import { FloatingEmailFormProvider } from '@contexts/FloatingEmailFormContext'; +import { MultiApplicantContextProvider } from '@contexts/MultiApplicantContext'; import ProcessColumn from './index'; const meta: Meta = { @@ -36,7 +38,11 @@ const meta: Meta = { (Child) => ( - + + + + + ), diff --git a/frontend/src/components/dashboard/ProcessColumn/index.tsx b/frontend/src/components/dashboard/ProcessColumn/index.tsx index a319e97b1..fcd208dc1 100644 --- a/frontend/src/components/dashboard/ProcessColumn/index.tsx +++ b/frontend/src/components/dashboard/ProcessColumn/index.tsx @@ -1,17 +1,19 @@ import { useParams } from 'react-router-dom'; -import { useSpecificApplicantId } from '@contexts/SpecificApplicnatIdContext'; -import { useSpecificProcessId } from '@contexts/SpecificProcessIdContext'; import { Process } from '@customTypes/process'; +import { DropdownItemType } from '@components/_common/molecules/DropdownItemRenderer'; + import useProcess from '@hooks/useProcess'; import useApplicant from '@hooks/useApplicant'; -import { useModal } from '@contexts/ModalContext'; +import specificApplicant from '@hooks/useSpecificApplicant'; +import { useSpecificApplicantId } from '@contexts/SpecificApplicnatIdContext'; +import { useSpecificProcessId } from '@contexts/SpecificProcessIdContext'; +import { useModal } from '@contexts/ModalContext'; import { DropdownProvider } from '@contexts/DropdownContext'; - -import { DropdownItemType } from '@components/_common/molecules/DropdownItemRenderer'; -import specificApplicant from '@hooks/useSpecificApplicant'; import { useFloatingEmailForm } from '@contexts/FloatingEmailFormContext'; +import { useMultiApplicant } from '@contexts/MultiApplicantContext'; + import ApplicantCard from '../ApplicantCard'; import ProcessDescription from './ProcessDescription'; import S from './style'; @@ -32,6 +34,7 @@ export default function ProcessColumn({ process, showRejectedApplicant, isPassed const { setProcessId } = useSpecificProcessId(); const { open } = useModal(); const { open: sideEmailFormOpen } = useFloatingEmailForm(); + const { isMultiType, applicants, addApplicant, removeApplicant } = useMultiApplicant(); const menuItemsList = ({ applicantId }: { applicantId: number }) => { const menuItems: DropdownItemType[] = [ @@ -73,6 +76,14 @@ export default function ProcessColumn({ process, showRejectedApplicant, isPassed return menuItems; }; + const applicantSelectHandler = (id: number, isChecked: boolean) => { + if (isChecked) { + addApplicant(id); + } else { + removeApplicant(id); + } + }; + const cardClickHandler = (id: number) => { setApplicantId(id); setProcessId(process.processId); @@ -97,7 +108,10 @@ export default function ProcessColumn({ process, showRejectedApplicant, isPassed evaluationCount={evaluationCount} averageScore={averageScore} popOverMenuItems={menuItemsList({ applicantId })} + isSelectMode={isMultiType} + isSelected={applicants.includes(applicantId)} onCardClick={() => cardClickHandler(applicantId)} + onSelectApplicant={(isChecked: boolean) => applicantSelectHandler(applicantId, isChecked)} /> ))}