Conversation
- 지원자 상세 페이지 레이아웃 및 스타일 개선 - 헤더 컨테이너 구조화 (ApplicantContainer, StatusSelect 분리) - 메모 영역 UI 개선 (MemoContainer, MemoLabel, MemoTextarea) - 커스텀 드롭다운 화살표 적용 (DropdownArrow SVG) - 상태별 배경색 동적 적용 - getStatusColor 함수로 상태별 색상 매핑 - SUBMITTED/SCREENING 동일 색상 처리 - StatusSelect 컴포넌트에 $backgroundColor prop 적용
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning
|
| Cohort / File(s) | Summary |
|---|---|
Routing import updatefrontend/src/App.tsx |
ApplicantDetailPage import 경로를 하위 디렉토리로 변경; Route JSX 소폭 포맷팅 조정. |
Applicant Detail Page 컴포넌트 및 스타일frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx, frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.styles.ts |
전용 스타일 파일 추가 및 사용(Wrapper, HeaderContainer, ApplicantContainer 등), StatusSelect에 $backgroundColor prop 추가 및 상태→색 매핑(getStatusColor) 도입, SUBMITTED 상태 포함, 메모 입력 영역(MemoLabel/MemoTextarea) 및 내비게이션 버튼 적용, 기존 메모 업데이트 로직(디바운스) 유지. |
Applicants table padding fixfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts |
테이블 헤더/셀의 non-memo 좌측 padding을 none에서 8px로 수정(메모 케이스는 30px 유지). |
사이드바 라벨 및 로그아웃 체크 포맷frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx |
/admin/applicants 탭 라벨을 "지원자 관리" → "지원자 현황"으로 변경; refreshToken 쿠키 검사식을 괄호/멀티라인으로 재포맷. |
Sequence Diagram(s)
sequenceDiagram
participant User
participant UI as ApplicantDetailPage(UI)
participant Debounce as DebouncedUpdater
participant API as updateApplicantInContext
User->>UI: 메모 입력 또는 상태 선택
UI->>Debounce: updateApplicantMemo(value) 호출 (디바운스)
Debounce-->>API: updateApplicantInContext(...) (최종 값)
API-->>UI: 업데이트된 applicant 상태/메모 반영
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~25 minutes
Assessment against linked issues
| Objective | Addressed | Explanation |
|---|---|---|
| 지원서 목록 UI 적용 (MOA-163) | ❓ | 변경된 것은 테이블 패딩과 사이드바 라벨, 상세 페이지 import 경로 등 일부에 한정되어 전체 목록 UI 적용 범위 불명확. |
| 메모칸 UI 적용 (MOA-163) | ✅ | |
| 지원자 상태(서류검토, 면접예정, 합격) UI 적용 (MOA-163) | ✅ |
Assessment against linked issues: Out-of-scope changes
| Code Change | Explanation |
|---|---|
ApplicantDetailPage import path 변경 (frontend/src/App.tsx) |
파일 이동/경로 변경은 기능 목적(MOA-163)의 UI/UX 요구와 직접적 관련이 없고 코드 조직 관련 변경임. |
사이드바 탭 라벨 변경 (frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx) |
라벨 텍스트 변경은 PO/UI 문구 업데이트로 보이며, MOA-163의 명세(목록/메모/상태 UI 구성)와 직접적인 구현 요구가 아님. |
Possibly related issues
- [feature] MOA-140 동아리 지원서 메모와 상태를 변경할 수 있다. #633: ApplicantDetailPage의 메모/상태 UI 및 동작을 다루는 이슈로, 본 PR의 memo/status 변경과 직접적 연관이 있음.
- [feature] MOA-163 지원자 현황 디자인을 적용한다 #658: 지원자 상세/목록 UI 변경을 다루며 본 PR의 스타일/레이아웃 변경과 범위가 유사함.
Possibly related PRs
- [feature] 동아리 관리자는 지원자를 확인할 수 있다. #612: ApplicantDetailPage 리스타일 및 App.tsx import 변경과 코드 영역이 겹침 — 밀접 관련.
- [Release] v1.0.9 #627: ApplicantDetailPage 리팩토링 및 스타일 파일 분리와 직접적 연관.
- [feature] 지원자의 상태와 메모를 변경한다. #651: 상태 선택자 및 메모 UI/로직 변경을 포함하는 PR로 유사 기능 중복 가능성 있음.
Suggested labels
✨ Feature
Suggested reviewers
- lepitaaar
- oesnuj
- Zepelown
Tip
🔌 Remote MCP (Model Context Protocol) integration is now available!
Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.
📜 Recent review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- frontend/src/pages/AdminPage/components/SideBar/SideBar.tsx
✨ 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/#658-applicant-page-header-ui-MOA-163
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: 0
🧹 Nitpick comments (16)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts (3)
144-145: th 좌측 패딩 8px 도입은 OK, 하지만 매직 넘버(8px/30px) 중복 사용을 상수화해 주세요두 곳(td/th)에 동일한 하드코딩 값이 반복됩니다. 상수(또는 theme 토큰)로 추출하면 유지보수성과 일관성이 좋아집니다.
아래 diff는 해당 라인만 상수로 치환합니다:
- padding-left: ${({ isMemo }) => (isMemo ? '30px' : '8px')}; + padding-left: ${({ isMemo }) => (isMemo ? PADDING_LEFT_MEMO : PADDING_LEFT_NON_MEMO)};그리고 파일 상단(또는 인접한 곳)에 상수를 추가하세요:
// 파일 상단 부근에 추가 const PADDING_LEFT_NON_MEMO = '8px'; const PADDING_LEFT_MEMO = '30px';
170-175: td 좌측 패딩 8px도 동일 상수 사용으로 통일 권장thead(th)와 tbody(td) 간 좌측 패딩이 상수로 통일되면 추후 디자인 스케일링 시 실수를 줄일 수 있습니다.
- padding-left: ${({ isMemo }) => (isMemo ? '30px' : '8px')}; + padding-left: ${({ isMemo }) => (isMemo ? PADDING_LEFT_MEMO : PADDING_LEFT_NON_MEMO)};
142-143: width prop 처리 개선: string에 px가 중복될 수 있습니다현재 구현은 width?: number | string 임에도 무조건
${width}px를 붙여서 '10%px' 같은 잘못된 값을 만들 수 있습니다. number/string 분기 처리가 필요합니다.- width: ${({ width }) => (width ? `${width}px` : 'auto')}; + width: ${({ width }) => + width === undefined || width === null + ? 'auto' + : typeof width === 'number' + ? `${width}px` + : width};frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.styles.ts (5)
77-80: StatusSelect 포커스 아웃라인 제거는 접근성 저하 가능성 — focus-visible 스타일을 추가하세요outline을 완전히 제거하면 키보드 사용자 접근성이 떨어집니다. focus-visible로 대체 표시를 제공해 주세요.
&:focus { outline: none; } + &:focus-visible { + outline: 2px solid var(--4B, #4b4b4b); + outline-offset: 2px; + }
105-106: MemoLabel의 color: var(--, #111)는 의도된 변수명인가요?커스텀 프로퍼티 이름이
--로 비어 있어 가독성이 떨어집니다. 변수 사용 의도가 없다면 직접 색상값을 지정하는 편이 명확합니다.- color: var(--, #111); + color: #111;
110-119: 중복된 height 선언 제거&::after 내부에
height가 두 번 선언되어 있습니다. 불필요 선언을 제거해 주세요.&::after { content: ''; position: absolute; right: -20px; top: 30%; - height: 40%; width: 4px; height: 14px; background: #d9d9d9; }
22-33: 고정 gap(240px)과 폭 82%는 반응형에서 취약 — 레이아웃 전략 개선 제안헤더 내 탐색/셀렉트 배치를 gap 240px에 의존하면 좁은 화면에서 깨질 수 있습니다. space-between, min/max-width, flex-wrap 조합으로 유연한 배치를 검토해 주세요. 또한 매직 넘버(240px, 82%)는 상수/테마 토큰으로 추출 권장입니다.
원하시면 반응형 대응을 위한 레이아웃 스니펫(예: space-between + minmax)과 테마 토큰 적용안을 만들어 드리겠습니다.
52-76: StatusSelect의 width: 18%는 컨테이너 종속성이 큼 — min-width 기반으로 안정화 권장퍼센트 폭은 부모 폭에 크게 좌우됩니다. 최소 너비(min-width 160~200px 등)를 두고, 나머지는 컨테이너 여유 공간에 맡기면 레이아웃 안정성이 좋아집니다. 또한 8/12/30px 등 패딩 값은 상수/테마 토큰 추출을 권장합니다.
frontend/src/App.tsx (1)
81-88: 라우트 파라미터questionId→applicantId일괄 변경앱 전역에서 더 명확한 파라미터명을 사용하기 위해 아래 두 곳을 수정해주세요.
• frontend/src/App.tsx
- <Route - path='applicants/:questionId' - element={<ApplicantDetailPage />} - /> + <Route + path='applicants/:applicantId' + element={<ApplicantDetailPage />} + />• frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (39번째 줄)
- const { questionId } = useParams<{ questionId: string }>(); + const { applicantId } = useParams<{ applicantId: string }>();
- 컴포넌트 내에서
questionId로 사용 중인 모든 변수 및 타입 선언을applicantId로 변경했는지 확인해주세요.[optional_refactors_recommended]
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (7)
24-36: 상태별 배경색 매핑은 OK, 중복 색상 값은 상수/토큰화 권장'#E5F6FF', '#E9FFF1', 'var(--f5, #F5F5F5)'가 여러 곳에서 재사용될 가능성이 높습니다. 상수 또는 theme(Styled-Components ThemeProvider) 토큰으로 추출하면 유지보수에 유리합니다.
예: const STATUS_BG = { ACCEPTED: 'var(--f5, #F5F5F5)', SCREENING: '#E5F6FF', INTERVIEW_SCHEDULED: '#E9FFF1', DEFAULT: 'var(--f5, #F5F5F5)' };
62-73: debounce 콜백 any 사용과 매직 넘버(400ms) 추출타입을 구체화하고(문자열/enum), 400ms를 상수로 올리는 것이 좋습니다. 또한 언마운트 시 지연 호출을 취소할 수 있으면 더 안전합니다.
- debounce((memo: any, status: any) => { - updateApplicantMemo( - memo as string, - status as ApplicationStatus, - clubId!, - questionId!, - ); - }, 400), + debounce((memo: string, status: ApplicationStatus) => { + updateApplicantMemo(memo, status, clubId!, questionId!); + }, DEBOUNCE_MS),파일 상단 인근에 상수를 추가해 주세요:
const DEBOUNCE_MS = 400;옵션: debounce 유틸이 cancel을 제공한다면 cleanup도 고려해 주세요.
useEffect(() => () => (updateApplicantDetail as any)?.cancel?.(), [updateApplicantDetail]);
105-111: 메모 입력 onInput → onChange 권장React에서는 텍스트 입력 제어에 onChange가 일반적이며 합성 이벤트 레벨에서 더 예측 가능합니다. onChange로 일관성 맞추는 것을 권장합니다.
JSX 쪽 변경도 함께 필요합니다(하단 코멘트 참고).
149-153: 옵션 라벨 생성 시 안전성 보강 필요
a.answers[0].value는 첫 답변 부재 시 런타임 에러가 납니다. 옵셔널 체이닝/폴백을 두세요.- <option key={a.id} value={a.id}> - {a.answers[0].value} - </option> + <option key={a.id} value={a.id}> + {a.answers[0]?.value ?? a.id} + </option>
176-181: onInput 사용을 onChange로 교체위에서 제안한 대로 핸들러도 onChange로 매칭해 주세요.
- <Styled.MemoTextarea - onInput={handleMemoChange} + <Styled.MemoTextarea + onChange={handleMemoChange} placeholder='메모를 입력해주세요' value={applicantMemo} ></Styled.MemoTextarea>
186-186: inline style 중복 제거Styled.QuestionsWrapper 자체에서 cursor: default를 지정하므로 inline style은 불필요합니다.
- <Styled.QuestionsWrapper style={{ cursor: 'default' }}> + <Styled.QuestionsWrapper>
39-41: 라우트 파라미터명questionId→applicantId로 통일 권장지원자 ID를 나타내는 경로 및 변수명이
questionId로 혼동을 일으키고 있으므로, 아래 항목을 참조해applicantId로 일괄 변경을 추천드립니다.• frontend/src/App.tsx (86행)
- path:
applicants/:questionId→applicants/:applicantId
• frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx- useParams 선언 (39행)
- applicants.findIndex 비교식 (49행)
- useMemo 의존성 배열 (53행)
- API 호출 파라미터 (69, 72행)
- 주석(81행)
변경 예시:
- const { questionId } = useParams<{ questionId: string }>(); + const { applicantId } = useParams<{ applicantId: string }>();레포 전체 사용처 점검:
rg -n -C2 -P "questionId(?!\")|useParams<\s*{\s*questionId\s*:\s*string\s*}\s*>" -g '*.ts' -g '*.tsx'[optional_refactors_recommended]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (5)
frontend/src/assets/images/icons/applicant_drop.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/back_arrow_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/forward_arrow_icon.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/next_applicant.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/prev_applicant.svgis excluded by!**/*.svg
📒 Files selected for processing (4)
frontend/src/App.tsx(2 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.styles.ts(1 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx(6 hunks)frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts(2 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/ApplicantDetailPage/ApplicantDetailPage.styles.tsfrontend/src/App.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.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/App.tsxfrontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: run
🔇 Additional comments (3)
frontend/src/App.tsx (1)
21-21: ApplicantDetailPage 경로 변경 LGTM폴더 구조 리팩토링과 맞물린 import 경로 업데이트 적절합니다.
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (2)
18-23: AVAILABLE_STATUSES 구성 적절SUBMITTED를 SCREENING 그룹에 포함시키는 의도를 주석으로 명시한 점 좋습니다. 불필요한 상태 노출 없이 UI 요구사항에 맞습니다.
45-46: clubId null 단언(!!) 사용 시 사전 가드 검증 요청
- 파일: frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (45–46)
useGetApplication(clubId!)호출 시clubId가 아직 undefined인 초기 렌더에서 API 요청이 발생하지 않도록, 훅 내부에 React Query의enabled옵션 등으로 가드가 적용되어 있는지 확인해주세요.- 내부 가드가 없다면 아래 패턴 적용 또는 상위 컴포넌트에서
clubId로딩 완료 후 렌더링하도록 수정을 권장드립니다.
- useGetApplication(clubId!, { enabled: !!clubId })
- 또는
{clubId && <ApplicantDetailPage clubId={clubId} />}형태의 조건부 렌더링
#️⃣연관된 이슈
📝작업 내용
ApplicationDetailPage 파일분리
ApplicationDetailPage/ApplicationDetailPage.tsx를 두었습니다.ApplicationDetailPage.styles.ts)도 따로 추가했습니다.지원자 현황 헤더
SCREENING과SUBMITTED는 "서류 검토"라는 공통적인 상태로 관리합니다.그 외 작업
중점적으로 리뷰받고 싶은 부분(선택)
논의하고 싶은 부분(선택)
🫡 참고사항
Summary by CodeRabbit