Skip to content

[feature] 지원서 관리 메인 페이지 및 년도별 서브페이지 구현 - #1881

Open
suhyun113 wants to merge 13 commits into
feature/#1850-application-mobile-common-components-MOA-1049from
feature/#1874-application-management-main-page-MOA-1053
Open

[feature] 지원서 관리 메인 페이지 및 년도별 서브페이지 구현#1881
suhyun113 wants to merge 13 commits into
feature/#1850-application-mobile-common-components-MOA-1049from
feature/#1874-application-management-main-page-MOA-1053

Conversation

@suhyun113

@suhyun113 suhyun113 commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • 지원서 관리 탭에 모바일 UI 구현 (ApplicationListTabMobile)
  • 상단: 게시된 지원서 섹션 (없을 경우 빈 상태 표시)
  • 하단: 연도별 그룹 카드 목록 (최신순 / 오래된순 정렬 토글)
  • 연도 카드 클릭 시 해당 연도 지원서 상세 페이지로 이동 (내부 상태 전환, 라우트 추가 없음)
  • 연도 상세 페이지: 최근 생성순 / 최근 수정순 정렬 토글, 정렬 기준에 따라 날짜 표시 분기
  • MobileFloatingButton (새 지원서 만들기) 공통 컴포넌트 재사용
  • ApplicationFormItem 타입에 createdAt 필드 추가 (백엔드 연동 필요 — 아래 참고)
  • 스크롤 상단 버튼 진입 시 자동 비활성화 (SCROLL_TRIGGER_DISABLED 패턴)
  • ApplicationMenu 복제하기 기능 모바일 카드에 연결

⚠️ 백엔드 연동 필요

연도 상세 페이지의 최근 생성순 정렬 및 날짜 표시 기능은 createdAt 필드를 필요로 합니다.

현재 /api/club/application 응답의 forms[].forms[] 각 항목에 createdAt이 포함되지 않아 해당 기능은 아직 동작하지 않습니다. 백엔드에서 createdAt 필드를 추가하면 프론트엔드 변경 없이 즉시 동작합니다.

Screenshots

게시된 지원서 없을 때 최신순 오래된 순 연도 상세 페이지
image image image image

Test plan

  • 게시된 지원서 없을 때 빈 상태 UI 확인
  • 게시된 지원서 있을 때 카드 표시 확인
  • 최신순 / 오래된순 정렬 토글 동작 확인
  • 연도 카드 클릭 시 상세 페이지 이동 확인
  • 상세 페이지 뒤로가기 시 스크롤 위치 복원 확인
  • 상세 페이지 최근 생성순 / 최근 수정순 토글 확인 (백엔드 연동 후)
  • FAB 버튼 클릭 시 새 지원서 생성 페이지 이동 확인
  • 스크롤 상단 버튼 비노출 확인
  • 카드 더보기 메뉴 (수정 / 복제 / 삭제 / 상태 변경) 동작 확인

@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 Jul 27, 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, Comment Jul 27, 2026 4:52pm

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

지원서 관리 화면을 데스크톱·모바일 환경으로 분리하고, 활성 지원서 고정 및 연도별 그룹화를 적용했습니다. 모바일 카드·메뉴·연도 상세 화면과 지원서 생성·수정 화면, 공통 테마와 플로팅 버튼도 추가했습니다.

Changes

지원서 목록 관리

Layer / File(s) Summary
데스크톱 목록 그룹화와 라우팅
frontend/src/pages/AdminPage/components/ApplicationFormList/*, frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/*, frontend/src/pages/AdminPage/components/ApplicationRow/*
활성 지원서를 상단에 고정하고 전체 지원서를 연도별로 표시하며, 조회와 편집 라우팅을 분리했습니다.
모바일 목록과 카드 흐름
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/*
모바일·태블릿 목록, 연도 상세 화면, 정렬, 외부 클릭 메뉴 닫기, CRUD 액션을 구현했습니다.
공통 메뉴와 플로팅 버튼
frontend/src/pages/AdminPage/components/ApplicationMenu/*, frontend/src/pages/AdminPage/components/MobileFloatingButton/*
지원서 상태 토글·편집·복제·삭제 메뉴와 안전 영역을 지원하는 고정 플로팅 버튼을 추가했습니다.
지원서 편집 화면
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/*
내부 질문형·외부 링크형 지원서의 생성·수정, 검증, 저장 및 유형 전환을 구현했습니다.
테마 및 공통 지원 변경
frontend/src/styles/theme/*, frontend/src/types/application.ts, frontend/src/utils/formatKSTDateTime.ts, frontend/docs/features/admin/application/*
타이포그래피·날짜 포맷·지원서 데이터 타입을 확장하고 관련 문서와 화면 스타일을 갱신했습니다.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related issues

Possibly related PRs

  • Moadong/moadong#772 — 동일한 지원서 목록과 공통 ApplicationMenu 흐름의 재구성과 직접 연결됩니다.
  • Moadong/moadong#1811semesterYear와 활성 상태를 기준으로 한 그룹 모델 변경과 관련됩니다.
  • Moadong/moadong#1869 — 공통 메뉴, 모바일 목록, onNavigate, 날짜 포맷과 문서 변경이 동일 코드 영역에 걸쳐 있습니다.

Suggested labels: 🎨 Design

Suggested reviewers: oesnuj, lepitaaar, 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 지원서 관리 메인 페이지와 년도별 서브페이지 구현이라는 제목이 모바일 지원서 관리 UI와 연도별 상세 화면 추가라는 변경사항을 잘 요약합니다.
✨ 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/#1874-application-management-main-page-MOA-1053

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 added ✨ Feature 기능 개발 💻 FE Frontend labels Jul 27, 2026
@suhyun113
suhyun113 changed the base branch from main to feature/#1850-application-mobile-common-components-MOA-1049 July 27, 2026 16:57
@suhyun113 suhyun113 changed the title feat(admin): 지원서 관리 모바일 페이지 구현 [feature] 지원서 관리 메인 페이지 및 년도별 서브페이지 구현 Jul 27, 2026

@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: 14

🧹 Nitpick comments (8)
frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsx (1)

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

아이콘 import 바인딩을 camelCase로 변경해 주세요.

Morebutton은 로컬 값 바인딩이므로 moreButtonIcon처럼 camelCase로 이름을 바꾸고 사용처도 함께 수정해 주세요.

As per coding guidelines: frontend/**/*.{ts,tsx}에서는 변수와 함수 이름에 camelCase를 사용합니다.

🤖 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/components/ApplicationRow/ApplicationRowItem.tsx`
at line 2, Rename the imported SVG binding Morebutton to a camelCase name such
as moreButtonIcon, and update every usage of that binding in the
ApplicationRowItem component while leaving the icon source unchanged.

Source: Coding guidelines

frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx (1)

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

React 이벤트 타입 import를 명시적으로 통일해 주세요.

기본 React import를 제거했지만 Line 132에서 여전히 React.MouseEvent를 사용합니다. ApplicationRowItem.tsx처럼 import type { MouseEvent } from 'react'e: MouseEvent를 사용하거나 React namespace import를 유지해 타입 해석이 전역 typings/tsconfig 설정에 의존하지 않도록 해 주세요.

🤖 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/components/ApplicationFormList/ApplicationFormList.tsx`
at line 1, Update the event type usage in ApplicationFormList to match the
existing React import style: replace the React.MouseEvent reference with an
explicit type-only MouseEvent import from react and use MouseEvent for the
handler parameter, or consistently restore the React namespace import. Ensure
type resolution does not depend on global typings or tsconfig settings.
frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.tsx (1)

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

ariaLabel을 필수 prop으로 변경 권장.

아이콘이 alt=''(장식용)이라 ariaLabel이 버튼의 유일한 접근 가능한 이름입니다. 현재 호출부는 모두 값을 전달하지만, optional로 두면 향후 호출부에서 누락 시 스크린리더에 이름 없는 버튼이 될 수 있습니다.

♻️ 필수 prop으로 변경
 interface MobileFloatingButtonProps {
   onClick: () => void;
   icon: string;
-  ariaLabel?: string;
+  ariaLabel: string;
   bottom?: string;
 }
🤖 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/components/MobileFloatingButton/MobileFloatingButton.tsx`
around lines 3 - 8, Update the MobileFloatingButtonProps interface to make
ariaLabel required instead of optional, ensuring every MobileFloatingButton
usage must provide an accessible button name while leaving the other props
unchanged.
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx (2)

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

Storybook 레이아웃도 styled-components와 테마를 사용하세요. 인라인 style은 저장소 스타일링 규칙을 우회합니다.

  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx#L25-L31: 폭 335px 래퍼를 styled-component로 추출하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx#L22-L28: 폭 335px 래퍼를 styled-component로 추출하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx#L22-L28: 폭 335px 래퍼를 styled-component로 추출하세요.
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx#L10-L16: 메뉴 캔버스의 위치·크기 스타일을 styled-component로 추출하세요.
🤖 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/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx`
around lines 25 - 31, Replace the inline Storybook layout styles with themed
styled-components: in
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx:25-31,
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx:22-28,
and
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx:22-28,
extract the 335px wrapper into a styled component and use it in each decorator.
In
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx:10-16,
extract the menu canvas position and dimensions into a styled component and use
that wrapper instead of inline styles.

