Skip to content

[feature] 지원자를 삭제할 수 있다.#698

Merged
lepitaaar merged 18 commits intodevelop-fefrom
feature/#694-delete-application-MOA-194
Aug 25, 2025
Merged

[feature] 지원자를 삭제할 수 있다.#698
lepitaaar merged 18 commits intodevelop-fefrom
feature/#694-delete-application-MOA-194

Conversation

@lepitaaar
Copy link
Contributor

@lepitaaar lepitaaar commented Aug 24, 2025

#️⃣연관된 이슈

#694

📝작업 내용

  • 삭제 API 연동 1a84b37
  • 디자인 반영

기존 디자인

스크린샷 2025-08-24 20 35 10

변경 디자인

스크린샷 2025-08-24 20 34 38 스크린샷 2025-08-24 20 35 33 스크린샷 2025-08-24 20 35 37 스크린샷 2025-08-24 20 35 47

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

리뷰어가 특별히 봐주었으면 하는 부분이 있다면 작성해주세요

ex) 메서드 XXX의 이름을 더 잘 짓고 싶은데 혹시 좋은 명칭이 있을까요?

논의하고 싶은 부분(선택)

논의하고 싶은 부분이 있다면 작성해주세요.

🫡 참고사항

Summary by CodeRabbit

  • New Features

    • 지원자 다중 선택 및 일괄 작업 메뉴 추가(전체 선택/해제, 상태별 선택, 메뉴 열기/닫기).
    • 선택 항목 일괄 삭제 기능 도입(삭제 확인 창, 성공/오류 알림, 목록 갱신).
    • 선택된 항목이 있을 때만 상태 변경 드롭다운 활성화.
  • Style

    • 체크박스·아이콘(삭제/선택)·구분선 등 시각적 개선 및 hover/disabled 상태 반영.
    • 헤더 레이아웃 조정으로 선택·삭제·상태 변경 도구 통합 배치.

@lepitaaar lepitaaar self-assigned this Aug 24, 2025
@lepitaaar lepitaaar added ✨ Feature 기능 개발 📬 API 서버 API 통신 작업 💻 FE Frontend labels Aug 24, 2025
@vercel
Copy link

vercel bot commented Aug 24, 2025

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

Project Deployment Preview Comments Updated (UTC)
moadong Ready Ready Preview Comment Aug 25, 2025 3:41am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 24, 2025

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

프론트엔드에 지원자 일괄 삭제 기능이 추가되었습니다. DELETE API 모듈과 React Query 뮤테이션 훅이 도입되었고, ApplicantsTab에 다중 선택/전체 선택/상태별 선택 UI 및 삭제 로직과 스타일 컴포넌트(아이콘 hover/disabled 포함)가 추가되었습니다.

Changes

Cohort / File(s) Summary
API: Applicants delete
frontend/src/apis/applicants/deleteApplicants.ts
신규 API 모듈 추가. DELETE /api/club/{clubId}/applicant{ applicantIds } JSON 전송, 응답 에러 메시지 파싱 후 throw, 성공 시 result.data 반환.
React Query mutation hook
frontend/src/hooks/queries/applicants/useDeleteApplicants.ts
클럽 ID 기반 삭제 뮤테이션 훅 추가. mutationFn으로 deleteApplicants 호출, 성공 시 ['clubApplicants'] 쿼리 무효화, 에러 로깅.
ApplicantsTab UI/logic
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
다중 선택 상태(Map), 전체선택/드롭다운 상태 도입, 상태별/전체 선택 토글 함수, 선택 항목 삭제 확인 후 useDeleteApplicants로 일괄 삭제 처리 및 성공/오류 처리, 행별 체크박스와 헤더의 전체선택/상태선택/삭제 버튼 UI 통합.
Styles for ApplicantsTab
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts
다양한 선택/삭제 관련 스타일 컴포넌트 추가 및 수정(VerticalLine, StatusSelect, DeleteButton, ApplicantAllSelect*, Arrow props 확장 등), 체크박스/드롭다운/아이콘 hover·disabled 상태 정의.

Sequence Diagram(s)

sequenceDiagram
    actor User as 사용자
    participant UI as ApplicantsTab (UI)
    participant Hook as useDeleteApplicants (React Query)
    participant API as deleteApplicants (API)
    participant BE as Backend

    User->>UI: 다중 선택 / 상태 선택
    User->>UI: 삭제 버튼 클릭
    UI->>User: 삭제 확인 다이얼로그
    alt 삭제 확인
        UI->>Hook: mutate({ applicantIds })
        Hook->>API: DELETE /api/club/{clubId}/applicant { applicantIds }
        API->>BE: HTTP DELETE 요청
        BE-->>API: 200 JSON { data }
        API-->>Hook: result.data
        Hook-->>UI: onSuccess
        UI->>UI: 쿼리 무효화(['clubApplicants']) 및 선택 초기화
        UI-->>User: 성공 알림
    else 취소/오류
        Hook-->>UI: onError
        UI-->>User: 오류 알림
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
동아리 지원자 삭제 기능 구현 (MOA-194)

