diff --git a/frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.tsx b/frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.tsx index 9737129b0..647490e01 100644 --- a/frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.tsx +++ b/frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/AwardEditor/AwardEditor.tsx @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react'; import deleteButton from '@/assets/images/icons/delete_button_icon.svg'; import selectIcon from '@/assets/images/icons/selectArrow.svg'; import { CustomDropDown } from '@/components/common/CustomDropDown/CustomDropDown'; -import { Award } from '@/types/club'; +import { Award, SemesterTerm, SemesterTermType } from '@/types/club'; import * as Styled from './AwardEditor.styles'; interface AwardEditorProps { @@ -12,12 +12,15 @@ interface AwardEditorProps { const START_YEAR = 2020; -const parseSemester = (semester: string): number => { - const match = semester.match(/(\d{4})\s+(\d)학기/); - if (!match) return 0; - const year = parseInt(match[1], 10); - const semesterNumber = parseInt(match[2], 10); - return year * 10 + semesterNumber; +const getSemesterSortValue = (award: Award): number => { + const semesterValue = award.semester === SemesterTerm.FIRST ? 1 : 2; + return award.year * 10 + semesterValue; +}; + +const formatSemesterLabel = (award: Award): string => { + const semesterLabel = + award.semester === SemesterTerm.FIRST ? '1학기' : '2학기'; + return `${award.year} ${semesterLabel}`; }; const generateYearOptions = (currentYear: number) => { @@ -32,44 +35,51 @@ const generateYearOptions = (currentYear: number) => { }; const SEMESTER_OPTIONS = [ - { value: '1', label: '1학기' }, - { value: '2', label: '2학기' }, + { value: SemesterTerm.FIRST, label: '1학기' }, + { value: SemesterTerm.SECOND, label: '2학기' }, ] as const; const AwardEditor = ({ awards, onChange }: AwardEditorProps) => { const currentYear = new Date().getFullYear(); const [selectedYear, setSelectedYear] = useState(currentYear.toString()); - const [selectedSemester, setSelectedSemester] = useState('1'); + const [selectedSemester, setSelectedSemester] = useState( + SemesterTerm.FIRST, + ); const [isYearDropdownOpen, setIsYearDropdownOpen] = useState(false); const [isSemesterDropdownOpen, setIsSemesterDropdownOpen] = useState(false); - const [lastAddedSemester, setLastAddedSemester] = useState( - null, - ); + const [lastAddedKey, setLastAddedKey] = useState(null); const inputRefs = useRef>({}); const yearOptions = generateYearOptions(currentYear); const sortedAwards = [...awards].sort( (awardA, awardB) => - parseSemester(awardB.semester) - parseSemester(awardA.semester), + getSemesterSortValue(awardB) - getSemesterSortValue(awardA), ); + const getAwardKey = (award: Award, index: number): string => + `${award.year}-${award.semester}-${index}`; + const handleAddSemester = () => { - const semesterText = `${selectedYear} ${selectedSemester}학기`; + const year = parseInt(selectedYear, 10); - const isDuplicate = awards.some((award) => award.semester === semesterText); + const isDuplicate = awards.some( + (award) => award.year === year && award.semester === selectedSemester, + ); if (isDuplicate) { alert('이미 추가된 학기입니다.'); return; } const newAward: Award = { - semester: semesterText, + year, + semester: selectedSemester, achievements: [''], }; - onChange([...awards, newAward]); - setLastAddedSemester(semesterText); + const newAwards = [...awards, newAward]; + onChange(newAwards); + setLastAddedKey(getAwardKey(newAward, newAwards.length - 1)); }; const handleRemoveSemester = (semesterIndex: number) => { @@ -83,7 +93,7 @@ const AwardEditor = ({ awards, onChange }: AwardEditorProps) => { : award, ); onChange(updatedAwards); - setLastAddedSemester(awards[semesterIndex].semester); + setLastAddedKey(getAwardKey(awards[semesterIndex], semesterIndex)); }; const handleRemoveAchievement = ( @@ -122,21 +132,22 @@ const AwardEditor = ({ awards, onChange }: AwardEditorProps) => { }; useEffect(() => { - if (lastAddedSemester) { - const award = awards.find( - (award) => award.semester === lastAddedSemester, + if (lastAddedKey) { + const awardIndex = awards.findIndex( + (award, index) => getAwardKey(award, index) === lastAddedKey, ); + const award = awardIndex !== -1 ? awards[awardIndex] : null; if (award) { const lastIndex = award.achievements.length - 1; - const key = `${lastAddedSemester}-${lastIndex}`; + const key = `${lastAddedKey}-${lastIndex}`; const inputRef = inputRefs.current[key]; if (inputRef) { inputRef.focus(); } } - setLastAddedSemester(null); + setLastAddedKey(null); } - }, [awards, lastAddedSemester]); + }, [awards, lastAddedKey]); return ( @@ -174,7 +185,7 @@ const AwardEditor = ({ awards, onChange }: AwardEditorProps) => { setSelectedSemester(value as SemesterTermType)} open={isSemesterDropdownOpen} onToggle={(isOpen) => setIsSemesterDropdownOpen(!isOpen)} style={{ width: '100px' }} @@ -206,14 +217,20 @@ const AwardEditor = ({ awards, onChange }: AwardEditorProps) => { - {sortedAwards.map((award) => { + {sortedAwards.map((award, sortedIndex) => { const originalIndex = awards.findIndex( - (originalAward) => originalAward.semester === award.semester, + (originalAward, idx) => + originalAward.year === award.year && + originalAward.semester === award.semester && + originalAward.achievements === award.achievements, ); + const awardKey = getAwardKey(award, originalIndex); return ( - + - {award.semester} + + {formatSemesterLabel(award)} + handleRemoveSemester(originalIndex)} > @@ -226,7 +243,7 @@ const AwardEditor = ({ awards, onChange }: AwardEditorProps) => { { - const key = `${award.semester}-${achievementIndex}`; + const key = `${awardKey}-${achievementIndex}`; inputRefs.current[key] = element; }} placeholder='수상 내역을 입력하세요' diff --git a/frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx b/frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx index 93385fe02..0da782fa2 100644 --- a/frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx +++ b/frontend/src/pages/ClubDetailPage/components/ClubIntroContent/ClubIntroContent.tsx @@ -1,29 +1,24 @@ import { useState } from 'react'; import { USER_EVENT } from '@/constants/eventName'; import useMixpanelTrack from '@/hooks/Mixpanel/useMixpanelTrack'; +import { Award, FAQ, IdealCandidate, SemesterTerm } from '@/types/club'; import * as Styled from './ClubIntroContent.styles'; -export interface Award { - semester: string; - achievements: string[]; -} - -export interface IdealCandidate { - tags?: string[]; // TODO: tags가 추가될수도 있음 - content: string; -} +const formatSemesterLabel = (award: Award): string => { + const semesterLabel = + award.semester === SemesterTerm.FIRST ? '1학기' : '2학기'; + return `${award.year} ${semesterLabel}`; +}; -export interface Faq { - question: string; - answer: string; -} +const getAwardKey = (award: Award, index: number): string => + `${award.year}-${award.semester}-${index}`; interface ClubIntroContentProps { activityDescription?: string; awards?: Award[]; idealCandidate?: IdealCandidate; benefits?: string; - faqs?: Faq[]; + faqs?: FAQ[]; } const ClubIntroContent = ({ @@ -35,11 +30,11 @@ const ClubIntroContent = ({ }: ClubIntroContentProps) => { const trackEvent = useMixpanelTrack(); - const [openFaqIndices, setOpenFaqIndices] = useState([]); + const [openFaqIndexes, setOpenFaqIndexes] = useState([]); const handleToggleFaq = (index: number) => { - const isOpening = !openFaqIndices.includes(index); - setOpenFaqIndices((prev) => + const isOpening = !openFaqIndexes.includes(index); + setOpenFaqIndexes((prev) => prev.includes(index) ? prev.filter((i) => i !== index) : [...prev, index], ); @@ -66,18 +61,23 @@ const ClubIntroContent = ({ 동아리 성과 - {awards.map((award) => ( - - {award.semester} - - {award.achievements.map((item, idx) => ( - - {item} - - ))} - - - ))} + {awards.map((award, index) => { + const awardKey = getAwardKey(award, index); + return ( + + + {formatSemesterLabel(award)} + + + {award.achievements.map((item, idx) => ( + + {item} + + ))} + + + ); + })} )} @@ -102,7 +102,7 @@ const ClubIntroContent = ({ FAQ {faqs.map((faq, index) => { - const isOpen = openFaqIndices.includes(index); + const isOpen = openFaqIndexes.includes(index); return ( handleToggleFaq(index)}> diff --git a/frontend/src/types/club.ts b/frontend/src/types/club.ts index d48f82cff..08d171de9 100644 --- a/frontend/src/types/club.ts +++ b/frontend/src/types/club.ts @@ -41,8 +41,16 @@ export interface ClubDescription { recruitmentTarget: string; } +export const SemesterTerm = { + FIRST: 'FIRST', + SECOND: 'SECOND', +} as const; + +export type SemesterTermType = (typeof SemesterTerm)[keyof typeof SemesterTerm]; + export interface Award { - semester: string; + year: number; + semester: SemesterTermType; achievements: string[]; }