Source: Coding guidelines


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

타입 import를 내부 모듈 뒤로 이동하세요. 저장소 규칙의 순서(외부 라이브러리 → 내부 모듈 → 타입 → 스타일)와 다릅니다.

  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx#L1-L9: ApplicationActiveSectionMobile import 뒤에 타입 import를 배치하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.tsx#L1-L7: ApplicationCardMobile import 뒤에 타입 import를 배치하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx#L1-L6: ApplicationCardMobile import 뒤에 타입 import를 배치하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.tsx#L1-L9: 모든 내부 값 import를 타입 import보다 앞에 배치하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx#L1-L6: ApplicationListCardMobile import 뒤에 타입 import를 배치하세요.
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx#L1-L3: ApplicationMenu import 뒤에 Storybook 타입 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/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx`
around lines 1 - 9, Reorder imports to follow the repository convention of
external libraries, internal value modules, type imports, then styles. In
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx:1-9,
place type imports after ApplicationActiveSectionMobile; in
ApplicationActiveSectionMobile.tsx:1-7, place them after ApplicationCardMobile;
in ApplicationCardMobile.stories.tsx:1-6, after ApplicationCardMobile; in
ApplicationCardMobile.tsx:1-9, move all internal value imports before type
imports; in ApplicationListCardMobile.stories.tsx:1-6, after
ApplicationListCardMobile; and in
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx:1-3,
place Storybook type imports after ApplicationMenu.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx (1)

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

import 순서가 가이드라인과 다릅니다.

@/styles/PageContainer.styles(17번째 줄)가 타입 import(@/types/application, 18-23번째 줄)보다 먼저 위치해 있습니다. "external libraries → internal modules → types → styles" 순서를 지켜야 합니다. 또한 24-25번째 줄에서 동일 파일(./ApplicationEditTab.styles)을 두 번에 나눠 import하고 있어 하나로 합칠 수 있습니다.

As per coding guidelines, "Order imports as external libraries, internal modules, types, then styles."

♻️ 제안 수정
-import { useAdminClubId } from '`@/store/useAdminClubStore`';
-import { PageContainer } from '`@/styles/PageContainer.styles`';
-import {
+import { useAdminClubId } from '`@/store/useAdminClubStore`';
+import {
   ApplicationFormData,
   ApplicationFormMode,
   Question,
   QuestionType,
 } from '`@/types/application`';
-import * as Styled from './ApplicationEditTab.styles';
-import { QuestionDivider } from './ApplicationEditTab.styles';
+import { PageContainer } from '`@/styles/PageContainer.styles`';
+import * as Styled, { QuestionDivider } from './ApplicationEditTab.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/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx`
around lines 1 - 26, Reorder imports in ApplicationEditTab.tsx to follow
external libraries, internal modules, types, then styles, moving the
ApplicationFormData-related type import before PageContainer styles. Merge the
two imports from ./ApplicationEditTab.styles into a single import.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.styles.ts (1)

29-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

폰트 스타일이 테마 타이포그래피 대신 하드코딩되어 있습니다.

colors 테마는 잘 사용하고 있지만, font-family/font-weight/font-size/line-height/letter-spacing은 값이 직접 하드코딩되어 있습니다. 이번 PR 계열에서 frontend/src/styles/theme/typography.ts가 공통 타이포그래피 목적으로 확장되었으므로, 가능하다면 해당 토큰을 재사용하는 것이 일관성 측면에서 좋습니다.

typography.ts에 이 스타일(14px/600/140%/-0.02em)에 대응하는 토큰이 있는지 확인해 주세요.

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/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.styles.ts`
around lines 29 - 34, Update the typography styling in the relevant styled
component of ApplicationTypeTab to reuse the matching token from the theme
typography definitions for 14px, weight 600, 140% line height, and -0.02em
letter spacing, including the font family where supported. Remove the
corresponding hardcoded typography values while preserving the existing
colors.gray[700] color.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.styles.ts (1)

58-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

제출 버튼 타이포그래피도 토큰으로 통일하세요.

Line 62-63의 하드코딩된 글꼴 크기·굵기를 typography.paragraph.p1 등 대응 토큰으로 교체하세요.

수정 예시
-  font-size: 1.25rem;
-  font-weight: 600;
+  ${setTypography(typography.paragraph.p1)}

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/ApplicationTab/ApplicationEditTab/ApplicationEditTab.styles.ts`
around lines 58 - 69, Update the submit button styles in the relevant styled
component to replace the hardcoded font-size and font-weight with the
corresponding project typography token, such as typography.paragraph.p1, while
preserving the existing button appearance and other styles.

Source: Coding guidelines

🤖 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/docs/features/admin/application/mobile-components.md`:
- Around line 51-63: 문서의 컴포넌트명을 ApplicationFAB에서 실제 공통 컴포넌트인
MobileFloatingButton으로 변경하고, 관련 코드 목록에
src/pages/AdminPage/components/MobileFloatingButton/ 경로를 추가하세요.

In
`@frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx`:
- Around line 228-231: 지원서 상태 용어를 “활성화된 지원서”와 “활성화/비활성화” 기준으로 통일하세요.
frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx의
빈 상태 문구와 Line 193 섹션 제목을 동일한 용어로 맞추고,
frontend/docs/features/admin/application/desktop.md의 7-8행 “게시된 지원서” 표현과 34-37행
상태 규칙도 UI와 일관되게 수정하세요.

In
`@frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.styles.ts`:
- Around line 23-44: Change MenuItem from a styled div to a styled button so
ApplicationMenu click actions are keyboard accessible and expose button
semantics. Remove the button’s default border, and add a visible :focus-visible
style while preserving the existing layout, colors, hover behavior, and
ToggleMenuItem styling.

In `@frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.tsx`:
- Around line 30-53: Update Styled.MenuItem and Styled.ToggleMenuItem usages in
ApplicationMenu so the interactive entries use native button semantics,
preferably by configuring their styled components to render as button elements
while preserving existing click handlers and styling. Ensure all menu actions
remain keyboard-focusable and activate correctly with Enter/Space.
- Line 3: Rename the imported binding Delete_applicant to the camelCase name
deleteApplicantIcon and update its usage in Styled.MenuIcon so the asset
reference remains unchanged.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx`:
- Around line 42-47: Update the nextId initialization in ApplicationEditTab so
it is derived from the current question IDs in INITIAL_FORM_DATA (and
persisted/local-storage form data when available), using the maximum existing ID
plus one rather than a fixed 1. Preserve the existing useEffect synchronization
for existingFormData and ensure addQuestion always receives an ID unique from
the current question set.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts`:
- Around line 6-88: Extract the duplicated MobileContainer/Container, SortRow,
SortButton, SortText, and CardList definitions into a shared mobile list layout
styles module. Update
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts
lines 6-88 and
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.styles.ts
lines 6-56 to import and reuse those shared styles, preserving each component’s
existing exported names and behavior.
- Around line 6-21: Extract the duplicated MobileContainer/Container, SortRow,
SortButton, SortText, and CardList styled definitions from
ApplicationListTabMobile.styles.ts and ApplicationYearDetailPage.styles.ts into
a shared style module. Update both consumers to import and reuse the shared
definitions, preserving their current styling and responsive behavior.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.tsx`:
- Around line 50-58: Extract the duplicated menu behavior from
ApplicationListTabMobile and ApplicationYearDetailPage into reusable custom
hooks, covering handleMenuToggle, outside-click detection, and scroll-trigger
disabling. Update both components to use the shared hooks while preserving their
existing open-menu key and toggle behavior.
- Around line 50-123: Extract the duplicated menu toggle, scroll-trigger
disabling, and outside-click logic into reusable hooks named
useApplicationCardMenu, useDisableScrollTrigger, and useOutsideClick. Update
ApplicationListTabMobile.tsx (lines 50-123) to use the hooks, and replace the
equivalent logic in ApplicationYearDetailPage.tsx (lines 43-73) with the same
hooks, preserving existing behavior and state handling.
- Around line 205-230: Update the latestForm calculation in the groupedByYear
map to safely handle empty group.forms arrays by providing an initial reduce
value or guarding empty groups before rendering ApplicationListCardMobile.
Preserve the existing newest-editedAt selection for non-empty groups and ensure
empty groups cannot crash rendering or produce an invalid application.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.tsx`:
- Around line 11-23: Update ApplicationListCardMobile to accept an explicit year
prop and use it for the displayed academic year instead of deriving the year
from application.editedAt. In ApplicationListTabMobile, pass group.semesterYear
as year when rendering each card, keeping onNavigate’s existing group-semester
navigation unchanged.

In `@frontend/src/types/application.ts`:
- Around line 71-77: Update ApplicationFormItem.createdAt to be optional, and
adjust every “recent creation” sort or display path that consumes it to safely
handle a missing value by applying the existing fallback or disabling that
behavior until the backend supplies the field. Ensure date sorting never passes
an absent value to new Date and preserve other ApplicationFormItem behavior.

In `@frontend/src/utils/formatKSTDateTime.ts`:
- Around line 38-49: Update the date conversion in the KST formatting function
to avoid reparsing the toLocaleString() output with Date. Use
Intl.DateTimeFormat.formatToParts() with the Asia/Seoul timezone to extract
year, month, day, hour, and minute directly, then preserve the existing Korean
AM/PM and display formatting.

---

Nitpick comments:
In
`@frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx`:
- Line 1: Update the event type usage in ApplicationFormList to match the
existing React import style: replace the React.MouseEvent reference with an
explicit type-only MouseEvent import from react and use MouseEvent for the
handler parameter, or consistently restore the React namespace import. Ensure
type resolution does not depend on global typings or tsconfig settings.

In
`@frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsx`:
- Line 2: Rename the imported SVG binding Morebutton to a camelCase name such as
moreButtonIcon, and update every usage of that binding in the ApplicationRowItem
component while leaving the icon source unchanged.

In
`@frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.tsx`:
- Around line 3-8: Update the MobileFloatingButtonProps interface to make
ariaLabel required instead of optional, ensuring every MobileFloatingButton
usage must provide an accessible button name while leaving the other props
unchanged.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.styles.ts`:
- Around line 58-69: Update the submit button styles in the relevant styled
component to replace the hardcoded font-size and font-weight with the
corresponding project typography token, such as typography.paragraph.p1, while
preserving the existing button appearance and other styles.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx`:
- Around line 1-26: Reorder imports in ApplicationEditTab.tsx to follow external
libraries, internal modules, types, then styles, moving the
ApplicationFormData-related type import before PageContainer styles. Merge the
two imports from ./ApplicationEditTab.styles into a single import.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.styles.ts`:
- Around line 29-34: Update the typography styling in the relevant styled
component of ApplicationTypeTab to reuse the matching token from the theme
typography definitions for 14px, weight 600, 140% line height, and -0.02em
letter spacing, including the font family where supported. Remove the
corresponding hardcoded typography values while preserving the existing
colors.gray[700] color.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx`:
- Around line 25-31: Replace the inline Storybook layout styles with themed
styled-components: in
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx:25-31,
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx:22-28,
and
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx:22-28,
extract the 335px wrapper into a styled component and use it in each decorator.
In
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx:10-16,
extract the menu canvas position and dimensions into a styled component and use
that wrapper instead of inline styles.
- Around line 1-9: Reorder imports to follow the repository convention of
external libraries, internal value modules, type imports, then styles. In
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx:1-9,
place type imports after ApplicationActiveSectionMobile; in
ApplicationActiveSectionMobile.tsx:1-7, place them after ApplicationCardMobile;
in ApplicationCardMobile.stories.tsx:1-6, after ApplicationCardMobile; in
ApplicationCardMobile.tsx:1-9, move all internal value imports before type
imports; in ApplicationListCardMobile.stories.tsx:1-6, after
ApplicationListCardMobile; and in
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx:1-3,
place Storybook type imports after ApplicationMenu.
🪄 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: d39d014d-325f-473b-a2f4-943a0596a86d

📥 Commits

Reviewing files that changed from the base of the PR and between 7d87a9b and 6f7632e.

⛔ Files ignored due to path filters (10)
  • frontend/src/assets/images/icons/Delete_applicant.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/Morebutton.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/checkBox.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/check_inactive.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/check_square_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/copy_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/ellipsis_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/pencil_icon_3.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/sort_asc_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/sort_desc_icon.svg is excluded by !**/*.svg
📒 Files selected for processing (42)
  • frontend/docs/features/admin/application/desktop.md
  • frontend/docs/features/admin/application/mobile-components.md
  • frontend/src/pages/AdminPage/AdminRoutes.tsx
  • frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.styles.ts
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.tsx
  • frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.style.ts
  • frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsx
  • frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.stories.tsx
  • frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.styles.ts
  • frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardEditPage/AwardEditPage.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardEditPage/AwardEditPage.tsx
  • frontend/src/styles/theme/typography.ts
  • frontend/src/types/application.ts
  • frontend/src/utils/formatKSTDateTime.ts
💤 Files with no reviewable changes (2)
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardEditPage/AwardEditPage.styles.ts

Comment on lines +6 to +21
export const MobileContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
max-width: 500px;
min-height: 100vh;
margin: 0 auto;
padding-bottom: calc(80px + env(safe-area-inset-bottom));
box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.04);

${media.mobile} {
max-width: 100%;
margin: 0;
box-shadow: none;
}
`;

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

ApplicationYearDetailPage.styles.ts와 스타일 정의가 완전히 중복됩니다.

MobileContainer/Container, SortRow, SortButton, SortText, CardList가 두 파일에 동일하게 작성되어 있습니다. 공통 스타일 모듈로 추출하는 것을 권장합니다. 상세 내용은 통합 코멘트에서 다룹니다.

Also applies to: 61-88

🤖 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/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts`
around lines 6 - 21, Extract the duplicated MobileContainer/Container, SortRow,
SortButton, SortText, and CardList styled definitions from
ApplicationListTabMobile.styles.ts and ApplicationYearDetailPage.styles.ts into
a shared style module. Update both consumers to import and reuse the shared
definitions, preserving their current styling and responsive behavior.

Comment on lines +6 to +88
export const MobileContainer = styled.div`
display: flex;
flex-direction: column;
width: 100%;
max-width: 500px;
min-height: 100vh;
margin: 0 auto;
padding-bottom: calc(80px + env(safe-area-inset-bottom));
box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.04);