Assessment against linked issues: Out-of-scope changes

(해당 없음)

Possibly related issues

Possibly related PRs

Suggested labels

🎨 Design

Suggested reviewers

  • seongwon030
  • 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 9ab38fc and 72c6c42.

📒 Files selected for processing (1)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.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/#694-delete-application-MOA-194

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
🪧 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 @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in 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 ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere 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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)

316-327: 날짜 포맷팅 로직을 유틸 함수로 분리

IIFE를 사용한 날짜 포맷팅이 컴포넌트 내부에 있어 가독성이 떨어집니다. date-fns 라이브러리가 이미 설치되어 있으므로 이를 활용하거나 유틸 함수로 분리하는 것이 좋습니다.

date-fns를 사용한 개선:

import { format } from 'date-fns';

// 사용처
<Styled.ApplicantTableCol>
  {format(new Date(item.createdAt), 'yyyy-MM-dd')}
</Styled.ApplicantTableCol>

또는 유틸 함수로 분리:

// utils/dateFormat.ts
export const formatDate = (dateString: string): string => {
  const date = new Date(dateString);
  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}`;
};
🧹 Nitpick comments (4)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts (1)

89-97: Arrow 컴포넌트의 props 타입 개선 필요

Arrow 컴포넌트가 선택적 width/height props를 받도록 수정되었으나, 기본값 설정이 비효율적입니다. styled-components의 기본 props 패턴을 사용하면 더 깔끔합니다.

-export const Arrow = styled.img<{ width?: number; height?: number }>`
+export const Arrow = styled.img<{ $width?: number; $height?: number }>`
   position: absolute;
   right: 12px;
   top: 50%;
   transform: translateY(-50%);
-  width: ${({ width }) => (width ? `${width}px` : '12px')};
-  height: ${({ height }) => (height ? `${height}px` : '12px')};
+  width: ${({ $width = 12 }) => `${$width}px`};
+  height: ${({ $height = 12 }) => `${$height}px`};
   pointer-events: none;
 `;

그리고 사용하는 곳도 수정이 필요합니다:

-<Styled.Arrow width={8} height={8} src={selectIcon} />
+<Styled.Arrow $width={8} $height={8} src={selectIcon} />
frontend/src/apis/applicants/deleteApplicants.ts (1)

11-11: 일관된 JSON 구조 사용

body 직렬화 시 속성명 스타일이 일관되지 않습니다.

-        body: JSON.stringify({applicantIds: applicantIds}),
+        body: JSON.stringify({ applicantIds }),
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (2)

54-56: CONFIRM_MESSAGE 상수로 분리하여 가독성 개선

삭제 확인 메시지가 하드코딩되어 있습니다. 상수로 분리하면 유지보수성이 향상됩니다.

삭제 확인 메시지를 상수로 분리:

const DELETE_CONFIRM_MESSAGE = (count: number) => 
  `${count}개의 지원자를 정말로 삭제하시겠습니까?\n삭제된 지원자는 복구할 수 없습니다.`;

그리고 사용처 수정:

-    const check = confirm(
-      `${ids.length}개의 지원자를 정말로 삭제하시겠습니까?\n삭제된 지원자는 복구할 수 없습니다.`,
-    );
+    const check = confirm(DELETE_CONFIRM_MESSAGE(ids.length));

172-172: StatusSelect disabled 상태 로직 개선

현재 선택된 항목이 있을 때 활성화되는 로직이 매번 Array.from과 some을 호출하여 비효율적입니다. useMemo를 사용하여 최적화할 수 있습니다.

컴포넌트 상단에 추가:

const hasSelectedItems = useMemo(
  () => Array.from(checkedItem.values()).some(Boolean),
  [checkedItem]
);

그리고 사용처 수정:

-              disabled={!Array.from(checkedItem.values()).some(Boolean)}
+              disabled={!hasSelectedItems}
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between 9f8a7a4 and a0729cc.

⛔ Files ignored due to path filters (5)
  • frontend/package-lock.json is excluded by !**/package-lock.json
  • frontend/src/assets/images/icons/applicant_delete.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/applicant_delete_disabled.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/applicant_delete_hover.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/applicant_select_arrow.svg is excluded by !**/*.svg
📒 Files selected for processing (5)
  • frontend/package.json (1 hunks)
  • frontend/src/apis/applicants/deleteApplicants.ts (1 hunks)
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts (1 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts (4 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (5 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/hooks/queries/applicants/useDeleteApplicants.ts
  • frontend/src/apis/applicants/deleteApplicants.ts
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts
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 (2)
frontend/src/apis/applicants/deleteApplicants.ts (1)
frontend/src/apis/auth/secureFetch.ts (1)
  • secureFetch (3-41)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (3)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (30-37)
frontend/src/hooks/queries/applicants/useDeleteApplicants.ts (1)
  • useDeleteApplicants (4-17)
frontend/src/types/applicants.ts (1)
  • Applicant (18-24)
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx

[error] 37-37: 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)


[error] 45-45: 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 (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)

17-23: React Hooks 규칙 위반 - useDeleteApplicants를 조건문 이전에 호출해야 함

React Hook인 useDeleteApplicants가 조건부 반환문(if (!clubId) return null;) 이후에 호출되고 있습니다. 모든 Hook은 컴포넌트의 최상위 레벨에서 무조건 호출되어야 합니다.

 const ApplicantsTab = () => {
   const navigate = useNavigate();
   const { clubId, applicantsData } = useAdminClubContext();
+  const { mutate: deleteApplicants } = useDeleteApplicants(clubId || '');
   const [keyword, setKeyword] = useState('');
   const [checkedItem, setCheckedItem] = useState<Map<string, boolean>>(
     new Map(),
   );
   const [selectAll, setSelectAll] = useState(false);
   const [open, setOpen] = useState(false);
-  const { mutate: deleteApplicants } = useDeleteApplicants(clubId!);
   if (!clubId) return null;

Likely an incorrect or invalid review comment.

Copy link
Member

@Zepelown Zepelown left a comment

Choose a reason for hiding this comment

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

고생하셨습니다~

Copy link
Collaborator

@suhyun113 suhyun113 left a comment

Choose a reason for hiding this comment

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

수고하셨습니다.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)

319-330: 날짜 형식화 로직을 유틸리티 함수로 분리

IIFE로 작성된 날짜 형식화 로직이 복잡합니다. 코딩 가이드라인에 따라 복잡한 로직을 별도 함수로 분리해주세요.

// utils/dateFormatter.ts
export const formatDateToYYYYMMDD = (dateString: string): string => {
  const date = new Date(dateString);
  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}`;
};

그리고 컴포넌트에서:

- {
-   // 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}`;
-   })()
- }
+ {formatDateToYYYYMMDD(item.createdAt)}
♻️ Duplicate comments (2)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (2)

104-112: 중복 코메트 - 비효율적인 Map 업데이트

이전 리뷰에서 지적된 바와 동일하게, Map을 변경한 후 새로운 Map을 생성하는 방식이 비효율적입니다. 함수형 방식으로 개선이 필요합니다.

 const checkoutAllApplicants = () => {
-  setCheckedItem((prev) => {
-    const newMap = new Map(prev);
-    newMap.forEach((_, key) => {
-      newMap.set(key, false);
-    });
-    return newMap;
-  });
+  setCheckedItem(prev => 
+    new Map(Array.from(prev.keys()).map(key => [key, false]))
+  );
 };

172-192: 중복 코메트 - DeleteButton disabled 로직 확인 필요

이전 리뷰에서 지적된 disabled 로직이 수정되었는지 확인이 필요합니다. 현재 로직은 올바르게 구현되어 있습니다 (선택된 항목이 없을 때 비활성화).

🧹 Nitpick comments (2)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (2)

54-56: 임시 confirm 대화상자

기존 코메트에서 언급된 바와 같이, 브라우저 기본 confirm을 사용하고 있습니다. 사용자 경험 개선을 위해 커스텀 모달로 교체를 고려해주세요.


73-102: 복잡한 선택 로직을 별도 컴포넌트나 훅으로 분리

selectApplicantsByStatus 함수가 복잡한 상태 관리 로직을 포함하고 있어 단일 책임 원칙에 어긋납니다. 선택 관련 로직을 커스텀 훅으로 분리하는 것을 고려해보세요.

// hooks/useApplicantSelection.ts
const useApplicantSelection = (filteredApplicants: Applicant[]) => {
  const [checkedItem, setCheckedItem] = useState<Map<string, boolean>>(new Map());
  const [selectAll, setSelectAll] = useState(false);

  // ... 선택 관련 로직들
  
  return {
    checkedItem,
    selectAll,
    selectApplicantsByStatus,
    checkoutAllApplicants,
    // ... 다른 함수들
  };
};
📜 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.

📥 Commits

Reviewing files that changed from the base of the PR and between a0729cc and 9ab38fc.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts (1 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts (4 hunks)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts
  • frontend/src/hooks/queries/applicants/useDeleteApplicants.ts
🧰 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 (3)
frontend/src/context/AdminClubContext.tsx (1)
  • useAdminClubContext (30-37)
frontend/src/hooks/queries/applicants/useDeleteApplicants.ts (1)
  • useDeleteApplicants (4-17)
frontend/src/types/applicants.ts (1)
  • Applicant (18-24)
🪛 Biome (2.1.2)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx

[error] 37-37: 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)


[error] 45-45: 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 (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx (1)

22-24: 훅 호출 순서 위반

useDeleteApplicants 훅이 조건부 반환문(if (!clubId) return null) 이후에 호출되고 있어 React의 훅 호출 규칙을 위반합니다. 모든 훅은 컴포넌트의 최상위에서 조건 없이 호출되어야 합니다.

 const ApplicantsTab = () => {
   const navigate = useNavigate();
   const { clubId, applicantsData } = useAdminClubContext();
+  const { mutate: deleteApplicants } = useDeleteApplicants(clubId!);
   const [keyword, setKeyword] = useState('');
   const [checkedItem, setCheckedItem] = useState<Map<string, boolean>>(
     new Map(),
   );
   const [selectAll, setSelectAll] = useState(false);
   const [open, setOpen] = useState(false);
-  const { mutate: deleteApplicants } = useDeleteApplicants(clubId!);
   if (!clubId) return null;

Likely an incorrect or invalid review comment.

Comment on lines +206 to +262
<Styled.ApplicantTableHeader width={55}>
<Styled.ApplicantAllSelectWrapper>
<Styled.ApplicantTableAllSelectCheckbox
checked={selectAll}
onClick={(e: React.MouseEvent<HTMLInputElement>) => {
e.stopPropagation();
selectApplicantsByStatus('all');
}}
/>
<Styled.ApplicantAllSelectArrow
src={selectAllIcon}
alt='전체선택'
onClick={() => setOpen((prev) => !prev)}
/>
<Styled.ApplicantAllSelectMenu open={open}>
<Styled.ApplicantAllSelectMenuItem
onClick={() => {
if (selectAll) {
setOpen(false);
return;
}
selectApplicantsByStatus('all');
}}
>
전체선택
</Styled.ApplicantAllSelectMenuItem>
<Styled.ApplicantAllSelectMenuItem
onClick={() => {
selectApplicantsByStatus(
'filter',
ApplicationStatus.SUBMITTED,
);
}}
>
서류 검토 필요
</Styled.ApplicantAllSelectMenuItem>
<Styled.ApplicantAllSelectMenuItem
onClick={() => {
selectApplicantsByStatus(
'filter',
ApplicationStatus.INTERVIEW_SCHEDULED,
);
}}
>
면접예정
</Styled.ApplicantAllSelectMenuItem>
<Styled.ApplicantAllSelectMenuItem
onClick={() => {
setOpen(false);
checkoutAllApplicants();
}}
>
선택해제
</Styled.ApplicantAllSelectMenuItem>
</Styled.ApplicantAllSelectMenu>
</Styled.ApplicantAllSelectWrapper>
</Styled.ApplicantTableHeader>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

복잡한 UI 로직을 별도 컴포넌트로 분리

전체 선택 관련 UI 로직이 매우 복잡합니다. ApplicantAllSelector와 같은 별도 컴포넌트로 분리하여 가독성과 재사용성을 높이는 것을 고려해보세요.

// components/ApplicantAllSelector.tsx
interface ApplicantAllSelectorProps {
  selectAll: boolean;
  open: boolean;
  onToggleOpen: () => void;
  onSelectAll: () => void;
  onSelectByStatus: (status: ApplicationStatus) => void;
  onClearAll: () => void;
}

const ApplicantAllSelector = ({ /* props */ }: ApplicantAllSelectorProps) => {
  // 현재 라인 207-261의 로직을 이곳으로 이동
};
🤖 Prompt for AI Agents
In frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.tsx around
lines 206-262, the "전체 선택" UI and menu logic is too large and should be
extracted into a dedicated ApplicantAllSelector component; create
components/ApplicantAllSelector.tsx that accepts controlled props (selectAll:
boolean, open: boolean, onToggleOpen: () => void, onSelectAll: () => void,
onSelectByStatus: (status: ApplicationStatus) => void, onClearAll: () => void)
and move the JSX and local event handlers there (preserve e.stopPropagation on
the checkbox click and all menu item behaviors), ensure styling imports are
preserved or re-exported, update the parent file to render <ApplicantAllSelector
.../> passing state and callbacks (or lift open/selectAll control into parent if
currently local) and remove the original block so behavior and props remain
identical.

Copy link
Member

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

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

삭제 기능 구현하느라 고생하셨습니다~

@lepitaaar lepitaaar merged commit a58ca36 into develop-fe Aug 25, 2025
5 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Sep 11, 2025
@lepitaaar lepitaaar deleted the feature/#694-delete-application-MOA-194 branch September 20, 2025 01:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📬 API 서버 API 통신 작업 💻 FE Frontend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants