Skip to content

[feature] 관리자 모바일 지원자 현황 리스트 컴포넌트 추가 - #1886

Open
suhyun113 wants to merge 12 commits into
develop-fefrom
feature/#1885-mobile-applicants-shared-components-MOA-1055
Open

[feature] 관리자 모바일 지원자 현황 리스트 컴포넌트 추가#1886
suhyun113 wants to merge 12 commits into
develop-fefrom
feature/#1885-mobile-applicants-shared-components-MOA-1055

Conversation

@suhyun113

@suhyun113 suhyun113 commented Aug 2, 2026

Copy link
Copy Markdown
Collaborator

#️⃣연관된 이슈

#1885

📝작업 내용

지원자 현황 페이지 모바일 구현에 필요한 공통 컴포넌트 7개를 추가했습니다.

추가된 컴포넌트 (components/mobile/)

컴포넌트 설명
ApplicantListRow 지원자 목록 행 (체크박스, 이름/메모, 상태태그, 날짜)
ApplicantSearchBox 지원자 이름 검색 입력창 (포커스 시 테두리 색상 변경)
BulkActionBar 전체선택 후 상태변경 / 삭제 액션 바
FormDropdownSelector 지원서 선택 드롭다운 (3개 초과 시 커스텀 스크롤바)
SortDropdown 제출순 / 이름순 정렬 드롭다운
StatusFilterPills 전체 / 검토 전 / 면접예정 / 합격 / 불합격 필터 pill
StatusSummaryCard 상태별 지원자 수 요약 카드

기타 변경사항

  • ExpandArrow.svg 제거 → more_arraw_icon.svg?react로 통일 (stroke="currentColor" 적용)
  • triangle_down.svg 추가 (BulkActionBar 드롭다운 아이콘)
  • ApplicantsInfo 타입에 declined 필드 추가
  • mapStatusToGroup 라벨 수정: 서류검토검토 전, 불합불합격

각 컴포넌트에 Storybook 스토리 포함 (상태별 / 인터랙티브)

🔗 Storybook: https://67904e61c16daa99a63b44a7-glatxlhpyz.chromatic.com/

중점적으로 리뷰받고 싶은 부분(선택)

  • FormDropdownSelector 커스텀 스크롤바 구현 방식 (scrollTop 기반 thumb offset 계산)
  • BulkActionBar 활성/비활성 상태 처리 방식

🫡 참고사항

각 컴포넌트는 독립적으로 구현되어 있으며, 페이지 조합은 별도 브랜치에서 진행 예정입니다.

Summary by CodeRabbit

  • 새로운 기능
    • 모바일 지원자 목록에서 검색, 상태 필터, 정렬 기능을 제공합니다.
    • 지원자별 이름·메모·상태·등록일을 확인하고 개별 선택할 수 있습니다.
    • 선택한 지원자의 상태를 일괄 변경하거나 삭제할 수 있습니다.
    • 지원 상태별 인원 요약 카드와 지원서 선택 드롭다운을 추가했습니다.
  • 개선
    • 지원 상태 명칭을 더 명확하게 변경했습니다.
    • 목록 펼치기 아이콘의 표시와 크기를 개선했습니다.
  • 버그 수정
    • 지원자 선택 시 행 클릭 동작과 분리되어 의도치 않은 화면 이동을 방지합니다.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
moadong Ready Ready Preview Aug 2, 2026 2:19pm

@github-actions github-actions Bot added ✨ Feature 기능 개발 💻 FE Frontend labels Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

모바일 지원자 관리 UI에 지원자 행, 검색, 상태 필터, 정렬, 지원서 선택, 일괄 작업, 상태 요약 컴포넌트와 Storybook 스토리를 추가했다. 지원자 상태 데이터와 라벨을 확장하고 기존 확장 화살표 렌더링을 변경했다.

Changes

모바일 지원자 관리

Layer / File(s) Summary
지원자 행 표시
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/*
지원자 이름, 메모, 상태, 생성일과 체크 상태를 표시하는 행 컴포넌트와 스타일을 추가했다. 단일·복수 지원자 Storybook 상태를 추가했다.
검색·필터·정렬 컨트롤
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantSearchBox/*, .../StatusFilterPills/*, .../SortDropdown/*
제어형 검색 입력, 상태 필터 토글, 제출순·이름순 정렬 드롭다운을 추가했다. 각 상태의 Storybook 스토리를 추가했다.
지원서 선택 드롭다운
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/*
지원서 선택 메뉴와 선택 상태를 추가했다. 지원서 수에 따른 고정 메뉴, 스크롤 목록, 커스텀 스크롤바를 구현했다.
일괄 작업과 상태 요약
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/BulkActionBar/*, .../StatusSummaryCard/*
활성화된 선택 항목의 상태 변경·삭제 메뉴와 상태별 집계 카드를 추가했다. 관련 Storybook 상태를 추가했다.
상태 데이터와 화살표 업데이트
frontend/src/types/applicants.ts, frontend/src/utils/mapStatusToGroup.ts, frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.styles.ts, frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx
ApplicantsInfo.declined 필드를 추가했다. SUBMITTEDDECLINED 라벨을 변경했다. 확장 화살표를 MoreArrowIcon 기반으로 변경하고 이미지 src 전달을 제거했다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related issues

Possibly related PRs

  • Moadong/moadong#612 — 기존 지원자 관리 UI를 확장하는 모바일 컴포넌트 변경과 직접 연결된다.
  • Moadong/moadong#705 — 거절 상태와 지원자 상태 집계 변경이 직접 연결된다.
  • Moadong/moadong#754mapStatusToGroup과 지원자 상태 필터·정렬 UI 변경이 직접 연결된다.

Suggested labels: 🎨 Design

Suggested reviewers: oesnuj, zepelown, seongwon030

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 관리자용 모바일 지원자 현황 리스트 컴포넌트 추가라는 PR의 주요 변경 사항을 명확하게 설명합니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/#1885-mobile-applicants-shared-components-MOA-1055

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@suhyun113 suhyun113 changed the title feat(admin): 지원자 현황 모바일 공통 컴포넌트 추가 [feature] 관리자는 모바일에서 지원자 현황을 조회하고 지원자 상태를 관리할 수 있다 Aug 2, 2026
@github-actions

github-actions Bot commented Aug 2, 2026

Copy link
Copy Markdown

✅ UI 변경사항 없음

구분 링크
📖 Storybook https://67904e61c16daa99a63b44a7-yhstirsifi.chromatic.com/

전체 128개 스토리 · 49개 컴포넌트

@suhyun113
suhyun113 changed the base branch from main to develop-fe August 2, 2026 14:07

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (5)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx (1)

21-27: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Storybook 레이아웃에도 styled-components를 사용해야 합니다.

  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx#L21-L27: decorator의 인라인 styleStoryViewport styled component로 교체하십시오.
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx#L152-L164: 다중 행 컨테이너의 인라인 style을 같은 styled component로 재사용하십시오.
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusSummaryCard/StatusSummaryCard.stories.tsx#L9-L15: decorator의 인라인 style을 styled component로 교체하십시오.

As per coding guidelines, Use styled-components and the project theme system for styling. 규칙을 적용해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx`
around lines 21 - 27, Replace the inline width styles in
ApplicantListRow.stories.tsx at lines 21-27 and 152-164 with a shared
StoryViewport styled component, defining its styling through the project’s
styled-components/theme system and reusing it for both the decorator and
multi-row container. Also replace the decorator’s inline style in
StatusSummaryCard.stories.tsx at lines 9-15 with the appropriate StoryViewport
styled component, creating or reusing the same styled-component pattern.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx (1)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

import 순서와 type-only import를 정리해야 합니다.

  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx#L1-L3: mapStatusToGroup를 먼저 import하고, Applicantimport type으로 분리한 뒤 styles import를 마지막에 두십시오.
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx#L1-L5: 내부 모듈 import 뒤에 Meta, StoryObj 등 type import를 두십시오.
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/BulkActionBar/BulkActionBar.stories.tsx#L1-L2: BulkActionBar import 뒤에 Storybook type import를 두십시오.
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusSummaryCard/StatusSummaryCard.stories.tsx#L1-L2: StatusSummaryCard import 뒤에 Storybook type import를 두십시오.

As per coding guidelines, external libraries, internal modules, types, then styles 순서를 적용해야 합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx`
around lines 1 - 3, 정해진 import 순서와 type-only import 규칙에 맞게 네 곳의 import를 정리하십시오.
ApplicantListRow.tsx에서는 mapStatusToGroup, type Applicant, Styled 순으로 배치하고,
ApplicantListRow.stories.tsx, BulkActionBar.stories.tsx,
StatusSummaryCard.stories.tsx에서는 각 내부 컴포넌트 import를 먼저 둔 뒤 Meta·StoryObj 등
Storybook type import를 배치하십시오. 변경 대상은
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx
1-3,
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx
1-5,
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/BulkActionBar/BulkActionBar.stories.tsx
1-2,
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusSummaryCard/StatusSummaryCard.stories.tsx
1-2입니다.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/FormDropdownSelector.tsx (1)

2-12: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

ApplicationFormId 타입을 유지하세요.

라인 10-11은 ApplicationFormItem.id가 사용하는 ApplicationFormId 대신 string을 노출합니다. 유효하지 않은 ID가 선택 상태와 onSelect 경계를 통과할 수 있습니다. selectedFormIdonSelect의 인자를 ApplicationFormId로 지정하세요.

수정 예시
-import { ApplicationFormItem } from '`@/types/application`';
+import type { ApplicationFormId, ApplicationFormItem } from '`@/types/application`';
 
 interface FormDropdownSelectorProps {
   forms: ApplicationFormItem[];
-  selectedFormId: string;
-  onSelect: (formId: string) => void;
+  selectedFormId: ApplicationFormId;
+  onSelect: (formId: ApplicationFormId) => void;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/FormDropdownSelector.tsx`
around lines 2 - 12, Update FormDropdownSelectorProps so selectedFormId and the
onSelect callback parameter use ApplicationFormId instead of string, importing
that type alongside ApplicationFormItem. Preserve the existing forms and
selection behavior while keeping the component’s ID boundary type-safe.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantSearchBox/ApplicantSearchBox.stories.tsx (1)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Storybook 파일의 import 순서를 통일하세요.

타입 전용 import는 내부 컴포넌트 import 뒤에 와야 합니다.

  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantSearchBox/ApplicantSearchBox.stories.tsx#L1-L3: ApplicantSearchBox import 뒤로 Meta, StoryObj 타입 import를 이동하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/SortDropdown/SortDropdown.stories.tsx#L1-L3: SortDropdown import 뒤로 Meta, StoryObj 타입 import를 이동하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/FormDropdownSelector.stories.tsx#L1-L4: FormDropdownSelector import 뒤로 Meta, StoryObj 타입 import를 이동하세요.

As per coding guidelines, imports must be ordered as external libraries, internal modules, types, then styles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantSearchBox/ApplicantSearchBox.stories.tsx`
around lines 1 - 3, Reorder imports in ApplicantSearchBox.stories.tsx (lines
1-3), SortDropdown.stories.tsx (lines 1-3), and FormDropdownSelector.stories.tsx
(lines 1-4) so each internal component import (ApplicantSearchBox, SortDropdown,
or FormDropdownSelector) precedes the type-only Meta and StoryObj import,
following the external libraries, internal modules, types, then styles
convention.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusFilterPills/StatusFilterPills.stories.tsx (1)

1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

React.ComponentProps의 전역 namespace 의존성을 제거하세요.

이 파일은 React 값을 import하지 않지만 React.ComponentProps에 전역 UMD namespace을 의존합니다. 설정 의존성을 없애기 위해 ComponentProps를 type import로 가져와서 사용하세요.

수정 예시
-import { useState } from 'react';
+import { useState, type ComponentProps } from 'react';
 
-const Interactive = (args: React.ComponentProps<typeof StatusFilterPills>) => {
+const Interactive = (args: ComponentProps<typeof StatusFilterPills>) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusFilterPills/StatusFilterPills.stories.tsx`
at line 1, Update StatusFilterPills.stories.tsx to type-import ComponentProps
from React and replace the React.ComponentProps usage with the imported type,
removing reliance on the global React namespace while preserving the existing
prop typing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx`:
- Around line 33-35: Update the InteractiveRow args type to use a type-only
ComponentProps import from react instead of React.ComponentProps, then reference
ComponentProps<typeof ApplicantListRow> directly while preserving the existing
component behavior.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx`:
- Around line 30-38: ApplicantListRow의 Styled.Row와 Styled.CheckboxWrapper를 스크립트
이벤트 전용 div 대신 네이티브 버튼과 연결된 label/input type="checkbox" 구조로 변경하십시오. 행 버튼은 키보드로 열
수 있게 하고, 체크박스는 onCheck(applicant.id)와 isChecked 상태를 연결하며 접근 가능한 label과 ARIA 상태를
제공하십시오. 체크박스 조작이 행 클릭을 함께 실행하지 않도록 기존 이벤트 분리 동작도 유지하십시오.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/BulkActionBar/BulkActionBar.tsx`:
- Around line 25-28: Update BulkActionBar’s handleStatusSelect and status-menu
interaction so disabled bulk actions cannot trigger onStatusChange: when enabled
is false, close the menu and return without invoking the callback. Ensure the
menu also closes when enabled becomes false after opening, including the related
enabled-dependent logic around the status menu.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/FormDropdownSelector.tsx`:
- Around line 19-21: Reset thumbOffset whenever the dropdown is opened or before
the conditionally rendered MenuList is recreated, so reopening starts with a
thumb position of 0 matching the list’s reset scrollTop. Update the state flow
around isOpen, thumbOffset, and the MenuList rendering without changing the
existing open/close behavior.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/SortDropdown/SortDropdown.tsx`:
- Around line 25-33: Remove the setIsOpen(false) call from the CustomDropDown
onSelect handler so it only invokes onChange(v); keep menu closing delegated to
CustomDropDown’s existing onToggle behavior.

In `@frontend/src/types/applicants.ts`:
- Line 16: ApplicantsInfo 생성 경로에서 declined 집계를 실제 값으로 유지하도록 수정하세요.
getClubApplicants의 API 응답 매핑과 useApplicantSSE의 prevData 기반 인라인 객체에 declined를
포함하고, 지원자 상태가 ApplicationStatus.DECLINED로 변경될 때 해당 집계를 갱신해 초기값 0에 고정되지 않도록 하세요.

---

Nitpick comments:
In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx`:
- Around line 21-27: Replace the inline width styles in
ApplicantListRow.stories.tsx at lines 21-27 and 152-164 with a shared
StoryViewport styled component, defining its styling through the project’s
styled-components/theme system and reusing it for both the decorator and
multi-row container. Also replace the decorator’s inline style in
StatusSummaryCard.stories.tsx at lines 9-15 with the appropriate StoryViewport
styled component, creating or reusing the same styled-component pattern.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx`:
- Around line 1-3: 정해진 import 순서와 type-only import 규칙에 맞게 네 곳의 import를 정리하십시오.
ApplicantListRow.tsx에서는 mapStatusToGroup, type Applicant, Styled 순으로 배치하고,
ApplicantListRow.stories.tsx, BulkActionBar.stories.tsx,
StatusSummaryCard.stories.tsx에서는 각 내부 컴포넌트 import를 먼저 둔 뒤 Meta·StoryObj 등
Storybook type import를 배치하십시오. 변경 대상은
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx
1-3,
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx
1-5,
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/BulkActionBar/BulkActionBar.stories.tsx
1-2,
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusSummaryCard/StatusSummaryCard.stories.tsx
1-2입니다.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantSearchBox/ApplicantSearchBox.stories.tsx`:
- Around line 1-3: Reorder imports in ApplicantSearchBox.stories.tsx (lines
1-3), SortDropdown.stories.tsx (lines 1-3), and FormDropdownSelector.stories.tsx
(lines 1-4) so each internal component import (ApplicantSearchBox, SortDropdown,
or FormDropdownSelector) precedes the type-only Meta and StoryObj import,
following the external libraries, internal modules, types, then styles
convention.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/FormDropdownSelector.tsx`:
- Around line 2-12: Update FormDropdownSelectorProps so selectedFormId and the
onSelect callback parameter use ApplicationFormId instead of string, importing
that type alongside ApplicationFormItem. Preserve the existing forms and
selection behavior while keeping the component’s ID boundary type-safe.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusFilterPills/StatusFilterPills.stories.tsx`:
- Line 1: Update StatusFilterPills.stories.tsx to type-import ComponentProps
from React and replace the React.ComponentProps usage with the imported type,
removing reliance on the global React namespace while preserving the existing
prop typing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5076e2be-4914-4ec7-b32c-270fc8197888

📥 Commits

Reviewing files that changed from the base of the PR and between fefd349 and ea47dec.

⛔ Files ignored due to path filters (3)
  • frontend/src/assets/images/icons/ExpandArrow.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/more_arraw_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/triangle_down.svg is excluded by !**/*.svg
📒 Files selected for processing (25)
  • frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantSearchBox/ApplicantSearchBox.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantSearchBox/ApplicantSearchBox.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantSearchBox/ApplicantSearchBox.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/BulkActionBar/BulkActionBar.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/BulkActionBar/BulkActionBar.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/BulkActionBar/BulkActionBar.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/FormDropdownSelector.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/FormDropdownSelector.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/FormDropdownSelector.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/SortDropdown/SortDropdown.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/SortDropdown/SortDropdown.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/SortDropdown/SortDropdown.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusFilterPills/StatusFilterPills.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusFilterPills/StatusFilterPills.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusFilterPills/StatusFilterPills.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusSummaryCard/StatusSummaryCard.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusSummaryCard/StatusSummaryCard.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/StatusSummaryCard/StatusSummaryCard.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationListTab.styles.ts
  • frontend/src/types/applicants.ts
  • frontend/src/utils/mapStatusToGroup.ts

Comment on lines +33 to +35
const InteractiveRow = (
args: React.ComponentProps<typeof ApplicantListRow>,
) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

fd -HI -t f 'tsconfig.*\.json$' . -x sh -c '
  echo "== $1 =="
  rg -n "\"allowUmdGlobalAccess\"" "$1" || true
' sh {}

rg -n -C 2 'React\.ComponentProps' \
  frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx

Repository: Moadong/moadong

Length of output: 1980


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== frontend/tsconfig.json =="
cat -n frontend/tsconfig.json

echo "== ApplicantListRow.stories.tsx imports and ComponentProps usages =="
sed -n '1,80p' frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx | cat -n

echo "== package versions relevant to TypeScript/React =="
node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('frontend/package.json','utf8'));
for (const k of ['typescript','react','gatsby','vite']) {
  console.log(k, pkg.dependencies?.[k] || pkg.devDependencies?.[k] || '(not listed)');
}
JS

echo "== vite.config storybook/ts context =="
fd -HI -t f 'vite.config.*|storybook.*|tsconfig.*' frontend -x sh -c 'echo "== $1 =="; sed -n "1,220p" "$1"' sh {}

Repository: Moadong/moadong

Length of output: 50372


🌐 Web query:

TypeScript allowUmdGlobalAccess allows accessing React like module UMD global exports

💡 Result:

The allowUmdGlobalAccess compiler option in TypeScript enables access to UMD global exports from within module files [1][2]. By default, TypeScript prevents code inside a module file (a file containing import or export declarations) from accessing global variables declared by UMD libraries [1][3]. This design enforces the practice of importing dependencies explicitly [1]. However, when allowUmdGlobalAccess is set to true in your tsconfig.json, TypeScript will allow you to reference these UMD globals (such as React, if it is available as a global) from anywhere in your project, including within modules [1][3]. Key details regarding this flag: - Purpose: It serves as an escape hatch for scenarios where a library is provided as a global variable at runtime (e.g., via a script tag) but you cannot or choose not to import it [1][4]. - Scope: It affects all UMD global declarations, not just a specific library [3]. - Safety: Enabling this flag is generally considered less safe than explicit imports, as it may hide errors where you intended to import a module but forgot, or where a library is not actually present in the global scope at runtime [4][3]. To use it, add the flag to your tsconfig.json under compilerOptions: { "compilerOptions": { "allowUmdGlobalAccess": true } } This feature was introduced in TypeScript 3.5 [3][5].

Citations:


React.ComponentProps 대신 타입 import를 사용해 주세요.

이 파일은 이미 import문을 사용해 TypeScript 모듈입니다. 현재 설정에서 React 전역 네임스페이스는 타입에 의존하지 않는 런타임 네임스페이스로 해석되기 때문에 React.ComponentProps<typeof ApplicantListRow>가 선언되지 않습니다. import type { ComponentProps } from 'react';와 함께 args: ComponentProps<typeof ApplicantListRow>로 변경해 Type-only import 규칙을 따르세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.stories.tsx`
around lines 33 - 35, Update the InteractiveRow args type to use a type-only
ComponentProps import from react instead of React.ComponentProps, then reference
ComponentProps<typeof ApplicantListRow> directly while preserving the existing
component behavior.

Comment on lines +30 to +38
<Styled.Row onClick={onClick}>
<Styled.CheckboxWrapper
onClick={(e) => {
e.stopPropagation();
onCheck(applicant.id);
}}
>
<Styled.Checkbox $checked={isChecked} />
</Styled.CheckboxWrapper>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx"
if [ -f "$file" ]; then
  echo "== file exists =="
  wc -l "$file"
  echo "== outline =="
  ast-grep outline "$file" --view expanded || true
  echo "== full file =="
  cat -n "$file"
else
  echo "missing $file"
  echo "== similar files =="
  fd -a 'ApplicantListRow\.tsx$' .
fi

echo "== related styled components definitions =="
rg -n "Styled\.Row|Styled\.CheckboxWrapper|Checkbox\$\|styled\.div|styled\.Checkbox|Checkbox\\\(\\\$checked|onClick=" frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow -S || true

Repository: Moadong/moadong

Length of output: 2788


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.styles.ts"
echo "== styles file =="
if [ -f "$file" ]; then
  wc -l "$file"
  cat -n "$file"
else
  echo "missing $file"
  fd -a 'ApplicantListRow\.styles\.ts$' .
fi

echo "== broader style definitions in file =="
rg -n "styled\.(div|button|input)|\$\$|Checkbox|\$checked|checkbox|radio|Row|CheckboxWrapper|Info|Name|StatusTag|Date|Tag" "$file" -S || true

echo "== prop/event definitions in parent =="
rowParent="$(fd 'ApplicantListTab|ApplicantsListTab|ApplicantsTab' frontend/src/pages | head -30)"
echo "$rowParent"
rg -n "ApplicantListRow|onCheck|onClick=|isSelected|isChecked|checkbox|radio" frontend/src/pages/AdminPage/tabs/ApplicantsTab -S || true

Repository: Moadong/moadong

Length of output: 16161


행과 선택 컨트롤에 키보드 접근성을 구현하십시오.

Styled.RowStyled.CheckboxWrapper가 스크립트 이벤트만으로 동작하는 div라서 키보드 사용자는 행 열기와 지원자 선택을 사용할 수 없습니다. 선택 상태를 알리는 ARIA 상태도 없습니다. 행 열기는 버튼을, 선택 전환은 연결된 label이 있는 input type="checkbox"로 구현해 네이티브 키보드/스크린 리더 동작을 사용하십시오.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/ApplicantListRow/ApplicantListRow.tsx`
around lines 30 - 38, ApplicantListRow의 Styled.Row와 Styled.CheckboxWrapper를 스크립트
이벤트 전용 div 대신 네이티브 버튼과 연결된 label/input type="checkbox" 구조로 변경하십시오. 행 버튼은 키보드로 열
수 있게 하고, 체크박스는 onCheck(applicant.id)와 isChecked 상태를 연결하며 접근 가능한 label과 ARIA 상태를
제공하십시오. 체크박스 조작이 행 클릭을 함께 실행하지 않도록 기존 이벤트 분리 동작도 유지하십시오.

