Conversation
- SearchField.styles.ts 도입으로 페이지 전용 스타일 의존 제거 - SearchField가 공통 스타일을 직접 참조하도록 변경 - 검색 입력 UI의 재사용성/일관성 향상
|
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
|
Warning
|
| Cohort / File(s) | Summary |
|---|---|
공통 SearchField 추가 + ApplicantsTab 연동frontend/src/components/common/SearchField/SearchField.tsx, frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx, frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts |
공통 SearchField 컴포넌트 추가(컨트롤드 입력, 포커스/블러, onSubmit 처리). ApplicantsTab에 keyword 상태 및 useMemo 기반 필터링 적용, SearchField 연결, 요약 카드 추가, 테이블 행 구조(체크박스·상태 배지·메모·생성일) 및 스타일 수정. |
SearchBox 리로케이트 및 Header 경로 갱신frontend/src/components/common/Header/Header.tsx, frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx, frontend/src/components/common/SearchBox/SearchBox.tsx |
기존 공통 SearchBox 파일 삭제. MainPage 전용 SearchBox 신규 추가(검색 컨텍스트·카테고리 리셋·라우팅·믹스패널 트래킹 포함). Header의 SearchBox import 경로를 MainPage용 컴포넌트로 변경. |
Sequence Diagram(s)
sequenceDiagram
participant U as 사용자
participant SF as SearchField
participant AT as ApplicantsTab
U->>SF: 입력 변경
SF-->>AT: onChange(value)
AT->>AT: keyword 업데이트 (useState)
AT->>AT: filteredApplicants = useMemo(필터)
U->>SF: 제출(버튼/엔터)
SF-->>AT: onSubmit()
AT->>AT: 필터링 적용(클라이언트 사이드)
sequenceDiagram
participant U as 사용자
participant SB as MainPage SearchBox
participant SC as SearchContext
participant CC as CategoryContext
participant R as Router
participant MX as Mixpanel
U->>SB: 제출
SB->>R: 현재 경로 확인 및 '/'로 이동(필요 시)
SB->>SC: setKeyword(inputValue), setIsSearching(true)
SB->>CC: setSelectedCategory('all')
SB->>MX: track("Search Executed", { inputValue, pathname })
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~15–20 minutes
Assessment against linked issues
| Objective | Addressed | Explanation |
|---|---|---|
| 지원자를 검색할 수 있다 (MOA-154) | ✅ | |
| 지원자 검색 UI를 공통 검색창 컴포넌트로 변경 (MOA-154) | ✅ |
Assessment against linked issues: Out-of-scope changes
| Code Change | Explanation |
|---|---|
MainPage 전용 SearchBox 추가 및 검색 실행 로직/믹스패널 트래킹 추가 (frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx) |
MOA-154는 ApplicantsTab 대상 변경이 목적이며 MainPage 전용 트래킹/라우팅 로직은 이 이슈의 요구사항에 포함되지 않음. |
Header의 SearchBox import 경로 변경 (frontend/src/components/common/Header/Header.tsx) |
ApplicantsTab 검색 도입의 직접적 요구사항이 아니며 파일 재배치에 따른 파생 변경으로 보임. |
Possibly related issues
- #645: 공통 SearchField 도입 및 ApplicantsTab 연동 관련 — 동일한 공통 컴포넌트 적용 목적.
- #642: ApplicantsTab 클라이언트 검색 구현 관련 — 클라이언트 측 필터링 로직과 연관.
- [feature] MOA-154 지원자를 검색할 수 있다 #648: ApplicantsTab에서 공통 SearchField로 UI 교체 요구 — UI 교체 목표와 일치.
Possibly related PRs
- [feature] 검색 시 전체동아리 기준으로 검색된다 #616: Search 흐름 및 SearchContext/카테고리 상태 변경과 직접 연관 — 이 PR과 SearchContext 사용 및 카테고리 리셋 로직 유사.
- [feature] 믹스패널 track 2차 추가 및 기타 작업 #283: SearchBox 믹스패널 트래킹 변경과 연관 — 트래킹 이벤트명/페이로드 변경 관련 코드와 연결 가능.
- [feature] 동아리 관리자는 지원자를 확인할 수 있다. #612: ApplicantsTab 및 스타일/구성 변경 이력과 연관 — ApplicantsTab 구조·요약 카드·검색 관련 수정사항 중복 가능성.
Suggested labels
🔨 Refactor, 🎨 Design
Suggested reviewers
- lepitaaar
- Zepelown
- oesnuj
✨ Finishing Touches
- 📝 Generate Docstrings
🧪 Generate unit tests
- Create PR with unit tests
- Post copyable unit tests in a comment
- Commit unit tests in branch
feature/#648-search-applicants-MOA-154
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.
🪧 Tips
Chat
There are 3 ways to chat with CodeRabbit:
- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
I pushed a fix in commit <commit_id>, please review it.Open a follow-up GitHub issue for this discussion.
- Files and specific lines of code (under the "Files changed" tab): Tag
@coderabbitaiin a new review comment at the desired location with your query. - PR comments: Tag
@coderabbitaiin a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
Support
Need help? Create a ticket on our support page for assistance with any issues or questions.
CodeRabbit Commands (Invoked using PR/Issue comments)
Type @coderabbitai help to get the list of available commands.
Other keywords and placeholders
- Add
@coderabbitai ignoreanywhere in the PR description to prevent this PR from being reviewed. - Add
@coderabbitai summaryto generate the high-level summary at a specific location in the PR description. - Add
@coderabbitaianywhere in the PR title to generate the title automatically.
Status, Documentation and Community
- Visit our Status Page to check the current availability of CodeRabbit.
- Visit our Documentation for detailed information on how to use CodeRabbit.
- Join our Discord Community to get help, request features, and share feedback.
- Follow us on X/Twitter for updates and announcements.
There was a problem hiding this comment.
Actionable comments posted: 4
🔭 Outside diff range comments (3)
frontend/src/components/common/Header/Header.tsx (2)
59-76: 모바일 헤더에서 관리 페이지에도 SearchBox가 노출되는 문제데스크톱 헤더는 isAdminPage에서 검색창을 숨기지만(Line 101), 모바일 헤더는 항상 를 렌더링합니다. SearchBox는 SearchContext/CategoryContext가 필요하므로, Admin 영역이 해당 Provider로 감싸져 있지 않다면 런타임 에러를 유발합니다. UX 일관성 측면에서도 Admin에서 검색창을 숨기는 현재 정책과 맞지 않습니다.
다음과 같이 MobileHeader에 isAdminPage를 전달하여 조건부 렌더링하세요.
interface MobileHeaderProps { handleHomeClick: (device: 'mobile' | 'desktop') => void; handleMenuClick: () => void; + isAdminPage: boolean; } const MobileHeader = ({ handleHomeClick, handleMenuClick, + isAdminPage, }: MobileHeaderProps) => ( <Styled.MobileHeaderContainer> <Styled.MobileHeaderWrapper> <Styled.MobileMainIcon> <img src={MobileMainIcon} alt='홈 버튼' onClick={() => handleHomeClick('mobile')} /> </Styled.MobileMainIcon> - <SearchBox /> + {!isAdminPage && <SearchBox />} <Styled.MobileMenu aria-label='메뉴 버튼'> <img src={MenuBar} alt='메뉴 버튼' onClick={handleMenuClick} /> </Styled.MobileMenu> </Styled.MobileHeaderWrapper> </Styled.MobileHeaderContainer> );헤더 컴포넌트 사용처도 함께 수정해 주세요(아래 별도 코멘트 참고).
118-137: MobileHeader에 isAdminPage 전달 필요위 수정에 맞춰 Header에서 MobileHeader 호출부도 isAdminPage를 전달해주세요.
return isMobile ? ( <> <MobileHeader handleHomeClick={handleHomeClick} handleMenuClick={openMenu} + isAdminPage={isAdminPage} /> <MobileMenuDrawer isOpen={isMenuOpen} onClose={closeMenu} handleHomeClick={handleHomeClick} handleIntroduceClick={handleIntroduceClick} /> </>frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts (1)
129-136: 중첩 삼항 연산자 제거로 가독성 향상가이드라인(frontend/**/*.{ts,tsx})에 따라 중첩 삼항 대신 명시적 매핑을 권장합니다. 스타일 계산을 상수 매핑으로 정리하면 읽기 쉬워지고 오타를 줄일 수 있습니다.
아래처럼 매핑 상수와 제한된 타입을 도입해 주세요.
-export const ApplicantStatusBadge = styled.span<{ status: string }>` +type ApplicantUiStatus = '서류검토' | '면접예정' | '합격'; +const STATUS_BG: Record<ApplicantUiStatus, string> = { + 서류검토: '#E6F4FB', + 면접예정: '#E6FBF0', + 합격: '#F5F5F5', +}; + +export const ApplicantStatusBadge = styled.span<{ status: ApplicantUiStatus }>` display: inline-block; border-radius: 8px; padding: 4px 12px; font-weight: 500; font-size: 15px; - background: ${({ status }) => - status === '서류검토' - ? '#E6F4FB' - : status === '면접예정' - ? '#E6FBF0' - : status === '합격' - ? '#F5F5F5' - : '#eee'}; + background: ${({ status }) => STATUS_BG[status] ?? '#eee'}; color: ${({ status }) => (status === '합격' ? '#888' : '#222')}; `;
🧹 Nitpick comments (4)
frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx (2)
26-30: window 전역 대신 useLocation 값 사용SSR/테스트 호환성과 일관성을 위해 window.location.pathname 대신 useLocation으로 얻은 location.pathname을 사용하세요.
- trackEvent('Search Executed', { - inputValue: inputValue, - page: window.location.pathname, - }); + trackEvent('Search Executed', { + inputValue, + page: location.pathname, + });
33-39: 불필요한 람다 제거로 간결화setInputValue는 동일 시그니처이므로 그대로 전달해도 됩니다.
return ( <SearchField value={inputValue} - onChange={(v) => setInputValue(v)} + onChange={setInputValue} onSubmit={handleSearch} placeholder='어떤 동아리를 찾으세요?' ariaLabel='동아리 검색창' /> );frontend/src/components/common/SearchField/SearchField.tsx (1)
48-49: ALT 텍스트 현지화 및 일관성다른 레이블이 한국어인 만큼 이미지 alt도 한국어로 통일하는 것이 좋습니다.
- <img src={searchButtonIcon} alt='Search Button' /> + <img src={searchButtonIcon} alt='검색 버튼' />frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)
147-156: 날짜 포맷 유틸로 분리 제안IIFE 내에서 직접 포맷팅하기보다 유틸 함수로 분리하면 재사용과 테스트가 용이합니다. 또한 타임존 고려(Date만 사용 시 로컬 타임존 영향)를 명시적으로 다룰 수 있습니다.
원한다면 공용 utils/date.ts에 formatYmd(dateString: string): string 형태로 생성해드릴 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
frontend/src/components/common/Header/Header.tsx(1 hunks)frontend/src/components/common/SearchBox/SearchBox.tsx(0 hunks)frontend/src/components/common/SearchField/SearchField.tsx(1 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts(2 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx(4 hunks)frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx(1 hunks)
💤 Files with no reviewable changes (1)
- frontend/src/components/common/SearchBox/SearchBox.tsx
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.
Files:
frontend/src/components/common/Header/Header.tsxfrontend/src/pages/MainPage/components/SearchBox/SearchBox.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.tsfrontend/src/components/common/SearchField/SearchField.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
frontend/**/*.tsx
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order 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.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.
Files:
frontend/src/components/common/Header/Header.tsxfrontend/src/pages/MainPage/components/SearchBox/SearchBox.tsxfrontend/src/components/common/SearchField/SearchField.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
🧬 Code Graph Analysis (2)
frontend/src/pages/MainPage/components/SearchBox/SearchBox.tsx (2)
frontend/src/context/SearchContext.tsx (1)
useSearch(39-45)frontend/src/context/CategoryContext.tsx (1)
useCategory(11-18)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)
frontend/src/types/applicants.ts (1)
Applicant(27-33)
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
[error] 36-36: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
🔇 Additional comments (3)
frontend/src/components/common/Header/Header.tsx (1)
3-3: SearchBox 경로 변경 LGTM새 경로로의 import 변경이 적절합니다. 새 SearchBox가 default export임도 일관적입니다.
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts (1)
109-116: 클릭 가능한 행 표시 추가 LGTM테이블 행에 cursor: pointer와 hover 배경을 추가해 상호작용 affordance가 좋아졌습니다.
frontend/src/components/common/SearchField/SearchField.tsx (1)
31-33: SearchBoxContainer는 이미 form으로 정의되어 있습니다
SearchField.styles.ts에서styled.form으로 선언된 것이 확인되어 onSubmit 핸들러가 정상 동작합니다. 추가 변경은 필요하지 않습니다.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (4)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (4)
96-102: SearchField의 불필요한 blur 동작 개선
onSubmit에 빈 함수를 전달하면서autoBlur기본값(true)이 적용되어, Enter 키 입력 시 불필요하게 포커스가 해제됩니다. 실시간 검색에서는 이런 동작이 사용자 경험을 저해할 수 있습니다.<SearchField value={keyword} onChange={setKeyword} - onSubmit={() => {}} + onSubmit={() => {}} + autoBlur={false} placeholder='지원자 이름을 입력해주세요' ariaLabel='지원자 검색창' />
30-31: 훅 호출 순서 위반으로 인한 React 규칙 위배
if (!clubId) return null;이useMemo훅 호출보다 먼저 위치하여 React 훅 규칙을 위반합니다. 모든 훅은 조건문 없이 컴포넌트 최상단에서 호출되어야 합니다.const { clubId, applicantsData } = useAdminClubContext(); const [keyword, setKeyword] = useState(''); - if (!clubId) return null; const applicants: Applicant[] = applicantsData?.applicants ?? []; const filteredApplicants = useMemo(() => { const lower = keyword.trim().toLowerCase(); if (!lower) return applicants; return applicants.filter((item) => { const name = (item.answers?.[0]?.value ?? '').toString().toLowerCase(); return name.includes(lower); }); }, [applicants, keyword]); + + if (!clubId) return null;
122-126: 배열 인덱스를 key로 사용하지 말 것리스트 아이템의 key로 배열 인덱스를 사용하면 재정렬이나 필터링 시 React가 컴포넌트를 잘못 매칭하여 UI 버그가 발생할 수 있습니다. 고유한
item.id를 사용하세요.- {filteredApplicants.map((item: Applicant, index: number) => ( + {filteredApplicants.map((item: Applicant) => ( <Styled.ApplicantTableRow - key={index} + key={item.id} onClick={() => navigate(`/admin/applicants/${item.id}`)} >
143-145: answers 배열 접근 시 안전성 확보 필요
item.answers[0].value에 직접 접근하면 answers가 빈 배열이거나 undefined일 때 런타임 에러가 발생할 수 있습니다. 옵셔널 체이닝과 기본값을 사용하세요.<Styled.ApplicantTableCol> - {item.answers[0].value} + {item.answers?.[0]?.value ?? '-'} </Styled.ApplicantTableCol>
🧹 Nitpick comments (3)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts (1)
73-78: FilterContainer 네이밍 개선 필요
FilterContainer는 너무 일반적인 이름입니다. 이 컴포넌트가 지원자 탭 전용이라는 것을 명확히 하기 위해ApplicantFilterContainer로 변경하는 것이 좋습니다.-export const FilterContainer = styled.div` +export const ApplicantFilterContainer = styled.div` display: flex; justify-content: center; align-items: center; gap: 8px; `;frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (2)
127-135: 체크박스 동작 미완성체크박스가 UI에만 존재하고 실제 선택 상태 관리나 일괄 작업 기능이 구현되지 않았습니다. 향후 기능 구현이 필요합니다.
체크박스를 통한 다중 선택 및 일괄 작업(일괄 상태 변경, 일괄 삭제 등) 기능 구현이 필요하신가요? 관련 상태 관리 코드를 생성해드릴 수 있습니다.
89-95: 비활성화된 필터 옵션 처리 필요필터 셀렉트 박스들이 단일 옵션만 가지고 있어 실제로 동작하지 않습니다. 추가 옵션이 구현될 때까지 disabled 처리하거나 TODO 주석을 추가하세요.
<Styled.FilterContainer> - <Styled.ApplicantFilterSelect> + {/* TODO: 필터 옵션 구현 예정 */} + <Styled.ApplicantFilterSelect disabled> <option>전체</option> </Styled.ApplicantFilterSelect> - <Styled.ApplicantFilterSelect> + <Styled.ApplicantFilterSelect disabled> <option>제출순</option> </Styled.ApplicantFilterSelect> </Styled.FilterContainer>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts(2 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx(4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.
Files:
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.tsfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
frontend/**/*.tsx
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order 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.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.
Files:
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
🧬 Code Graph Analysis (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)
frontend/src/types/applicants.ts (1)
Applicant(27-33)
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
[error] 35-35: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
- answers가 비어 있는 경우 대비해 String(item.answers?.[0]?.value ?? '') 사용 - null/undefined도 안전하게 문자열화 후 toLowerCase 적용 - 드물게 answers[0]가 없는 데이터로 인한 크래시 예방
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (2)
124-127: 리스트 key에 index 사용은 버그를 유발 — 고유 id로 교체 필요필터링/정렬 시 index key는 재사용 문제로 렌더링 버그를 유발할 수 있습니다. 안정적인 고유 식별자인 item.id를 사용하세요.
- {filteredApplicants.map((item: Applicant, index: number) => ( + {filteredApplicants.map((item: Applicant) => ( <Styled.ApplicantTableRow - key={index} + key={item.id} onClick={() => navigate(`/admin/applicants/${item.id}`)} >
146-147: answers 접근 시 런타임 에러 가능 — 안전한 옵셔널 체이닝 적용answers 또는 answers[0]이 없을 경우 접근 시 크래시가 발생합니다. 필터 로직에서는 안전하게 처리하셨으므로 렌더링도 동일하게 맞추세요.
- <Styled.ApplicantTableCol> - {item.answers[0].value} - </Styled.ApplicantTableCol> + <Styled.ApplicantTableCol> + {item.answers?.[0]?.value ?? '-'} + </Styled.ApplicantTableCol>
🧹 Nitpick comments (3)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (3)
89-104: SearchField에 no-op onSubmit 전달 제거 제안실시간 필터만 사용한다면 onSubmit={() => {}}는 불필요합니다. 유지하면 코드 소음이 늘고 오해를 일으킬 수 있습니다. autoBlur={false}는 잘 설정되어 있습니다.
아래처럼 onSubmit을 제거하세요.
<SearchField value={keyword} onChange={setKeyword} - onSubmit={() => {}} autoBlur={false} placeholder='지원자 이름을 입력해주세요' ariaLabel='지원자 검색창' />
130-136: 체크박스 인라인 스타일의 매직 넘버 상수화가이드라인(frontend/**/*.{ts,tsx})에 따라 매직 넘버를 상수로 치환해 의미를 드러내세요.
<input type='checkbox' - style={{ width: 24, height: 24, borderRadius: 6 }} + style={{ width: CHECKBOX_SIZE, height: CHECKBOX_SIZE, borderRadius: CHECKBOX_RADIUS }} onClick={(e: React.MouseEvent<HTMLInputElement>) => e.stopPropagation() } />파일 상단(컴포넌트 근처)에 상수를 선언하세요:
// 파일 상단 (import 아래) const CHECKBOX_SIZE = 24; const CHECKBOX_RADIUS = 6;
139-144: status 매핑 중복 호출 제거 제안applicationStatusMapping(item.status)를 두 번 호출합니다. 가독성을 위해 한 번만 계산해 변수로 재사용하세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx(4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
frontend/**/*.{ts,tsx}
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.{ts,tsx}: Replace magic numbers with named constants for clarity.
Replace complex or nested ternary operators with if/else statements or IIFEs for readability.
Assign complex boolean conditions to named variables.
Use consistent return types for similar functions and hooks.
Avoid hidden side effects; functions should only perform actions implied by their signature (Single Responsibility Principle).
Use unique, descriptive names for custom wrappers and functions to avoid ambiguity.
Define constants near related logic or ensure names link them clearly.
Files:
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
frontend/**/*.tsx
📄 CodeRabbit Inference Engine (frontend/.cursorrules)
frontend/**/*.tsx: Abstract complex logic/interactions into dedicated components or higher-order 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.
Break down broad state management into smaller, focused hooks or contexts.
Use component composition instead of props drilling.
Files:
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
🧬 Code Graph Analysis (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)
frontend/src/types/applicants.ts (1)
Applicant(27-33)
🔇 Additional comments (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)
30-41: 훅 순서 문제 해결 및 필터 로직 적절early return을 훅 이후로 이동해 훅 규칙 위반이 해소되었고, useMemo 기반의 소문자/trim 검색도 간결하고 명확합니다.
| { | ||
| // createdAt을 yyyy-mm-dd 형식으로 변환 | ||
| // 임시로.. 나중에 변경해야함 | ||
| (() => { | ||
| const date = new Date(item.createdAt); | ||
| const year = date.getFullYear(); | ||
| const month = String(date.getMonth() + 1).padStart(2, '0'); | ||
| const day = String(date.getDate()).padStart(2, '0'); | ||
| return `${year}-${month}-${day}`; | ||
| })() | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
날짜 포맷팅 IIFE 제거하고 유틸 함수로 분리
인라인 IIFE는 가독성과 재사용성에 불리합니다. 공용 유틸로 추출해 사용하세요.
아래처럼 포맷팅 호출로 대체:
<Styled.ApplicantTableCol>
- {
- // createdAt을 yyyy-mm-dd 형식으로 변환
- // 임시로.. 나중에 변경해야함
- (() => {
- const date = new Date(item.createdAt);
- const year = date.getFullYear();
- const month = String(date.getMonth() + 1).padStart(2, '0');
- const day = String(date.getDate()).padStart(2, '0');
- return `${year}-${month}-${day}`;
- })()
- }
+ {formatDate(item.createdAt)}
</Styled.ApplicantTableCol>임포트 추가:
import React, { useMemo, useState } from 'react';
+import { formatDate } from '@/utils/date';
import * as Styled from './ApplicantsTab.styles';
import { useNavigate } from 'react-router-dom';
import SearchField from '@/components/common/SearchField/SearchField';유틸 파일 생성:
// frontend/src/utils/date.ts
export const formatDate = (dateString: string): string => {
const date = new Date(dateString);
if (Number.isNaN(date.getTime())) return '-';
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};Also applies to: 3-6
🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx around
lines 150-160, remove the inline IIFE date formatting and replace it with a call
to a shared util; create frontend/src/utils/date.ts exporting
formatDate(dateString: string): string which parses the date, returns '-' for
invalid dates, and formats as yyyy-mm-dd, then import formatDate into
ApplicantsTab.tsx and replace the IIFE with formatDate(item.createdAt); apply
the same replacement to the other similar IIFE occurrences in this file (lines
referenced as 3-6) so all inline date formatting uses the shared util.
#️⃣연관된 이슈
📝작업 내용
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit
New Features
Refactor
Style