${media.mobile} {
max-width: 100%;
margin: 0;
box-shadow: none;
}
`;

export const Content = styled.div`
display: flex;
flex-direction: column;
gap: 20px;
padding: 32px 20px;
`;

export const SectionHeader = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
`;

export const SectionTitle = styled.h2`
${setTypography(typography.title.title5)}
letter-spacing: -0.02em;
color: ${colors.base.black};
margin: 0;
`;

export const SectionSubtitle = styled.p`
${setTypography(typography.button.button1)}
letter-spacing: -0.02em;
color: ${colors.gray[700]};
margin: 0;
`;

export const MainContent = styled.div`
display: flex;
flex-direction: column;
gap: 51px;
`;

export const ListSection = styled.div`
display: flex;
flex-direction: column;
`;

export const SortRow = styled.div`
display: flex;
justify-content: flex-end;
padding-bottom: 12px;
`;

export const SortButton = styled.button`
display: flex;
flex-direction: row;
align-items: center;
gap: 4px;
background: none;
border: none;
cursor: pointer;
padding: 0;
`;

export const SortText = styled.span`
${setTypography(typography.etc.medium12)}
letter-spacing: -0.02em;
color: ${colors.base.black};
`;

export const CardList = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
`;

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

두 파일의 Container/SortRow/SortButton/SortText/CardList 스타일 정의가 완전히 동일합니다.

공통 스타일 파일(예: MobileListLayout.styles.ts)로 분리해 두 곳에서 import하는 것을 권장합니다.

  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts#L6-L88: MobileContainer(6-21), SortRow/SortButton/SortText/CardList(61-88)를 공통 모듈로 이동.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.styles.ts#L6-L56: Container(6-21), SortRow/SortButton/SortText/CardList(29-56)를 동일 공통 모듈에서 import.
📍 Affects 2 files
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts#L6-L88 (this comment)
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.styles.ts#L6-L56
🤖 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/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts`
around lines 6 - 88, Extract the duplicated MobileContainer/Container, SortRow,
SortButton, SortText, and CardList definitions into a shared mobile list layout
styles module. Update
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts
lines 6-88 and
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.styles.ts
lines 6-56 to import and reuse those shared styles, preserving each component’s
existing exported names and behavior.

Comment on lines +50 to +58
const handleMenuToggle = (
e: React.MouseEvent,
id: string,
prefix: string,
) => {
e.stopPropagation();
const key = `${prefix}-${id}`;
setOpenMenuId((prev) => (prev === key ? null : key));
};

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

ApplicationYearDetailPage.tsx와 로직이 그대로 중복됩니다.

handleMenuToggle, 스크롤 트리거 비활성화 이펙트, 바깥 클릭 감지 이펙트가 ApplicationYearDetailPage.tsx(43-73줄)에도 동일하게 존재합니다. 커스텀 훅(useOutsideClick, useDisableScrollTrigger, useApplicationMenu 등)으로 추출해 재사용하는 것을 권장합니다. 상세 내용은 통합 코멘트에서 다룹니다.

Also applies to: 103-123