Comment on lines +25 to +28
const handleStatusSelect = (status: ApplicationStatus) => {
onStatusChange(status);
setIsStatusMenuOpen(false);
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

비활성화된 일괄 작업에서 상태 변경을 차단하세요.

상태 메뉴를 연 뒤 선택이 해제되면 enabledfalse가 되어도 메뉴는 남습니다. 이 상태에서 메뉴 항목을 누르면 onStatusChange가 실행됩니다. enabledfalse일 때 메뉴를 닫고 handleStatusSelect에서도 callback을 호출하지 마세요.

수정 예시
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 
   const [isStatusMenuOpen, setIsStatusMenuOpen] = useState(false);
 
+  useEffect(() => {
+    if (!enabled) setIsStatusMenuOpen(false);
+  }, [enabled]);
+
   const handleStatusSelect = (status: ApplicationStatus) => {
+    if (!enabled) return;
     onStatusChange(status);
     setIsStatusMenuOpen(false);
   };

Also applies to: 40-51

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/BulkActionBar/BulkActionBar.tsx`
around lines 25 - 28, Update BulkActionBar’s handleStatusSelect and status-menu
interaction so disabled bulk actions cannot trigger onStatusChange: when enabled
is false, close the menu and return without invoking the callback. Ensure the
menu also closes when enabled becomes false after opening, including the related
enabled-dependent logic around the status menu.

Comment on lines +19 to +21
const [isOpen, setIsOpen] = useState(false);
const [thumbOffset, setThumbOffset] = useState(0);
const listRef = useRef<HTMLDivElement>(null);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

메뉴를 다시 열기 전에 thumb 위치를 초기화하세요.

라인 52의 조건부 렌더링은 닫힌 MenuList를 제거합니다. 다시 열면 목록의 scrollTop은 0이지만 thumbOffset은 이전 값을 유지합니다. 사용자가 다시 열면 thumb가 실제 목록 위치와 다르게 표시됩니다.

수정 예시
   const [thumbOffset, setThumbOffset] = useState(0);
   const listRef = useRef<HTMLDivElement>(null);
 
+  const handleToggle = () => {
+    if (!isOpen) setThumbOffset(0);
+    setIsOpen((prev) => !prev);
+  };
+
   return (
     <Styled.Wrapper>
       <Styled.Trigger
         $isOpen={isOpen}
-        onClick={() => setIsOpen((prev) => !prev)}
+        onClick={handleToggle}
       >

Also applies to: 42-57

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/FormDropdownSelector/FormDropdownSelector.tsx`
around lines 19 - 21, Reset thumbOffset whenever the dropdown is opened or
before the conditionally rendered MenuList is recreated, so reopening starts
with a thumb position of 0 matching the list’s reset scrollTop. Update the state
flow around isOpen, thumbOffset, and the MenuList rendering without changing the
existing open/close behavior.

Comment on lines +25 to +33
<CustomDropDown
options={SORT_OPTIONS}
selected={value}
onSelect={(v) => {
onChange(v);
setIsOpen(false);
}}
open={isOpen}
onToggle={() => setIsOpen((prev) => !prev)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

선택 시 중복된 상태 변경을 제거하세요.

CustomDropDownonSelect 뒤에 onToggle(open)를 호출합니다. 라인 30의 setIsOpen(false) 뒤에 라인 33의 함수형 토글이 실행됩니다. React 배치 업데이트에서 메뉴 상태가 다시 열림 상태가 됩니다.

onSelect에서는 onChange(v)만 호출하세요. 메뉴 닫기는 CustomDropDown의 토글 처리로 맡기세요.

수정 예시
         options={SORT_OPTIONS}
         selected={value}
         onSelect={(v) => {
           onChange(v);
-          setIsOpen(false);
         }}
📝 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.

Suggested change
<CustomDropDown
options={SORT_OPTIONS}
selected={value}
onSelect={(v) => {
onChange(v);
setIsOpen(false);
}}
open={isOpen}
onToggle={() => setIsOpen((prev) => !prev)}
<CustomDropDown
options={SORT_OPTIONS}
selected={value}
onSelect={(v) => {
onChange(v);
}}
open={isOpen}
onToggle={() => setIsOpen((prev) => !prev)}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/components/mobile/SortDropdown/SortDropdown.tsx`
around lines 25 - 33, Remove the setIsOpen(false) call from the CustomDropDown
onSelect handler so it only invokes onChange(v); keep menu closing delegated to
CustomDropDown’s existing onToggle behavior.

reviewRequired: number;
scheduledInterview: number;
accepted: number;
declined: number;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# ApplicantsInfo 선언과 객체 생성 지점을 확인합니다.
rg -n -C 4 '\bApplicantsInfo\b|reviewRequired:|scheduledInterview:|declined:' frontend

# 지원자 집계 API와 mock 응답을 찾습니다.
rg -n -C 4 'applicants.*info|ApplicantsInfo|reviewRequired|scheduledInterview' frontend

Repository: Moadong/moadong

Length of output: 21094


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json, pathlib, re

print("Repository files:")
for f in json.loads(__import__("subprocess").check_output(["git","ls-files","frontend"], text=True).split(b"\n")[0:-1]):
    pass

types = pathlib.Path("frontend/src/types/applicants.ts")
use_sse = pathlib.Path("frontend/src/hooks/useApplicantSSE.ts")
api = pathlib.Path("frontend/src/apis/applicants.ts")
applicants_tab = pathlib.Path("frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx")

print("\napplicant type declaration:")
print(types.read_text())

print("\nuseApplicantSSE relevant section:")
txt = use_sse.read_text()
for m in re.finditer(r'applicantsData:|const useApplicantSSE|const eventSourceRef|setApplicantsData\((.*?)\);|setApplicantsData\((?:\([^)]*\)|[^)]){0,1200}\);\s*(?:const|});', txt, re.S):
    pass
lines = txt.splitlines()
for i, line in enumerate(lines, 1):
    if any(s in line for s in ["useState<ApplicantsInfo", "createApplicantSSE", "setApplicantsData", "reviewRequired", "scheduledInterview", "declined", "ApplicationStatus.DECLINED"]):
        lo=max(1,i-4); hi=min(len(lines),i+5)
        print(f"\n-- useApplicantSSE.ts:{lo}-{hi} --")
        for j in range(lo, hi+1):
            print(f"{j:4}: {lines[j-1]}")

print("\nApplicantsTab relevant API + StatusSummaryCard usages:")
lines = applicants_tab.read_text().splitlines()
for i, line in enumerate(lines, 1):
    if any(s in line for s in ["getClubApplicants", "<StatusSummaryCard", "declined", "applicantsData"]):
        lo=max(1,i-2); hi=min(len(lines),i+7)
        print(f"\n-- ApplicantsTab.tsx:{lo}-{hi} --")
        for j in range(lo, hi+1):
            print(f"{j:4}: {lines[j-1]}")

print("\nAPI implementation:")
print(api.read_text())

print("\nApplicantsInfo object literal constructor occurrences with declined (text):")
occurrences = []
for file in pathlib.Path("frontend").rglob("*"):
    if file.suffix not in (".ts", ".tsx"):
        continue
    text = file.read_text(errors="ignore")
    if re.search(r"\bdeclined\s*:", text):
        occurrences.append(file)
        print(f"\n-- {file} --")
        for lineno, line in enumerate(text.splitlines(), 1):
            if "declined:" in line:
                lo=max(1,lineno-8); hi=min(len(text.splitlines()),lineno+8)
                for k in range(lo, hi+1):
                    print(f"{k}: {text.splitlines()[k-1]}")

print("\nApplicantsInfo declarations/constructors without declined:")
def is_literal_like(text):
    return bool(re.search(r"\b(total|reviewRequired|scheduledInterview|accepted):", text))

for file in pathlib.Path("frontend").rglob("*"):
    if file.suffix not in (".ts", ".tsx"):
        continue
    text = file.read_text(errors="ignore")
    if "setApplicantsData" in text and not re.search(r"\bdeclined\s*:", text):
        print(f"\n-- {file} lacks declined assignment --")
        for lineno, line in enumerate(text.splitlines(), 1):
            if "setApplicantsData" in line or "ApplicantsInfo" in line:
                print(f"{lineno}: {line}")
PY

Repository: Moadong/moadong

Length of output: 286


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json, pathlib, re

files = json.loads(__import__("subprocess").check_output(["git","ls-files","frontend"], text=True))
print("Repository files count:", len(files))

types = pathlib.Path("frontend/src/types/applicants.ts")
use_sse = pathlib.Path("frontend/src/hooks/useApplicantSSE.ts")
api = pathlib.Path("frontend/src/apis/applicants.ts")
applicants_tab = pathlib.Path("frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx")

print("\napplicant type declaration:")
print(types.read_text())

print("\nuseApplicantSSE relevant occurrences:")
txt = use_sse.read_text()
for i, line in enumerate(txt.splitlines(), 1):
    if any(s in line for s in ["useState<ApplicantsInfo", "createApplicantSSE", "setApplicantsData", "reviewRequired", "scheduledInterview", "declined", "ApplicationStatus.DECLINED"]):
        lo=max(1,i-4); hi=min(len(txt.splitlines()),i+5)
        print(f"\n-- useApplicantSSE.ts:{lo}-{hi} --")
        for j in range(lo, hi+1):
            print(f"{j:4}: {txt.splitlines()[j-1]}")

print("\nApplicantsTab relevant API + StatusSummaryCard usages:")
lines = applicants_tab.read_text().splitlines()
for i, line in enumerate(lines, 1):
    if any(s in line for s in ["getClubApplicants", "<StatusSummaryCard", "declined", "applicantsData"]):
        lo=max(1,i-2); hi=min(len(lines),i+7)
        print(f"\n-- ApplicantsTab.tsx:{lo}-{hi} --")
        for j in range(lo, hi+1):
            print(f"{j:4}: {lines[j-1]}")

print("\nAPI implementation:")
print(api.read_text())

print("\nApplicantsInfo object literal constructor occurrences with declined:")
occurrences = []
for file in pathlib.Path("frontend").rglob("*"):
    if file.suffix not in (".ts", ".tsx"):
        continue
    text = file.read_text(errors="ignore")
    if re.search(r"\bdeclined\s*:", text):
        occurrences.append(file)
        print(f"\n-- {file} --")
        for lineno, line in enumerate(text.splitlines(), 1):
            if "declined:" in line:
                lo=max(1,lineno-8); hi=min(len(text.splitlines()),lineno+8)
                for k in range(lo, hi+1):
                    print(f"{k}: {text.splitlines()[k-1]}")
if not occurrences:
    print("\nNo file contains a 'declined:' assignment in frontend/src.")

print("\nApplicantsInfo setApplicantsData assignments without declined:")
for file in pathlib.Path("frontend").rglob("*"):
    if file.suffix not in (".ts", ".tsx"):
        continue
    text = file.read_text(errors="ignore")
    if "setApplicantsData" in text and not re.search(r"\bdeclined\s*:", text):
        print(f"\n-- {file} lacks declined assignment --")
        for lineno, line in enumerate(text.splitlines(), 1):
            if "setApplicantsData" in line or "ApplicantsInfo" in line:
                print(f"{lineno}: {line}")
PY

Repository: Moadong/moadong

Length of output: 635


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmp=$(mktemp)

printf '\napplicant type declaration:\n'
cat -n frontend/src/types/applicants.ts

printf '\ncreateApplicantSSE and applicantsData assignments:\n'
rg -n -C 8 'createApplicantSSE|useState<ApplicantsInfo|setApplicantsData|reviewRequired|scheduledInterview|declined|ApplicationStatus.DECLINED' frontend/src/hooks frontend/src/apis frontend/src/pages/AdminPage || true

printf '\nApplicantsInfo object literal occurrences:\n'
rg -n -C 6 'declined\s*:' frontend/src || true

printf '\nApplicantsTab getClubApplicants and StatusSummaryCard usage:\n'
rg -n -C 6 'getClubApplicants|<StatusSummaryCard|applicantsData\?\.declined|declined' frontend/src/pages/AdminPage/tabs/ApplicantsTab || true

printf '\nAPI implementation:\n'
cat -n frontend/src/apis/applicants.ts

Repository: Moadong/moadong

Length of output: 42431


declinedApplicantsInfo 생성 경로에 추가하세요.

getClubApplicants/api/club/apply/info/...에서 ApplicantsInfo를 받아오고, useApplicantSSEprevData 기반 인라인 객체를 만들지만 declined는 현재 0이면 항상 0으로 표시됩니다. ApplicationStatus.DECLINED로 상태가 변경될 때 집계 값을 갱신하도록 조정해야 실제 불합격 지원자 수가 반영됩니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/types/applicants.ts` at line 16, ApplicantsInfo 생성 경로에서 declined
집계를 실제 값으로 유지하도록 수정하세요. getClubApplicants의 API 응답 매핑과 useApplicantSSE의 prevData
기반 인라인 객체에 declined를 포함하고, 지원자 상태가 ApplicationStatus.DECLINED로 변경될 때 해당 집계를 갱신해
초기값 0에 고정되지 않도록 하세요.

@suhyun113 suhyun113 changed the title [feature] 관리자는 모바일에서 지원자 현황을 조회하고 지원자 상태를 관리할 수 있다 [feature] 관리자 모바일 지원자 현황 리스트 컴포넌트 추가 Aug 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FE Frontend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant