-
Notifications
You must be signed in to change notification settings - Fork 2
[release] FE 릴리즈 #1001
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
base: main
Are you sure you want to change the base?
[release] FE 릴리즈 #1001
Conversation
[fix] 동아리 상세페이지 모집 버튼 푸터 삭제 복구
[feat] 모집예정 상태를 추가한다
[feature] 자동 스크롤 훅 추가
…e-MOA-467 [fix] 상시모집 버튼 상태가 유지되지 않는 버그를 수정한다
…OA-474 [feature] sns링크 버튼 클릭이벤트 추가
…own-MOA-462 [feature] 사용자가 모집 시작 일정을 확인할 수 있도록 지원하기 버튼을 개선한다
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | 변경 요약 |
|---|---|
앱 라우트 스크롤 frontend/src/App.tsx, frontend/src/hooks/ScrollToTop.tsx, frontend/src/hooks/useScrollDetection.ts, frontend/src/components/common/Header/Header.tsx |
ScrollToTop 컴포넌트 추가 및 App에 렌더링. useScroll 훅을 useScrollDetection으로 리네이밍 및 Header에서 교체. |
모집 상태 타입·유틸 frontend/src/types/club.ts, frontend/src/utils/getDeadLineText.ts, frontend/src/utils/getDeadLineText.test.ts |
RecruitmentStatus 타입 추가('OPEN' |
상태 표시 연동 frontend/src/components/ClubStateBox/ClubStateBox.tsx, frontend/src/styles/clubTags.ts |
UPCOMING('모집예정')에 대한 STATE_TEXT 및 STATUS_COLORS 매핑 추가. |
공통 컴포넌트 스타일 수정 frontend/src/components/common/Button/Button.tsx, frontend/src/components/common/InputField/InputField.styles.ts, frontend/src/styles/Global.styles.ts |
Button 크기/타이포 변경(높이·폰트), Input 필드 border-radius 및 플레이스홀더 색상/폰트 적용, 폼 요소 전반에 font-family 적용. |
ClubDetail 하단·신청 버튼 frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx, frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/*, frontend/src/pages/ClubDetailPage/components/ClubApplyButton/* |
ClubDetailFooter에 recruitmentStatus prop 추가 및 전달. ClubApplyButton에 recruitmentStatus 기반 비활성화 처리(UPCOMING/CLOSED), 핸들러/상태명 리네이밍 및 스타일/반응형 개선. |
프로필 카드·공유 버튼 frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx, frontend/src/pages/ClubDetailPage/components/ShareButton/* |
ClubProfileCard에 Mixpanel SNS 클릭 트래킹 추가. ShareButton에 디바이스 감지로 모바일 전용 아이콘 적용 및 아이콘 크기 조정. |
Admin 모집 편집(캘린더 포함) frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx, frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts, frontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.ts |
상시 모집 판별을 문자열 비교에서 isFarFuture(end) 날짜 기반으로 변경. 스타일을 테마 컬러·media로 통합 및 border-radius/크기 조정. |
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
- [feature] 사용자가 모집 시작 일정을 확인할 수 있도록 지원하기 버튼을 개선한다 #986: getDeadlineText 시그니처·ClubDetailFooter/ClubApplyButton 연관 변경을 함께 포함하고 있음.
- [HotFix] 모집예정 상태를 추가한다 #981: ClubStateBox 및 clubTags에서 UPCOMING/모집예정 상태 매핑 추가와 중복됨.
- [fix] 상시모집 버튼 상태가 유지되지 않는 버그를 수정한다 #990: RecruitEditTab의 상시 모집(isFarFuture) 전환 및 스타일/캘린더 변경과 겹침.
Suggested reviewers
- oesnuj
- suhyun113
Pre-merge checks and finishing touches
❌ Failed checks (1 inconclusive)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Title check | ❓ Inconclusive | PR 제목이 너무 일반적이고 모호합니다. '[release] FE 릴리즈'는 실제 변경 사항을 구체적으로 설명하지 않으며, 본질적으로 무엇이 변경되었는지 알 수 없습니다. | 제목을 더 구체적으로 변경하세요. 예: '[release] FE: 모집 상태 관리 개선 및 UI 스타일 리팩토링' 또는 주요 변경 사항을 명시하는 형태로 수정 권장합니다. |
✅ Passed checks (2 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
✨ Finishing touches
- 📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (4)
frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx (1)
13-20: recruitmentStatus prop의 타입을 RecruitmentStatus로 변경하세요.
RecruitmentStatus타입이club.ts에 정의되어 있으므로, Props 인터페이스에서도 동일한 타입을 사용하면 타입 안전성을 확보할 수 있습니다. 코딩 가이드라인에 따라 유사한 데이터에 대해 일관된 타입을 사용해야 합니다.🔎 제안된 수정
+import { RecruitmentStatus, SNSPlatform } from '@/types/club'; -import { SNSPlatform } from '@/types/club'; interface Props { name: string; logo?: string; cover?: string; - recruitmentStatus: string; + recruitmentStatus: RecruitmentStatus; socialLinks: Record<SNSPlatform, string>; introDescription: string; }frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts (1)
8-9: 매직 넘버를 명명된 상수로 추출하는 것을 고려하세요.패딩 값들(10px, 24px, 16px)이 하드코딩되어 있습니다. 이러한 값들을 디자인 토큰이나 명명된 상수로 추출하면 유지보수성과 일관성이 향상됩니다.
🔎 제안하는 리팩터링
+const FOOTER_SPACING = { + VERTICAL: '10px', + BOTTOM_DESKTOP: '24px', + BOTTOM_MOBILE: '16px', +} as const; + export const ClubDetailFooterContainer = styled.div` position: sticky; bottom: 0; width: 100%; z-index: 1040; // TODO: Portal로 모달 분리 후 header보다 낮게 재조정 - padding: 10px 0px 24px 0px; + padding: ${FOOTER_SPACING.VERTICAL} 0px ${FOOTER_SPACING.BOTTOM_DESKTOP} 0px; display: flex; align-items: center; justify-content: space-between; gap: 16px; background-color: white; border-top: 1px solid #cdcdcd; ${media.mobile} { - padding: 10px 0px 16px 0px; + padding: ${FOOTER_SPACING.VERTICAL} 0px ${FOOTER_SPACING.BOTTOM_MOBILE} 0px; } `;Based on learnings, 매직 넘버는 명명된 상수로 교체하여 명확성을 높여야 합니다.
Also applies to: 19-21
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts (1)
14-45: 매직 넘버를 명명된 상수로 추출하는 것을 고려하세요.버튼 크기 관련 값들(60px, 273px, 44px, 16px 등)이 하드코딩되어 있습니다. 이러한 값들을 명명된 상수로 추출하면 의미가 명확해지고 유지보수가 용이해집니다.
🔎 제안하는 리팩터링
+const BUTTON_SIZES = { + DESKTOP: { + WIDTH: '517px', + HEIGHT: '60px', + FONT_SIZE: '20px', + FONT_WEIGHT: 700, + }, + MOBILE: { + WIDTH: '273px', + HEIGHT: '44px', + FONT_SIZE: '16px', + FONT_WEIGHT: 500, + }, +} as const; + export const ApplyButton = styled.button` display: flex; align-items: center; justify-content: center; border: none; border-radius: 10px; cursor: ${({disabled}) => (disabled ? 'default' : 'pointer')}; transition: transform 0.2s ease-in-out; background-color: ${({ disabled }) => disabled ? colors.gray[500] : colors.primary[800]}; padding: 10px 40px; - width: 517px; - height: 60px; - font-size: 20px; + width: ${BUTTON_SIZES.DESKTOP.WIDTH}; + height: ${BUTTON_SIZES.DESKTOP.HEIGHT}; + font-size: ${BUTTON_SIZES.DESKTOP.FONT_SIZE}; font-style: normal; - font-weight: 700; + font-weight: ${BUTTON_SIZES.DESKTOP.FONT_WEIGHT}; color: ${colors.gray[200]}; text-align: center; img { font-size: 12px; font-weight: 600; } ${media.mobile} { - width: 273px; - height: 44px; - font-size: 16px; - font-weight: 500; + width: ${BUTTON_SIZES.MOBILE.WIDTH}; + height: ${BUTTON_SIZES.MOBILE.HEIGHT}; + font-size: ${BUTTON_SIZES.MOBILE.FONT_SIZE}; + font-weight: ${BUTTON_SIZES.MOBILE.FONT_WEIGHT}; background-color: ${({ disabled }) => disabled ? colors.gray[500] : colors.gray[900]}; } `;Based on learnings, 매직 넘버는 명명된 상수로 교체하여 명확성을 높여야 합니다.
frontend/src/utils/getDeadLineText.ts (1)
13-13:recruitmentStatus파라미터에 타입 안전성을 강화하세요.
recruitmentStatus가string으로 타입이 지정되어 있지만,RecruitmentStatus타입이 이미 정의되어 있습니다. 타입 안전성을 위해 구체적인 타입을 사용하는 것이 좋습니다.🔎 제안하는 수정
파일 상단에 타입을 import하세요:
import { format, differenceInCalendarDays } from 'date-fns'; import { ko } from 'date-fns/locale'; +import { RecruitmentStatus } from '@/types/club';그런 다음 파라미터 타입을 업데이트하세요:
const getDeadlineText = ( recruitmentStart: Date | null, recruitmentEnd: Date | null, - recruitmentStatus: string, + recruitmentStatus: RecruitmentStatus, today: Date = new Date(), ): string => {
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (2)
frontend/src/assets/images/icons/share_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/share_icon_mobile.svgis excluded by!**/*.svg
📒 Files selected for processing (23)
frontend/src/App.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/components/common/Button/Button.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/components/common/InputField/InputField.styles.tsfrontend/src/hooks/ScrollToTop.tsxfrontend/src/hooks/useScrollDetection.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/styles/Global.styles.tsfrontend/src/styles/clubTags.tsfrontend/src/types/club.tsfrontend/src/utils/getDeadLineText.test.tsfrontend/src/utils/getDeadLineText.ts
🧰 Additional context used
📓 Path-based instructions (3)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/types/club.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/hooks/ScrollToTop.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/App.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/styles/Global.styles.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/hooks/useScrollDetection.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.tsfrontend/src/utils/getDeadLineText.test.tsfrontend/src/utils/getDeadLineText.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/styles/clubTags.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.tsfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/components/common/Button/Button.tsxfrontend/src/components/common/InputField/InputField.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/types/club.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/hooks/ScrollToTop.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/App.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/styles/Global.styles.tsfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/hooks/useScrollDetection.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.tsfrontend/src/utils/getDeadLineText.test.tsfrontend/src/utils/getDeadLineText.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/styles/clubTags.tsfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.tsfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.tsfrontend/src/components/common/Button/Button.tsxfrontend/src/components/common/InputField/InputField.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts
frontend/**/*.{tsx,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{tsx,jsx}: Abstract complex logic/interactions into dedicated components/HOCs
Separate significantly different conditional UI/logic into distinct components
Colocate simple, localized logic or use inline definitions to reduce context switching
Choose field-level or form-level cohesion based on form requirements when using form libraries like react-hook-form
Use Component Composition instead of Props Drilling to reduce coupling
Files:
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/hooks/ScrollToTop.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/App.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsxfrontend/src/components/common/Button/Button.tsx
🧠 Learnings (11)
📚 Learning: 2025-09-21T02:23:27.796Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 744
File: frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx:47-48
Timestamp: 2025-09-21T02:23:27.796Z
Learning: ClubApplyButton 컴포넌트에서 ShareButton은 항상 렌더링되어야 하므로 정적 import를 사용하는 것이 적절함. 동적 import는 불필요함.
Applied to files:
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts
📚 Learning: 2025-03-19T05:18:07.818Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 195
File: frontend/src/pages/AdminPage/AdminPage.tsx:7-7
Timestamp: 2025-03-19T05:18:07.818Z
Learning: AdminPage.tsx에서 현재 하드코딩된 클럽 ID('67d2e3b9b15c136c6acbf20b')는 로그인 기능 구현 후 동적으로 가져오는 방식으로 수정될 예정입니다.
Applied to files:
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsxfrontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsxfrontend/src/pages/ClubDetailPage/ClubDetailPage.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Applied to files:
frontend/src/hooks/ScrollToTop.tsxfrontend/src/App.tsxfrontend/src/components/common/Header/Header.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
📚 Learning: 2025-07-19T05:05:10.196Z
Learnt from: seongwon030
Repo: Moadong/moadong PR: 548
File: frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx:17-57
Timestamp: 2025-07-19T05:05:10.196Z
Learning: ClubDetailPage.tsx에서 notJoinedClubNames 배열의 하드코딩은 의도적인 설계 결정입니다. 개발자가 명시적으로 하드코딩을 선택했으므로 이에 대한 리팩토링 제안을 하지 않아야 합니다.
Applied to files:
frontend/src/components/ClubStateBox/ClubStateBox.tsxfrontend/src/styles/clubTags.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Abstract complex logic/interactions into dedicated components/HOCs
Applied to files:
frontend/src/App.tsxfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Colocate simple, localized logic or use inline definitions to reduce context switching
Applied to files:
frontend/src/App.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Use Component Composition instead of Props Drilling to reduce coupling
Applied to files:
frontend/src/App.tsx
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Replace magic numbers with named constants for clarity
Applied to files:
frontend/src/styles/Global.styles.tsfrontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.tsfrontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsxfrontend/src/components/common/InputField/InputField.styles.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Define constants near related logic or ensure names link them clearly to avoid silent failures
Applied to files:
frontend/src/styles/Global.styles.tsfrontend/src/utils/getDeadLineText.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{tsx,jsx} : Separate significantly different conditional UI/logic into distinct components
Applied to files:
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
📚 Learning: 2025-09-30T05:26:41.788Z
Learnt from: alsdddk
Repo: Moadong/moadong PR: 765
File: backend/src/main/java/moadong/club/service/ClubApplyService.java:431-435
Timestamp: 2025-09-30T05:26:41.788Z
Learning: In the Moadong codebase's club application feature (backend/src/main/java/moadong/club/), multiple ClubApplicationForm entities can have ACTIVE status for the same clubId, semesterYear, and semesterTerm simultaneously. There is no uniqueness constraint requiring only one ACTIVE form per semester.
Applied to files:
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx
🧬 Code graph analysis (11)
frontend/src/App.tsx (1)
frontend/src/hooks/ScrollToTop.tsx (1)
ScrollToTop(4-12)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.ts (1)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (2)
frontend/src/types/club.ts (1)
RecruitmentStatus(3-7)frontend/src/utils/recruitmentDateParser.ts (1)
recruitmentDateParser(3-18)
frontend/src/styles/clubTags.ts (1)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/components/common/Header/Header.tsx (1)
frontend/src/hooks/useScrollDetection.ts (1)
useScrollDetection(3-16)
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (2)
frontend/src/types/application.ts (1)
ApplicationForm(65-68)frontend/src/constants/eventName.ts (1)
USER_EVENT(1-38)
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (1)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)
frontend/src/components/common/InputField/InputField.styles.ts (1)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.styles.ts (1)
frontend/src/styles/mediaQuery.ts (1)
media(8-14)
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.styles.ts (2)
frontend/src/styles/theme/colors.ts (1)
colors(1-81)frontend/src/styles/mediaQuery.ts (1)
media(8-14)
🔇 Additional comments (21)
frontend/src/components/common/InputField/InputField.styles.ts (1)
1-1: 테마 기반 스타일링 적용 및 모바일 UX 개선 확인테마 색상 사용으로 일관성이 향상되었고, placeholder의
font-size: 16px는 iOS Safari에서 포커스 시 자동 확대를 방지하는 좋은 선택입니다.Also applies to: 39-39, 70-71
frontend/src/styles/Global.styles.ts (1)
9-11: 폼 요소 전반에 일관된 폰트 적용 확인
button,input,select까지 확장하여 모든 폼 컨트롤에 일관된 폰트가 적용되도록 개선되었습니다.frontend/src/hooks/ScrollToTop.tsx (1)
1-12: 브라우저 뒤로/앞으로 이동 시 스크롤 복원 동작을 확인하세요모든 경로 변경 시 최상단으로 스크롤하는 구현은 정상적으로 작동하지만, 브라우저의 뒤로/앞으로 버튼 사용 시에도 스크롤 위치가 복원되지 않고 최상단으로 이동합니다.
일반적으로 사용자는 브라우저 네비게이션 시 이전 스크롤 위치가 복원되기를 기대합니다. 이것이 의도된 동작인지 확인하고, 필요하다면
useNavigationType을 활용하여POP액션(뒤로/앞으로)일 때는 스크롤하지 않도록 개선할 수 있습니다.브라우저 네비게이션 시 스크롤 복원을 위한 제안
import { useEffect } from 'react'; -import { useLocation } from 'react-router-dom'; +import { useLocation, useNavigationType } from 'react-router-dom'; export const ScrollToTop = () => { const { pathname } = useLocation(); + const navigationType = useNavigationType(); useEffect(() => { - window.scrollTo(0, 0); + // POP (뒤로/앞으로 버튼)이 아닐 때만 스크롤 + if (navigationType !== 'POP') { + window.scrollTo(0, 0); + } }, [pathname]); return null; };frontend/src/App.tsx (1)
7-7: ScrollToTop 컴포넌트 통합 확인
BrowserRouter내부에 적절히 배치되었으며,GlobalStyles이후에 렌더링하여 전역 라우트 변경 감지가 올바르게 동작합니다.Also applies to: 29-29
frontend/src/components/ClubStateBox/ClubStateBox.tsx (1)
8-8: 모집예정 상태 지원 추가 확인새로운
UPCOMING상태를 지원하여 모집 상태 시스템이 확장되었습니다.STATUS_COLORS의 '모집예정' 매핑과 일관되게 작동합니다.frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.tsx (1)
2-2: 디바이스별 아이콘 분기 처리 확인
useDevice훅을 활용하여 모바일과 데스크톱에 맞는 공유 아이콘을 선택적으로 렌더링하도록 개선되었습니다. 간결하고 명확한 구현입니다.Also applies to: 6-6, 18-18, 60-63
frontend/src/components/common/Button/Button.tsx (1)
21-21: 버튼 크기 및 폰트 사이즈 조정 확인높이와 폰트 크기가 조정되어 다른 컴포넌트들과 시각적 일관성이 개선되었습니다.
font-size: 16px는 모바일 포커스 시 자동 확대를 방지합니다.Also applies to: 25-25
frontend/src/pages/ClubDetailPage/components/ShareButton/ShareButton.styles.ts (1)
1-1: 반응형 아이콘 크기 조정 확인데스크톱에서 60x60, 모바일에서 44x44로 아이콘 크기가 디바이스별로 최적화되었습니다.
media헬퍼를 사용하여 프로젝트의 반응형 스타일 패턴과 일관성을 유지하고 있습니다.Also applies to: 11-17
frontend/src/components/common/Header/Header.tsx (1)
9-9: LGTM! 훅 이름 변경이 적절합니다.
useScroll→useScrollDetection리네이밍은 훅의 역할(스크롤 감지)을 더 명확하게 표현합니다. 관련 훅 파일의 변경과 일관성 있게 적용되었습니다.Also applies to: 18-18
frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.styles.ts (1)
1-1: LGTM! 테마 색상 적용이 잘 되었습니다.하드코딩된 색상값을
colors테마 토큰으로 교체하여 유지보수성과 일관성이 향상되었습니다.colors.primary[800],colors.gray[700]등 다른 컴포넌트들과 동일한 테마 시스템을 사용하고 있습니다.Also applies to: 26-28
frontend/src/pages/ClubDetailPage/ClubDetailPage.tsx (1)
83-87: LGTM! recruitmentStatus prop 추가가 적절합니다.
ClubDetailFooter에recruitmentStatusprop을 전달하여 상태 기반 마감 텍스트 렌더링이 가능해졌습니다. 이는 PR의 모집 상태 시스템 개선 목표와 일치합니다.frontend/src/styles/clubTags.ts (1)
17-22: 모집중과 모집예정이 동일한 색상을 사용합니다.
모집중과모집예정모두colors.accent[1][900](#3DBBFF)을 사용하고 있습니다. 이는 의도된 디자인일 수 있지만, 두 상태를 시각적으로 구분하지 못할 수 있습니다.디자인 의도가 맞는지 확인해 주세요. 만약 구분이 필요하다면
모집예정에 다른 색상(예:colors.accent[1][800])을 고려해 볼 수 있습니다.frontend/src/types/club.ts (2)
3-7: LGTM! 타입 안전성이 개선되었습니다.
RecruitmentStatus유니온 타입 도입으로 모집 상태 처리의 타입 안전성이 향상되었습니다.'OPEN' | 'CLOSED' | 'UPCOMING' | 'ALWAYS'리터럴 타입은 컴파일 타임에 유효하지 않은 상태값을 방지합니다.Also applies to: 15-15
78-83: 참고: ClubApiResponse의 recruitmentStatus는 string으로 유지됩니다.
ClubApiResponse.recruitmentStatus가string으로 남아있고Club.recruitmentStatus는RecruitmentStatus로 변경되었습니다. API 응답을 도메인 모델로 변환하는 과정에서 적절한 타입 매핑이 이루어지는지 확인해 주세요.frontend/src/pages/ClubDetailPage/components/ClubProfileCard/ClubProfileCard.tsx (1)
8-9: LGTM! Mixpanel 추적 통합이 잘 되었습니다.SNS 링크 클릭 이벤트 추적이 기존 패턴을 따르고 있으며,
platform과clubName컨텍스트를 포함하여 분석에 유용한 정보를 제공합니다.Also applies to: 30-30, 96-101
frontend/src/hooks/useScrollDetection.ts (1)
3-16: LGTM! 훅 리네이밍과 구현이 적절합니다.
useScrollDetection이라는 이름이 훅의 역할을 더 명확하게 표현합니다. 이벤트 리스너 정리(cleanup)가 올바르게 구현되어 있습니다.frontend/src/pages/AdminPage/tabs/RecruitEditTab/RecruitEditTab.tsx (1)
17-17: LGTM! 날짜 기반 상시모집 판단 로직이 적절합니다.
FAR_FUTURE_YEAR상수와isFarFuture헬퍼 함수를 사용한 상시모집 판단은 코딩 가이드라인(매직 넘버 상수화)을 따르며,handleUpdateClub의 저장 로직과 일관성이 있습니다.Also applies to: 39-40, 52-52
frontend/src/pages/ClubDetailPage/components/ClubDetailFooter/ClubDetailFooter.tsx (1)
5-22: LGTM!
recruitmentStatusprop이 깔끔하게 추가되었으며, 타입 안전성이 유지되고 있습니다.getDeadlineText호출이 날짜 기반 계산에서 상태 기반 로직으로 올바르게 업데이트되었습니다.frontend/src/utils/getDeadLineText.test.ts (1)
3-82: LGTM!테스트 케이스들이 새로운
recruitmentStatus파라미터 기반 로직을 포괄적으로 검증하고 있습니다. OPEN, CLOSED, UPCOMING, ALWAYS 상태에 대한 모든 시나리오가 잘 커버되어 있습니다.frontend/src/pages/AdminPage/tabs/RecruitEditTab/components/Calendar/Calendar.styles.ts (1)
3-222: LGTM!하드코딩된 컬러 값들을 테마 기반 토큰으로 성공적으로 마이그레이션했습니다. 이는 디자인 시스템의 일관성을 높이고 유지보수성을 향상시킵니다.
frontend/src/pages/ClubDetailPage/components/ClubApplyButton/ClubApplyButton.tsx (1)
23-24: LGTM!코드 리팩터링이 잘 되어 있습니다:
- 상태 및 함수 이름이 더 명확하고 설명적으로 변경되었습니다.
- 모집 상태 관련 불리언 변수들(Lines 82-85)이 명확하게 명명되어 조건 로직의 의도가 분명합니다.
renderButtonContent함수가 복잡한 삼항 연산자 대신 if/else를 사용하여 가독성이 향상되었습니다.- Disabled 상태가 적절하게 처리되고 있습니다.
As per coding guidelines, 복잡한 불리언 조건을 명명된 변수에 할당하고 if/else 구조를 사용하여 가독성을 높였습니다.
Also applies to: 28-52, 54-80, 82-103, 105-120
| const days = differenceInCalendarDays(recruitmentEnd, today); | ||
|
|
||
| if (days > 365) return '상시 모집'; | ||
| if (days > 365) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
매직 넘버 365를 명명된 상수로 추출하세요.
365일 임계값이 하드코딩되어 있습니다. 이 값을 명명된 상수로 추출하면 의도가 명확해지고 향후 조정이 용이해집니다.
🔎 제안하는 리팩터링
+const ALWAYS_RECRUITING_THRESHOLD_DAYS = 365;
+
const RECRUITMENT_STATUS = {
CLOSED: '모집 마감',
ALWAYS: '상시 모집',
UPCOMING: '모집 시작',
};
const getDeadlineText = (
recruitmentStart: Date | null,
recruitmentEnd: Date | null,
recruitmentStatus: string,
today: Date = new Date(),
): string => {
console.log(recruitmentStart, recruitmentEnd, recruitmentStatus, today);
if (recruitmentStatus === 'CLOSED') {
return RECRUITMENT_STATUS.CLOSED;
}
if (recruitmentStatus === 'UPCOMING') {
if (!recruitmentStart) return RECRUITMENT_STATUS.CLOSED;
const hour = recruitmentStart.getHours();
const minute = recruitmentStart.getMinutes();
let formatStr =
hour === 0 && minute === 0
? 'M월 d일'
: 'M월 d일 HH:mm';
return `${format(recruitmentStart, formatStr, { locale: ko })} ${RECRUITMENT_STATUS.UPCOMING}`;
}
if (!recruitmentEnd) return RECRUITMENT_STATUS.CLOSED;
const days = differenceInCalendarDays(recruitmentEnd, today);
- if (days > 365) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시
+ if (days > ALWAYS_RECRUITING_THRESHOLD_DAYS) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시
return days > 0 ? `D-${days}` : 'D-Day';
};Based on learnings, 매직 넘버는 명명된 상수로 교체하여 명확성을 높이고, 로직 근처에 상수를 정의하여 연결이 명확하도록 해야 합니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (days > 365) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 | |
| const ALWAYS_RECRUITING_THRESHOLD_DAYS = 365; | |
| const RECRUITMENT_STATUS = { | |
| CLOSED: '모집 마감', | |
| ALWAYS: '상시 모집', | |
| UPCOMING: '모집 시작', | |
| }; | |
| const getDeadlineText = ( | |
| recruitmentStart: Date | null, | |
| recruitmentEnd: Date | null, | |
| recruitmentStatus: string, | |
| today: Date = new Date(), | |
| ): string => { | |
| console.log(recruitmentStart, recruitmentEnd, recruitmentStatus, today); | |
| if (recruitmentStatus === 'CLOSED') { | |
| return RECRUITMENT_STATUS.CLOSED; | |
| } | |
| if (recruitmentStatus === 'UPCOMING') { | |
| if (!recruitmentStart) return RECRUITMENT_STATUS.CLOSED; | |
| const hour = recruitmentStart.getHours(); | |
| const minute = recruitmentStart.getMinutes(); | |
| let formatStr = | |
| hour === 0 && minute === 0 | |
| ? 'M월 d일' | |
| : 'M월 d일 HH:mm'; | |
| return `${format(recruitmentStart, formatStr, { locale: ko })} ${RECRUITMENT_STATUS.UPCOMING}`; | |
| } | |
| if (!recruitmentEnd) return RECRUITMENT_STATUS.CLOSED; | |
| const days = differenceInCalendarDays(recruitmentEnd, today); | |
| if (days > ALWAYS_RECRUITING_THRESHOLD_DAYS) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 | |
| return days > 0 ? `D-${days}` : 'D-Day'; | |
| }; |
🤖 Prompt for AI Agents
In frontend/src/utils/getDeadLineText.ts around line 37, the literal 365 is a
magic number; extract it into a clearly named constant (e.g., const
ALWAYS_RECRUITMENT_DAYS = 365) defined near the function/logic so its intent is
obvious, replace the hardcoded 365 in the if (days > 365) check with that
constant, and export the constant only if it must be reused elsewhere (otherwise
keep it module-local).
…down-MOA-462 [fix] 불필요한 로그 제거
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (1)
frontend/src/utils/getDeadLineText.ts (1)
35-35: 매직 넘버 365를 명명된 상수로 추출하세요.365일 임계값이 하드코딩되어 있습니다. 이 값을 명명된 상수로 추출하면 의도가 명확해지고 향후 조정이 용이해집니다.
🔎 제안하는 리팩터링
+const ALWAYS_RECRUITING_THRESHOLD_DAYS = 365; + const RECRUITMENT_STATUS = { CLOSED: '모집 마감', ALWAYS: '상시 모집', UPCOMING: '모집 시작', }; const getDeadlineText = ( recruitmentStart: Date | null, recruitmentEnd: Date | null, recruitmentStatus: string, today: Date = new Date(), ): string => { if (recruitmentStatus === 'CLOSED') { return RECRUITMENT_STATUS.CLOSED; } if (recruitmentStatus === 'UPCOMING') { if (!recruitmentStart) return RECRUITMENT_STATUS.CLOSED; const hour = recruitmentStart.getHours(); const minute = recruitmentStart.getMinutes(); let formatStr = hour === 0 && minute === 0 ? 'M월 d일' : 'M월 d일 HH:mm'; return `${format(recruitmentStart, formatStr, { locale: ko })} ${RECRUITMENT_STATUS.UPCOMING}`; } if (!recruitmentEnd) return RECRUITMENT_STATUS.CLOSED; const days = differenceInCalendarDays(recruitmentEnd, today); - if (days > 365) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 + if (days > ALWAYS_RECRUITING_THRESHOLD_DAYS) return RECRUITMENT_STATUS.ALWAYS; // D-day가 의미 없을 정도로 긴 경우 '상시 모집'으로 표시 return days > 0 ? `D-${days}` : 'D-Day'; };학습된 내용과 코딩 가이드라인에 따라, 매직 넘버는 명명된 상수로 교체하여 명확성을 높이고, 로직 근처에 상수를 정의하여 연결이 명확하도록 해야 합니다.
🧹 Nitpick comments (2)
frontend/src/utils/getDeadLineText.ts (2)
13-13: 타입 안정성을 위해RecruitmentStatus타입을 사용하세요.
recruitmentStatus매개변수가string으로 타입되어 있어 타입 안정성이 부족합니다. AI 요약에 따르면frontend/src/types/club.ts에RecruitmentStatus타입이 정의되어 있으므로, 이를 임포트하여 사용하면 컴파일 타임에 유효하지 않은 상태 값을 감지할 수 있습니다.🔎 제안하는 리팩터링
파일 상단에 타입 임포트를 추가하세요:
+import type { RecruitmentStatus } from '@/types/club'; import { format, differenceInCalendarDays } from 'date-fns'; import { ko } from 'date-fns/locale';그 다음 함수 시그니처를 업데이트하세요:
const getDeadlineText = ( recruitmentStart: Date | null, recruitmentEnd: Date | null, - recruitmentStatus: string, + recruitmentStatus: RecruitmentStatus, today: Date = new Date(), ): string => {코딩 가이드라인에 따라, 일관된 타입 사용으로 유사 함수/훅 간 타입 일관성을 유지해야 합니다.
25-28: 가독성을 위해 삼항 연산자를if/else로 변경하세요.포맷 문자열을 결정하는 복잡한 삼항 연산자를
if/else문으로 변경하면 가독성이 향상됩니다.🔎 제안하는 리팩터링
- let formatStr = - hour === 0 && minute === 0 - ? 'M월 d일' - : 'M월 d일 HH:mm'; + let formatStr: string; + if (hour === 0 && minute === 0) { + formatStr = 'M월 d일'; + } else { + formatStr = 'M월 d일 HH:mm'; + } return `${format(recruitmentStart, formatStr, { locale: ko })} ${RECRUITMENT_STATUS.UPCOMING}`;코딩 가이드라인에 따라, 복잡하거나 중첩된 삼항 연산자는 가독성을 위해
if/else로 교체해야 합니다.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Jira integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
frontend/src/utils/getDeadLineText.ts
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx,js,jsx}: Replace magic numbers with named constants for clarity
Replace complex/nested ternaries withif/elseor IIFEs for readability
Assign complex boolean conditions to named variables for explicit meaning
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle)
Use unique and descriptive names for custom wrappers/functions to avoid ambiguity
Define constants near related logic or ensure names link them clearly to avoid silent failures
Break down broad state management into smaller, focused hooks/contexts to reduce coupling
Files:
frontend/src/utils/getDeadLineText.ts
frontend/**/*.{ts,tsx}
📄 CodeRabbit inference engine (frontend/.cursorrules)
Use consistent return types for similar functions/hooks
Files:
frontend/src/utils/getDeadLineText.ts
🧠 Learnings (3)
📚 Learning: 2025-08-24T17:33:23.092Z
Learnt from: suhyun113
Repo: Moadong/moadong PR: 698
File: frontend/src/hooks/queries/applicants/useDeleteApplicants.ts:10-16
Timestamp: 2025-08-24T17:33:23.092Z
Learning: The codebase has inconsistent console logging patterns across files - mixing console.log/console.error for errors, using both Korean and English messages, and various formatting styles (template literals vs separate arguments, with/without emojis). A unified logging format like `console.error('[ModuleName] Operation failed:', error)` should be established for better debugging consistency.
Applied to files:
frontend/src/utils/getDeadLineText.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Define constants near related logic or ensure names link them clearly to avoid silent failures
Applied to files:
frontend/src/utils/getDeadLineText.ts
📚 Learning: 2025-11-25T14:08:23.253Z
Learnt from: CR
Repo: Moadong/moadong PR: 0
File: frontend/.cursorrules:0-0
Timestamp: 2025-11-25T14:08:23.253Z
Learning: Applies to frontend/**/*.{ts,tsx,js,jsx} : Replace magic numbers with named constants for clarity
Applied to files:
frontend/src/utils/getDeadLineText.ts
🔇 Additional comments (1)
frontend/src/utils/getDeadLineText.ts (1)
1-8: LGTM! 명확한 상수 정의와 적절한 임포트입니다.date-fns 라이브러리와 한국어 로케일을 사용한 임포트가 적절하며,
RECRUITMENT_STATUS상수 객체를 통해 한글 라벨을 명확하게 정의했습니다.
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
New Features
UI/Style Improvements
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.