🤖 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/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.tsx`
around lines 50 - 58, Extract the duplicated menu behavior from
ApplicationListTabMobile and ApplicationYearDetailPage into reusable custom
hooks, covering handleMenuToggle, outside-click detection, and scroll-trigger
disabling. Update both components to use the shared hooks while preserving their
existing open-menu key and toggle behavior.

Comment on lines +50 to +123
const handleMenuToggle = (
e: React.MouseEvent,
id: string,
prefix: string,
) => {
e.stopPropagation();
const key = `${prefix}-${id}`;
setOpenMenuId((prev) => (prev === key ? null : key));
};

const handleToggleStatus = (id: string, status: ApplicationFormStatus) => {
updateStatus(
{ applicationFormId: id, currentStatus: status },
{
onSuccess: () => setOpenMenuId(null),
onError: () => alert('상태 변경에 실패했습니다.'),
},
);
};

const handleEdit = (id: string) => {
navigate(`/admin/application-list/${id}/edit`);
};

const handleDelete = (id: string) => {
if (
window.confirm(
'지원서 양식을 정말 삭제하시겠습니까?\n삭제된 양식은 복구할 수 없습니다.',
)
) {
deleteApplication(id, {
onSuccess: () => setOpenMenuId(null),
onError: () => alert('지원서 삭제에 실패했습니다.'),
});
}
};

const handleDuplicate = (id: string) => {
duplicateApplication(id, {
onSuccess: () => {
setOpenMenuId(null);
alert('지원서가 성공적으로 복제되었습니다.');
},
onError: () => alert('지원서 복제에 실패했습니다.'),
});
};

const handleNavigateToYear = (year: number) => {
savedScrollY.current = window.scrollY;
setSelectedYear(year);
setActivePage('year-detail');
};

useEffect(() => {
document.body.dataset[SCROLL_TRIGGER_DISABLED] = 'true';
window.dispatchEvent(new Event('scroll'));
return () => {
delete document.body.dataset[SCROLL_TRIGGER_DISABLED];
};
}, []);

useEffect(() => {
const handleOutsideClick = (e: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
setOpenMenuId(null);
}
};
if (openMenuId !== null) {
document.addEventListener('mousedown', handleOutsideClick);
}
return () => {
document.removeEventListener('mousedown', handleOutsideClick);
};
}, [openMenuId]);

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

모바일 목록/연도 상세 페이지 간 상태·이펙트 로직이 그대로 복사되어 있습니다.

메뉴 토글 핸들러, 스크롤 트리거 비활성화 이펙트, 바깥 클릭 감지 이펙트가 두 컴포넌트에 동일하게 작성되어 있습니다. 공통 훅으로 추출하면 유지보수성이 크게 개선되고, 향후 유사 모바일 화면(예: 지원서 편집 탭) 추가 시 재사용할 수 있습니다.

  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.tsx#L50-L123: handleMenuToggle, 스크롤 트리거 비활성화 이펙트(103-109), 바깥 클릭 감지 이펙트(111-123)를 공통 훅(useApplicationCardMenu, useDisableScrollTrigger, useOutsideClick)으로 추출.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.tsx#L43-L73: 동일한 공통 훅을 재사용하도록 교체.
📍 Affects 2 files
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.tsx#L50-L123 (this comment)
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.tsx#L43-L73
🤖 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/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.tsx`
around lines 50 - 123, Extract the duplicated menu toggle, scroll-trigger
disabling, and outside-click logic into reusable hooks named
useApplicationCardMenu, useDisableScrollTrigger, and useOutsideClick. Update
ApplicationListTabMobile.tsx (lines 50-123) to use the hooks, and replace the
equivalent logic in ApplicationYearDetailPage.tsx (lines 43-73) with the same
hooks, preserving existing behavior and state handling.

Comment on lines +205 to +230
{groupedByYear.map((group) => {
const latestForm = group.forms.reduce((latest, form) =>
new Date(form.editedAt) > new Date(latest.editedAt)
? form
: latest,
);
return (
<ApplicationListCardMobile
key={group.semesterYear}
application={latestForm}
isActive={latestForm.status === 'ACTIVE'}
uniqueKeyPrefix={`yeargroup-${group.semesterYear}`}
openMenuId={openMenuId}
menuRef={menuRef}
onToggleStatus={handleToggleStatus}
onEdit={handleEdit}
onMenuToggle={handleMenuToggle}
onDelete={handleDelete}
onDuplicate={handleDuplicate}
onNavigate={() =>
handleNavigateToYear(group.semesterYear)
}
/>
);
})}
</Styled.CardList>

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

초기값 없는 reduce로 인한 크래시 위험.

group.forms가 빈 배열인 연도 그룹이 존재하면(예: 백엔드가 해당 연도에 지원서가 0건인 그룹을 반환하는 경우) 초기값 없는 reduceTypeError: Reduce of empty array with no initial value를 던져 모바일 목록 페이지 전체 렌더링이 크래시됩니다.

🐛 초기값 추가 및 빈 배열 가드
                 {groupedByYear.map((group) => {
-                  const latestForm = group.forms.reduce((latest, form) =>
-                    new Date(form.editedAt) > new Date(latest.editedAt)
-                      ? form
-                      : latest,
-                  );
+                  if (group.forms.length === 0) return null;
+                  const latestForm = group.forms.reduce((latest, form) =>
+                    new Date(form.editedAt) > new Date(latest.editedAt)
+                      ? form
+                      : latest,
+                  , group.forms[0]);
📝 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
{groupedByYear.map((group) => {
const latestForm = group.forms.reduce((latest, form) =>
new Date(form.editedAt) > new Date(latest.editedAt)
? form
: latest,
);
return (
<ApplicationListCardMobile
key={group.semesterYear}
application={latestForm}
isActive={latestForm.status === 'ACTIVE'}
uniqueKeyPrefix={`yeargroup-${group.semesterYear}`}
openMenuId={openMenuId}
menuRef={menuRef}
onToggleStatus={handleToggleStatus}
onEdit={handleEdit}
onMenuToggle={handleMenuToggle}
onDelete={handleDelete}
onDuplicate={handleDuplicate}
onNavigate={() =>
handleNavigateToYear(group.semesterYear)
}
/>
);
})}
</Styled.CardList>
{groupedByYear.map((group) => {
if (group.forms.length === 0) return null;
const latestForm = group.forms.reduce((latest, form) =>
new Date(form.editedAt) > new Date(latest.editedAt)
? form
: latest,
, group.forms[0]);
return (
<ApplicationListCardMobile
key={group.semesterYear}
application={latestForm}
isActive={latestForm.status === 'ACTIVE'}
uniqueKeyPrefix={`yeargroup-${group.semesterYear}`}
openMenuId={openMenuId}
menuRef={menuRef}
onToggleStatus={handleToggleStatus}
onEdit={handleEdit}
onMenuToggle={handleMenuToggle}
onDelete={handleDelete}
onDuplicate={handleDuplicate}
onNavigate={() =>
handleNavigateToYear(group.semesterYear)
}
/>
);
})}
</Styled.CardList>
🤖 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/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.tsx`
around lines 205 - 230, Update the latestForm calculation in the groupedByYear
map to safely handle empty group.forms arrays by providing an initial reduce
value or guarding empty groups before rendering ApplicationListCardMobile.
Preserve the existing newest-editedAt selection for non-empty groups and ensure
empty groups cannot crash rendering or produce an invalid application.

Comment on lines 71 to 77
export interface ApplicationFormItem {
id: ApplicationFormId;
title: string;
createdAt: string;
editedAt: string;
status: ApplicationFormStatus;
}

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

echo "== files =="
git ls-files | rg 'frontend/src/types/application\.ts|frontend/src/.*/ApplicationYearDetailPage\.tsx|frontend/src/hooks|frontend/src/apis' || true

echo "== application types =="
if [ -f frontend/src/types/application.ts ]; then
  cat -n frontend/src/types/application.ts | sed -n '1,140p'
fi

echo "== ApplicationYearDetailPage occurrences =="
rg -n "ApplicationYearDetailPage|createdAt|recent|recently|최근 생성순|getTime\(|new Date\(a\.createdAt|new Date\(b\.createdAt" frontend/src || true

Repository: Moadong/moadong

Length of output: 10244


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from datetime import datetime

def sort(items):
    return sorted(items, key=lambda a: datetime.fromisoformat(a.get('createdAt') or '1970-01-01') if a.get('createdAt') else datetime.min)

for name, items in {
    'with createdAt from backend': [{'createdAt': '2025-07-01'}, {'createdAt': '2024-01-01'}],
    'without createdAt before backend': [{}, {}],
}.items():
    print(name, sort(items))
PY

Repository: Moadong/moadong

Length of output: 282


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from datetime import datetime

def sort_with_invalid_date_logic(items):
    return sorted(items, key=lambda a: datetime.fromisoformat(a.get('createdAt')) if a.get('createdAt') is not None else datetime.min)

for name, items in {
    'with createdAt from backend': [{'createdAt': '2025-07-01'}, {'createdAt': '2024-01-01'}],
    'without createdAt before backend': [{}, {}],
}.items():
    try:
        print(name, sort_with_invalid_date_logic(items), 'new Date(undefined).getTime()=', datetime.fromisoformat(None))
    except Exception as e:
        print(name, 'throws', type(e).__name__, str(e), 'new Date(undefined).getTime()=', float('nan') if not float('nan') else 'NaN')
PY

Repository: Moadong/moadong

Length of output: 390


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== hooks useApplication and useApplicants relevant types/calls =="
for f in frontend/src/apis/application.ts frontend/src/hooks/Queries/useApplication.ts frontend/src/hooks/Queries/useApplicants.ts frontend/src/apis/applicants.ts; do
  if [ -f "$f" ]; then
    echo "--- $f ---"
    cat -n "$f" | sed -n '1,220p'
  fi
done

echo "== ApplicationYearDetailPage relevant sections =="
cat -n frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.tsx | sed -n '1,150p'

Repository: Moadong/moadong

Length of output: 17811


백엔드 응답 변경 전에 createdAt 필수 선언/종속 정렬을 막아 주세요.

createdAt은 백엔드 응답에만 있는 필드인데 타입을 필수로 선언하면 new Date(b.createdAt).getTime()에서 값이 없어도 타입 오류가 잡히지 않고 Invalid Date 상태로 정렬이 깨집니다. 당분간 createdAt?: string으로 처리하고 “최근 생성순” 정렬/표시는 폴백하거나 비활성화하는 편이 안전합니다.

🤖 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/application.ts` around lines 71 - 77, Update
ApplicationFormItem.createdAt to be optional, and adjust every “recent creation”
sort or display path that consumes it to safely handle a missing value by
applying the existing fallback or disabling that behavior until the backend
supplies the field. Ensure date sorting never passes an absent value to new Date
and preserve other ApplicationFormItem behavior.

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 14

🧹 Nitpick comments (8)
frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsx (1)

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

아이콘 import 바인딩을 camelCase로 변경해 주세요.

Morebutton은 로컬 값 바인딩이므로 moreButtonIcon처럼 camelCase로 이름을 바꾸고 사용처도 함께 수정해 주세요.

As per coding guidelines: frontend/**/*.{ts,tsx}에서는 변수와 함수 이름에 camelCase를 사용합니다.

🤖 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/components/ApplicationRow/ApplicationRowItem.tsx`
at line 2, Rename the imported SVG binding Morebutton to a camelCase name such
as moreButtonIcon, and update every usage of that binding in the
ApplicationRowItem component while leaving the icon source unchanged.

Source: Coding guidelines

frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx (1)

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

React 이벤트 타입 import를 명시적으로 통일해 주세요.

기본 React import를 제거했지만 Line 132에서 여전히 React.MouseEvent를 사용합니다. ApplicationRowItem.tsx처럼 import type { MouseEvent } from 'react'e: MouseEvent를 사용하거나 React namespace import를 유지해 타입 해석이 전역 typings/tsconfig 설정에 의존하지 않도록 해 주세요.

🤖 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/components/ApplicationFormList/ApplicationFormList.tsx`
at line 1, Update the event type usage in ApplicationFormList to match the
existing React import style: replace the React.MouseEvent reference with an
explicit type-only MouseEvent import from react and use MouseEvent for the
handler parameter, or consistently restore the React namespace import. Ensure
type resolution does not depend on global typings or tsconfig settings.
frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.tsx (1)

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

ariaLabel을 필수 prop으로 변경 권장.

아이콘이 alt=''(장식용)이라 ariaLabel이 버튼의 유일한 접근 가능한 이름입니다. 현재 호출부는 모두 값을 전달하지만, optional로 두면 향후 호출부에서 누락 시 스크린리더에 이름 없는 버튼이 될 수 있습니다.

♻️ 필수 prop으로 변경
 interface MobileFloatingButtonProps {
   onClick: () => void;
   icon: string;
-  ariaLabel?: string;
+  ariaLabel: string;
   bottom?: string;
 }
🤖 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/components/MobileFloatingButton/MobileFloatingButton.tsx`
around lines 3 - 8, Update the MobileFloatingButtonProps interface to make
ariaLabel required instead of optional, ensuring every MobileFloatingButton
usage must provide an accessible button name while leaving the other props
unchanged.
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx (2)

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

Storybook 레이아웃도 styled-components와 테마를 사용하세요. 인라인 style은 저장소 스타일링 규칙을 우회합니다.

  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx#L25-L31: 폭 335px 래퍼를 styled-component로 추출하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx#L22-L28: 폭 335px 래퍼를 styled-component로 추출하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx#L22-L28: 폭 335px 래퍼를 styled-component로 추출하세요.
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx#L10-L16: 메뉴 캔버스의 위치·크기 스타일을 styled-component로 추출하세요.
🤖 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/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx`
around lines 25 - 31, Replace the inline Storybook layout styles with themed
styled-components: in
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx:25-31,
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx:22-28,
and
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx:22-28,
extract the 335px wrapper into a styled component and use it in each decorator.
In
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx:10-16,
extract the menu canvas position and dimensions into a styled component and use
that wrapper instead of inline styles.

Source: Coding guidelines


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

타입 import를 내부 모듈 뒤로 이동하세요. 저장소 규칙의 순서(외부 라이브러리 → 내부 모듈 → 타입 → 스타일)와 다릅니다.

  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx#L1-L9: ApplicationActiveSectionMobile import 뒤에 타입 import를 배치하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.tsx#L1-L7: ApplicationCardMobile import 뒤에 타입 import를 배치하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx#L1-L6: ApplicationCardMobile import 뒤에 타입 import를 배치하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.tsx#L1-L9: 모든 내부 값 import를 타입 import보다 앞에 배치하세요.
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx#L1-L6: ApplicationListCardMobile import 뒤에 타입 import를 배치하세요.
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx#L1-L3: ApplicationMenu import 뒤에 Storybook 타입 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/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx`
around lines 1 - 9, Reorder imports to follow the repository convention of
external libraries, internal value modules, type imports, then styles. In
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx:1-9,
place type imports after ApplicationActiveSectionMobile; in
ApplicationActiveSectionMobile.tsx:1-7, place them after ApplicationCardMobile;
in ApplicationCardMobile.stories.tsx:1-6, after ApplicationCardMobile; in
ApplicationCardMobile.tsx:1-9, move all internal value imports before type
imports; in ApplicationListCardMobile.stories.tsx:1-6, after
ApplicationListCardMobile; and in
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx:1-3,
place Storybook type imports after ApplicationMenu.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx (1)

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

import 순서가 가이드라인과 다릅니다.

@/styles/PageContainer.styles(17번째 줄)가 타입 import(@/types/application, 18-23번째 줄)보다 먼저 위치해 있습니다. "external libraries → internal modules → types → styles" 순서를 지켜야 합니다. 또한 24-25번째 줄에서 동일 파일(./ApplicationEditTab.styles)을 두 번에 나눠 import하고 있어 하나로 합칠 수 있습니다.

As per coding guidelines, "Order imports as external libraries, internal modules, types, then styles."

♻️ 제안 수정
-import { useAdminClubId } from '`@/store/useAdminClubStore`';
-import { PageContainer } from '`@/styles/PageContainer.styles`';
-import {
+import { useAdminClubId } from '`@/store/useAdminClubStore`';
+import {
   ApplicationFormData,
   ApplicationFormMode,
   Question,
   QuestionType,
 } from '`@/types/application`';
-import * as Styled from './ApplicationEditTab.styles';
-import { QuestionDivider } from './ApplicationEditTab.styles';
+import { PageContainer } from '`@/styles/PageContainer.styles`';
+import * as Styled, { QuestionDivider } from './ApplicationEditTab.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/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx`
around lines 1 - 26, Reorder imports in ApplicationEditTab.tsx to follow
external libraries, internal modules, types, then styles, moving the
ApplicationFormData-related type import before PageContainer styles. Merge the
two imports from ./ApplicationEditTab.styles into a single import.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.styles.ts (1)

29-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

폰트 스타일이 테마 타이포그래피 대신 하드코딩되어 있습니다.

colors 테마는 잘 사용하고 있지만, font-family/font-weight/font-size/line-height/letter-spacing은 값이 직접 하드코딩되어 있습니다. 이번 PR 계열에서 frontend/src/styles/theme/typography.ts가 공통 타이포그래피 목적으로 확장되었으므로, 가능하다면 해당 토큰을 재사용하는 것이 일관성 측면에서 좋습니다.

typography.ts에 이 스타일(14px/600/140%/-0.02em)에 대응하는 토큰이 있는지 확인해 주세요.

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/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.styles.ts`
around lines 29 - 34, Update the typography styling in the relevant styled
component of ApplicationTypeTab to reuse the matching token from the theme
typography definitions for 14px, weight 600, 140% line height, and -0.02em
letter spacing, including the font family where supported. Remove the
corresponding hardcoded typography values while preserving the existing
colors.gray[700] color.

Source: Coding guidelines

frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.styles.ts (1)

58-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

제출 버튼 타이포그래피도 토큰으로 통일하세요.

Line 62-63의 하드코딩된 글꼴 크기·굵기를 typography.paragraph.p1 등 대응 토큰으로 교체하세요.

수정 예시
-  font-size: 1.25rem;
-  font-weight: 600;
+  ${setTypography(typography.paragraph.p1)}

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/ApplicationTab/ApplicationEditTab/ApplicationEditTab.styles.ts`
around lines 58 - 69, Update the submit button styles in the relevant styled
component to replace the hardcoded font-size and font-weight with the
corresponding project typography token, such as typography.paragraph.p1, while
preserving the existing button appearance and other styles.

Source: Coding guidelines

🤖 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/docs/features/admin/application/mobile-components.md`:
- Around line 51-63: 문서의 컴포넌트명을 ApplicationFAB에서 실제 공통 컴포넌트인
MobileFloatingButton으로 변경하고, 관련 코드 목록에
src/pages/AdminPage/components/MobileFloatingButton/ 경로를 추가하세요.

In
`@frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx`:
- Around line 228-231: 지원서 상태 용어를 “활성화된 지원서”와 “활성화/비활성화” 기준으로 통일하세요.
frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx의
빈 상태 문구와 Line 193 섹션 제목을 동일한 용어로 맞추고,
frontend/docs/features/admin/application/desktop.md의 7-8행 “게시된 지원서” 표현과 34-37행
상태 규칙도 UI와 일관되게 수정하세요.

In
`@frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.styles.ts`:
- Around line 23-44: Change MenuItem from a styled div to a styled button so
ApplicationMenu click actions are keyboard accessible and expose button
semantics. Remove the button’s default border, and add a visible :focus-visible
style while preserving the existing layout, colors, hover behavior, and
ToggleMenuItem styling.

In `@frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.tsx`:
- Around line 30-53: Update Styled.MenuItem and Styled.ToggleMenuItem usages in
ApplicationMenu so the interactive entries use native button semantics,
preferably by configuring their styled components to render as button elements
while preserving existing click handlers and styling. Ensure all menu actions
remain keyboard-focusable and activate correctly with Enter/Space.
- Line 3: Rename the imported binding Delete_applicant to the camelCase name
deleteApplicantIcon and update its usage in Styled.MenuIcon so the asset
reference remains unchanged.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx`:
- Around line 42-47: Update the nextId initialization in ApplicationEditTab so
it is derived from the current question IDs in INITIAL_FORM_DATA (and
persisted/local-storage form data when available), using the maximum existing ID
plus one rather than a fixed 1. Preserve the existing useEffect synchronization
for existingFormData and ensure addQuestion always receives an ID unique from
the current question set.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts`:
- Around line 6-88: Extract the duplicated MobileContainer/Container, SortRow,
SortButton, SortText, and CardList definitions into a shared mobile list layout
styles module. Update
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts
lines 6-88 and
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.styles.ts
lines 6-56 to import and reuse those shared styles, preserving each component’s
existing exported names and behavior.
- Around line 6-21: Extract the duplicated MobileContainer/Container, SortRow,
SortButton, SortText, and CardList styled definitions from
ApplicationListTabMobile.styles.ts and ApplicationYearDetailPage.styles.ts into
a shared style module. Update both consumers to import and reuse the shared
definitions, preserving their current styling and responsive behavior.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.tsx`:
- Around line 50-58: Extract the duplicated menu behavior from
ApplicationListTabMobile and ApplicationYearDetailPage into reusable custom
hooks, covering handleMenuToggle, outside-click detection, and scroll-trigger
disabling. Update both components to use the shared hooks while preserving their
existing open-menu key and toggle behavior.
- Around line 50-123: Extract the duplicated menu toggle, scroll-trigger
disabling, and outside-click logic into reusable hooks named
useApplicationCardMenu, useDisableScrollTrigger, and useOutsideClick. Update
ApplicationListTabMobile.tsx (lines 50-123) to use the hooks, and replace the
equivalent logic in ApplicationYearDetailPage.tsx (lines 43-73) with the same
hooks, preserving existing behavior and state handling.
- Around line 205-230: Update the latestForm calculation in the groupedByYear
map to safely handle empty group.forms arrays by providing an initial reduce
value or guarding empty groups before rendering ApplicationListCardMobile.
Preserve the existing newest-editedAt selection for non-empty groups and ensure
empty groups cannot crash rendering or produce an invalid application.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.tsx`:
- Around line 11-23: Update ApplicationListCardMobile to accept an explicit year
prop and use it for the displayed academic year instead of deriving the year
from application.editedAt. In ApplicationListTabMobile, pass group.semesterYear
as year when rendering each card, keeping onNavigate’s existing group-semester
navigation unchanged.

In `@frontend/src/types/application.ts`:
- Around line 71-77: Update ApplicationFormItem.createdAt to be optional, and
adjust every “recent creation” sort or display path that consumes it to safely
handle a missing value by applying the existing fallback or disabling that
behavior until the backend supplies the field. Ensure date sorting never passes
an absent value to new Date and preserve other ApplicationFormItem behavior.

In `@frontend/src/utils/formatKSTDateTime.ts`:
- Around line 38-49: Update the date conversion in the KST formatting function
to avoid reparsing the toLocaleString() output with Date. Use
Intl.DateTimeFormat.formatToParts() with the Asia/Seoul timezone to extract
year, month, day, hour, and minute directly, then preserve the existing Korean
AM/PM and display formatting.

---

Nitpick comments:
In
`@frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx`:
- Line 1: Update the event type usage in ApplicationFormList to match the
existing React import style: replace the React.MouseEvent reference with an
explicit type-only MouseEvent import from react and use MouseEvent for the
handler parameter, or consistently restore the React namespace import. Ensure
type resolution does not depend on global typings or tsconfig settings.

In
`@frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsx`:
- Line 2: Rename the imported SVG binding Morebutton to a camelCase name such as
moreButtonIcon, and update every usage of that binding in the ApplicationRowItem
component while leaving the icon source unchanged.

In
`@frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.tsx`:
- Around line 3-8: Update the MobileFloatingButtonProps interface to make
ariaLabel required instead of optional, ensuring every MobileFloatingButton
usage must provide an accessible button name while leaving the other props
unchanged.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.styles.ts`:
- Around line 58-69: Update the submit button styles in the relevant styled
component to replace the hardcoded font-size and font-weight with the
corresponding project typography token, such as typography.paragraph.p1, while
preserving the existing button appearance and other styles.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx`:
- Around line 1-26: Reorder imports in ApplicationEditTab.tsx to follow external
libraries, internal modules, types, then styles, moving the
ApplicationFormData-related type import before PageContainer styles. Merge the
two imports from ./ApplicationEditTab.styles into a single import.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.styles.ts`:
- Around line 29-34: Update the typography styling in the relevant styled
component of ApplicationTypeTab to reuse the matching token from the theme
typography definitions for 14px, weight 600, 140% line height, and -0.02em
letter spacing, including the font family where supported. Remove the
corresponding hardcoded typography values while preserving the existing
colors.gray[700] color.

In
`@frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx`:
- Around line 25-31: Replace the inline Storybook layout styles with themed
styled-components: in
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx:25-31,
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx:22-28,
and
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx:22-28,
extract the 335px wrapper into a styled component and use it in each decorator.
In
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx:10-16,
extract the menu canvas position and dimensions into a styled component and use
that wrapper instead of inline styles.
- Around line 1-9: Reorder imports to follow the repository convention of
external libraries, internal value modules, type imports, then styles. In
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx:1-9,
place type imports after ApplicationActiveSectionMobile; in
ApplicationActiveSectionMobile.tsx:1-7, place them after ApplicationCardMobile;
in ApplicationCardMobile.stories.tsx:1-6, after ApplicationCardMobile; in
ApplicationCardMobile.tsx:1-9, move all internal value imports before type
imports; in ApplicationListCardMobile.stories.tsx:1-6, after
ApplicationListCardMobile; and in
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx:1-3,
place Storybook type imports after ApplicationMenu.
🪄 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: d39d014d-325f-473b-a2f4-943a0596a86d

📥 Commits

Reviewing files that changed from the base of the PR and between 7d87a9b and 6f7632e.

⛔ Files ignored due to path filters (10)
  • frontend/src/assets/images/icons/Delete_applicant.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/Morebutton.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/checkBox.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/check_inactive.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/check_square_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/copy_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/ellipsis_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/pencil_icon_3.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/sort_asc_icon.svg is excluded by !**/*.svg
  • frontend/src/assets/images/icons/sort_desc_icon.svg is excluded by !**/*.svg
📒 Files selected for processing (42)
  • frontend/docs/features/admin/application/desktop.md
  • frontend/docs/features/admin/application/mobile-components.md
  • frontend/src/pages/AdminPage/AdminRoutes.tsx
  • frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.stories.tsx
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.styles.ts
  • frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.tsx
  • frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.style.ts
  • frontend/src/pages/AdminPage/components/ApplicationRow/ApplicationRowItem.tsx
  • frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.stories.tsx
  • frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.styles.ts
  • frontend/src/pages/AdminPage/components/MobileFloatingButton/MobileFloatingButton.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsListTab/ApplicantsListTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantsTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/components/mobile/ApplicationTypeTab/ApplicationTypeTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTab.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTab.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/ApplicationListTabMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationActiveSectionMobile/ApplicationActiveSectionMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationCardMobile/ApplicationCardMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.stories.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.styles.ts
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationYearDetailPage/ApplicationYearDetailPage.tsx
  • frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationTab.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/ClubIntroEditTabMobile.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardEditPage/AwardEditPage.styles.ts
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardEditPage/AwardEditPage.tsx
  • frontend/src/styles/theme/typography.ts
  • frontend/src/types/application.ts
  • frontend/src/utils/formatKSTDateTime.ts
💤 Files with no reviewable changes (2)
  • frontend/src/pages/AdminPage/tabs/ApplicationListTab/ApplicationMenu.tsx
  • frontend/src/pages/AdminPage/tabs/ClubIntroEditTab/components/mobile/AwardEditPage/AwardEditPage.styles.ts
🛑 Comments failed to post (8)
frontend/docs/features/admin/application/mobile-components.md (1)

51-63: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

실제 컴포넌트명으로 문서를 맞추세요.

PR에서 추가한 공통 컴포넌트는 MobileFloatingButton인데 문서는 ApplicationFAB로 표기합니다. 제목을 변경하고 src/pages/AdminPage/components/MobileFloatingButton/ 경로도 관련 코드에 추가하세요.

🤖 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/docs/features/admin/application/mobile-components.md` around lines
51 - 63, 문서의 컴포넌트명을 ApplicationFAB에서 실제 공통 컴포넌트인 MobileFloatingButton으로 변경하고, 관련
코드 목록에 src/pages/AdminPage/components/MobileFloatingButton/ 경로를 추가하세요.
frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx (1)

228-231: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

지원서 상태 용어를 문서와 데스크톱 UI에서 일관되게 맞춰 주세요.

  • frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx#L228-L231: 빈 상태 문구와 함께 Line 193의 섹션 제목도 “활성화된 지원서”로 변경하거나, 게시 용어를 유지하도록 문서 규칙을 수정해 주세요.
  • frontend/docs/features/admin/application/desktop.md#L7-L8: 섹션 설명의 “게시된 지원서” 표현을 실제 상태 용어와 맞춰 주세요.
  • frontend/docs/features/admin/application/desktop.md#L34-L37: “활성화/비활성화” 규칙과 위 섹션 표현이 일관되도록 정리해 주세요.
📍 Affects 2 files
  • frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx#L228-L231 (this comment)
  • frontend/docs/features/admin/application/desktop.md#L7-L8
  • frontend/docs/features/admin/application/desktop.md#L34-L37
🤖 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/components/ApplicationFormList/ApplicationFormList.tsx`
around lines 228 - 231, 지원서 상태 용어를 “활성화된 지원서”와 “활성화/비활성화” 기준으로 통일하세요.
frontend/src/pages/AdminPage/components/ApplicationFormList/ApplicationFormList.tsx의
빈 상태 문구와 Line 193 섹션 제목을 동일한 용어로 맞추고,
frontend/docs/features/admin/application/desktop.md의 7-8행 “게시된 지원서” 표현과 34-37행
상태 규칙도 UI와 일관되게 수정하세요.
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.styles.ts (1)

23-44: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

메뉴 항목을 button으로 변경하세요.

ApplicationMenu.tsx에서 클릭 핸들러를 연결하지만 현재 기반 요소가 div라 키보드 포커스·Enter/Space 실행·컨트롤 의미가 없습니다. styled.button으로 바꾸고 기본 테두리를 제거하며 :focus-visible 상태를 추가하세요.

수정 예시
-export const MenuItem = styled.div<{ $danger?: boolean }>`
+export const MenuItem = styled.button<{ $danger?: boolean }>`
+  border: 0;
   display: flex;
   align-items: center;
   align-self: stretch;
   padding: 4px 12px;
+  text-align: left;
   gap: 11px;
   ${setTypography(typography.paragraph.p6)}
   letter-spacing: -0.02em;
   color: ${({ $danger }) => ($danger ? '`#FF3B30`' : colors.gray[800])};
   background-color: transparent;
   cursor: pointer;
   box-sizing: border-box;
 
   &:hover {
     background-color: ${colors.gray[100]};
   }
+
+  &:focus-visible {
+    outline: 2px solid ${colors.primary[800]};
+    outline-offset: -2px;
+  }
 `;
📝 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.

export const MenuItem = styled.button<{ $danger?: boolean }>`
  border: 0;
  display: flex;
  align-items: center;
  align-self: stretch;
  padding: 4px 12px;
  text-align: left;
  gap: 11px;
  ${setTypography(typography.paragraph.p6)}
  letter-spacing: -0.02em;
  color: ${({ $danger }) => ($danger ? '`#FF3B30`' : colors.gray[800])};
  background-color: transparent;
  cursor: pointer;
  box-sizing: border-box;

  &:hover {
    background-color: ${colors.gray[100]};
  }

  &:focus-visible {
    outline: 2px solid ${colors.primary[800]};
    outline-offset: -2px;
  }
`;

export const ToggleMenuItem = styled(MenuItem)<{ $active?: boolean }>`
  ${setTypography(typography.paragraph.p5)}
  letter-spacing: -0.02em;
`;
🤖 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/components/ApplicationMenu/ApplicationMenu.styles.ts`
around lines 23 - 44, Change MenuItem from a styled div to a styled button so
ApplicationMenu click actions are keyboard accessible and expose button
semantics. Remove the button’s default border, and add a visible :focus-visible
style while preserving the existing layout, colors, hover behavior, and
ToggleMenuItem styling.
frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.tsx (2)

3-3: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

camelCase 네이밍 위반: Delete_applicant.

As per coding guidelines, "Use camelCase for variables and functions". import 바인딩 이름은 자산 파일명과 별개로 자유롭게 지정 가능하므로 camelCase로 변경하는 것을 권장합니다.

♻️ 네이밍 수정
-import Delete_applicant from '`@/assets/images/icons/Delete_applicant.svg`';
+import deleteApplicantIcon from '`@/assets/images/icons/Delete_applicant.svg`';

(사용처: <Styled.MenuIcon src={Delete_applicant} />src={deleteApplicantIcon})

📝 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.

import deleteApplicantIcon from '`@/assets/images/icons/Delete_applicant.svg`';
🤖 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/components/ApplicationMenu/ApplicationMenu.tsx`
at line 3, Rename the imported binding Delete_applicant to the camelCase name
deleteApplicantIcon and update its usage in Styled.MenuIcon so the asset
reference remains unchanged.

Source: Coding guidelines


30-53: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
ast-grep outline frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.styles.ts --items all

Repository: Moadong/moadong

Length of output: 782


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "ApplicationMenu component:"
cat -n frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.tsx

echo
echo "ApplicationMenu styles:"
cat -n frontend/src/pages/AdminPage/components/ApplicationMenu/ApplicationMenu.styles.ts

Repository: Moadong/moadong

Length of output: 4535


인터랙티브 메뉴 항목을 네이티브 버튼으로 교체하세요.

MenuItemToggleMenuItemstyled.div이기 때문에 기본적으로 포커스/엔터 클릭이 처리되지 않습니다. button 요소나 role="button" + 키보드 핸들러와 tabIndex를 추가해 접근성 문제를 해결해야 합니다.

🤖 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/components/ApplicationMenu/ApplicationMenu.tsx`
around lines 30 - 53, Update Styled.MenuItem and Styled.ToggleMenuItem usages in
ApplicationMenu so the interactive entries use native button semantics,
preferably by configuring their styled components to render as button elements
while preserving existing click handlers and styling. Ensure all menu actions
remain keyboard-focusable and activate correctly with Enter/Space.
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx (1)

42-47: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -a 'initialFormData.ts'
fd -a 'initialFormData.ts' --exec cat -n {}

Repository: Moadong/moadong

Length of output: 1485


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx"
wc -l "$file"

printf '\n--- outline ---\n'
ast-grep outline "$file" || true

printf '\n--- relevant source 1-110 ---\n'
sed -n '1,110p' "$file" | cat -n

printf '\n--- relevant source 210-265 ---\n'
sed -n '210,265p' "$file" | cat -n

Repository: Moadong/moadong

Length of output: 6905


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx"

printf '\n--- relevant source 110-220 ---\n'
sed -n '110,220p' "$file" | cat -n

printf '\n--- locate ApplicationFormMode declarations and use ---\n'
rg -n "ApplicationFormMode|INITIAL_FORM_DATA|nextId|addQuestion|removeQuestion|updateQuestionField" frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx frontend/src/constants frontend/src/types -S

Repository: Moadong/moadong

Length of output: 9191


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx"

printf '\n--- relevant source 244-360 ---\n'
sed -n '244,360p' "$file" | cat -n

printf '\n--- question builder render for questions ---\n'
sed -n '282,350p' "$file" | cat -n

Repository: Moadong/moadong

Length of output: 7311


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n--- check whether existingFormData.mode exists in other files/types ---\n'
rg -n "mode:|formMode|formId|useGetApplication|createApplication\\(" frontend/src -S

printf '\n--- package/tooling availability ---\n'
node -v 2>/dev/null || true
npm --version 2>/dev/null || true

Repository: Moadong/moadong

Length of output: 4565


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n--- ApplicationFormPage initialization state ---\n'
file="frontend/src/pages/ApplicationFormPage/ApplicationFormPage.tsx"
wc -l "$file"
sed -n '1,120p' "$file" | cat -n

printf '\n--- ApplicationFormPage submit id normalization ---\n'
sed -n '120,220p' "$file" | cat -n

Repository: Moadong/moadong

Length of output: 7316


nextId 초기값을 시드 질문 id와 겹치지 않게 맞추세요.

신규 생성 모드에서는 existingFormData가 없어 useEffect에서 nextId 재생산이 안 되고, 현재 NEXT_ID가 고정된 1입니다. INITIAL_FORM_DATA에는 이미 id: 1 시드 질문이 있으므로 addQuestion로 새 질문을 추가하면 같은 id가 반복되어 updateQuestionField/removeQuestion/handleTypeChange/handleRequiredChange에서 이전 질문까지 영향을 주거나 React key 중복 경고를 일으킬 수 있습니다. 초기값/로컬 스토리지의 현재 질문 set을 기준으로 한 뒤+1 값을 사용하세요.

🤖 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/ApplicationTab/ApplicationEditTab/ApplicationEditTab.tsx`
around lines 42 - 47, Update the nextId initialization in ApplicationEditTab so
it is derived from the current question IDs in INITIAL_FORM_DATA (and
persisted/local-storage form data when available), using the maximum existing ID
plus one rather than a fixed 1. Preserve the existing useEffect synchronization
for existingFormData and ensure addQuestion always receives an ID unique from
the current question set.
frontend/src/pages/AdminPage/tabs/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.tsx (1)

11-23: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

표시되는 연도가 실제 이동 대상 연도와 어긋날 수 있습니다.

ApplicationListTabMobile.tsx에서 이 카드는 연도 그룹의 최신 수정 폼(latestForm)을 application으로 받지만, 실제 그룹 연도(group.semesterYear)는 전달되지 않습니다. 이 컴포넌트는 40번째 줄에서 application.editedAt의 연도를 계산해 표시하는데, latestForm의 수정 시점이 실제 학기 연도와 다른 해로 넘어가면(예: 연말에 생성된 폼을 다음 해 초에 수정) 카드에 표시되는 "OOOO년도"와 onNavigate가 실제로 이동시키는 연도(group.semesterYear)가 서로 달라집니다.

year를 prop으로 명시적으로 받아 표시하도록 수정하는 것을 권장합니다.

🐛 `year`를 명시적 prop으로 전달
 interface ApplicationListCardMobileProps {
   application: ApplicationFormItem;
   isActive: boolean;
+  year: number;
   uniqueKeyPrefix: string;
   ...
 }

 const ApplicationListCardMobile = ({
   application,
   isActive,
+  year,
   uniqueKeyPrefix,
   ...
 }: ApplicationListCardMobileProps) => {
   const currentMenuKey = `${uniqueKeyPrefix}-${application.id}`;
   const isMenuOpen = openMenuId === currentMenuKey;
-  const year = new Date(application.editedAt).getFullYear();

호출부(ApplicationListTabMobile.tsx)에서는 year={group.semesterYear}를 전달합니다.

📝 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.

interface ApplicationListCardMobileProps {
  application: ApplicationFormItem;
  isActive: boolean;
  year: number;
  uniqueKeyPrefix: string;
  openMenuId: string | null;
  menuRef: RefObject<HTMLDivElement | null>;
  onToggleStatus: (id: string, status: ApplicationFormStatus) => void;
  onEdit: (id: string) => void;
  onMenuToggle: (e: MouseEvent, id: string, prefix: string) => void;
  onDelete: (id: string) => void;
  onDuplicate?: (id: string) => void;
  onNavigate?: () => 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/ApplicationTab/ApplicationListTab/components/mobile/ApplicationListCardMobile/ApplicationListCardMobile.tsx`
around lines 11 - 23, Update ApplicationListCardMobile to accept an explicit
year prop and use it for the displayed academic year instead of deriving the
year from application.editedAt. In ApplicationListTabMobile, pass
group.semesterYear as year when rendering each card, keeping onNavigate’s
existing group-semester navigation unchanged.
frontend/src/utils/formatKSTDateTime.ts (1)

38-49: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP 'new Date\(\s*.*\.toLocaleString\(' frontend/src

Repository: Moadong/moadong

Length of output: 153


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Locate file:\n'
fd -a 'formatKSTDateTime.ts' . || true

printf '\nRepository files relevant:\n'
git ls-files | rg 'frontend/src/utils/formatKSTDateTime\.ts|formatKSTDateTime' || true

if [ -f frontend/src/utils/formatKSTDateTime.ts ]; then
  printf '\nFile contents with line numbers:\n'
  cat -n frontend/src/utils/formatKSTDateTime.ts
fi

printf '\nAll references:\n'
rg -n "formatKSTDateTime|formatKSTDate|KST" frontend/src || true

printf '\nAll new Date(toLocaleString patterns, broad):\n'
rg -n "new Date\s*\(\s*.*toLocaleString|toLocaleString\s*\([^)]*\)\s*\)\s*,?\s*new Date" frontend/src || true

Repository: Moadong/moadong

Length of output: 5542


🌐 Web query:

MDN Date constructor parses locale-specific date string unsupported behavior

💡 Result:

The JavaScript Date constructor and Date.parse method officially support only a specific subset of the ISO 8601 format (specifically YYYY-MM-DDTHH:mm:ss.sssZ) [1][2][3][4]. While these are the only formats guaranteed by the ECMAScript specification, browser engines are permitted to implement support for other, non-standard date formats at their discretion [1][2][4]. Because of this "implementation-defined" behavior, locale-specific date strings (such as "Mercredi 8 Septembre 2021" or "MM/DD/YYYY") are not reliably supported across different environments [2][5][4]. Key points regarding this behavior include: - Inconsistency Across Engines: Engines like V8 (Chrome, Edge, Node.js) and SpiderMonkey (Firefox) often use different heuristics to parse non-standard strings [5][6]. A string that parses correctly in one browser may return "Invalid Date" (resulting in NaN) in another [5]. - No Guarantee of Success: The ECMAScript specification does not require support for locale-specific formats, such as those produced by toLocaleString [2][7]. Consequently, relying on the Date constructor to parse such strings is considered unsafe practice [3][6]. - Implementation-Specific Heuristics: When an engine attempts to parse a non-standard string, it often relies on legacy code paths that may guess the format based on character patterns, which is prone to error and variation between browser versions [2][8][6]. - Recommendation: Developers should avoid using the Date constructor or Date.parse for any string format other than the required ISO 8601 subset [3][6]. For locale-specific or user-provided input, use dedicated date-parsing libraries (like date-fns, Luxon, or Day.js) or the modern Temporal API, which offer robust, predictable, and internationalized parsing capabilities [6].

Citations:


🌐 Web query:

MDN Intl.DateTimeFormat formatToParts dayPeriod value PM AM documentation

💡 Result:

In the Intl.DateTimeFormat.prototype.formatToParts method, the dayPeriod part type represents the period of the day, such as AM, PM, "in the morning," or "noon" [1][2]. Key details regarding the dayPeriod value: - The value is returned as a string within the object representing that component in the array returned by formatToParts [1]. - The inclusion of the dayPeriod component—and its specific string value—is governed by the dayPeriod option provided to the Intl.DateTimeFormat constructor [3][4]. - This option only takes effect when using a 12-hour clock (hourCycle: "h12" or "h11") [3][4]. - The possible values for the dayPeriod configuration option are "narrow", "short", and "long" [3][4]. - Note that for many locales, the resulting string value (e.g., "AM", "PM") may be identical regardless of whether you specify "narrow", "short", or "long", as implementations often follow specific locale data standards (CLDR) [3][4][5]. Example usage: const formatter = new Intl.DateTimeFormat("en-US", { hour: "numeric", dayPeriod: "short" }); const parts = formatter.formatToParts(new Date); // Example output for parts: [{ type: "hour", value: "3" },..., { type: "dayPeriod", value: "AM" }] [1][2]

Citations:


locale 문자열을 Date로 재파싱하지 마세요.

toLocaleString() 결과는 Date 생성자가 이식성 있게 파싱한다고 보장되지 않아요. 브라우저에 따라 Invalid Date/NaN으로 표시될 수 있으니, formatToParts()로 KST 시계열 구성 요소를 직접 추출하세요.

🤖 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/utils/formatKSTDateTime.ts` around lines 38 - 49, Update the
date conversion in the KST formatting function to avoid reparsing the
toLocaleString() output with Date. Use Intl.DateTimeFormat.formatToParts() with
the Asia/Seoul timezone to extract year, month, day, hour, and minute directly,
then preserve the existing Korean AM/PM and display formatting.

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