Skip to content

[Feat] WTH-306: 어드민 일정 API 연결 #69

Merged
JIN921 merged 16 commits intodevelopfrom
feat/WTH-306-어드민-일정-api-연결
Apr 26, 2026

Hidden character warning

The head ref may contain hidden characters: "feat/WTH-306-\uc5b4\ub4dc\ubbfc-\uc77c\uc815-api-\uc5f0\uacb0"
Merged

[Feat] WTH-306: 어드민 일정 API 연결 #69
JIN921 merged 16 commits intodevelopfrom
feat/WTH-306-어드민-일정-api-연결

Conversation

@JIN921
Copy link
Copy Markdown
Collaborator

@JIN921 JIN921 commented Apr 24, 2026

✅ PR 유형

어떤 변경 사항이 있었나요?

  • 새로운 기능 추가
  • 버그 수정
  • 코드 리팩토링

📌 관련 이슈번호

  • Closed #306

✅ Key Changes

API 연결

  • 어드민 일정 월별 조회 API 연결 (useAdminMonthlySchedules)
  • 일반 일정 생성 · 수정 · 삭제 API 연결 (useCreateSchedule, useUpdateSchedule, useDeleteSchedule)
  • 에러 응답 코드에 따른 토스트 에러 메시지 처리

기능 추가

  • 세션 탭 분리 (SessionTabContent) 및 세션 수정/삭제 모달 (EditSessionModal) 추가
  • 일정 생성 모달에 일반/세션 탭 전환 기능 추가 (CreateScheduleModal activeTab 제어)
  • 기수 드롭다운 공통 컴포넌트 (CardinalDropdown) 적용
  • 반복 세션 삭제/저장 시 '이 세션만 / 이후 전체' 선택 다이얼로그 처리

버그 수정

  • 날짜 피커 z-index 수정
  • 일반 일정 렌더링 안 되는 문제 수정
  • 일정/세션 수정 탭 오류 수정
  • 로그아웃 에러 해결

코드 리팩토링

  • 상대 경로 import → @/ alias 통일 (EditSessionModal)
  • 세션/일반 제목 유효성 검사 isScheduleTitleValid() 로 일관화
  • 중복된 닫기·메뉴 버튼 → ModalIconButton 컴포넌트로 교체
  • SessionScheduleForm 내 로컬 Cardinal 인터페이스 제거 → @/types/admin/cardinal import
  • CalendarPicker, DateTimeInput 직접 경로 import → @/components/ui barrel import
  • SchedulePageContent 중복 @/components/ui import 병합
  • admin/index.tsModalIconButton export 추가
  • Tailwind arbitrary value → canonical token class 변환 (max-h-[700px]max-h-175 등)

📸 스크린샷 or 실행영상


🎸 기타 사항 or 추가 코멘트

  • 세션 수정 API는 미구현 상태로 TODO 주석 처리 (EditSessionModal — API 스펙 확정 후 연동 예정)

  • Schedule 타입에 content 필드가 없어 수정 모달에서 기존 설명을 불러올 수 없음 — API 응답 스펙 업데이트 필요

  • 일단 어드민 레이아웃 헤더는 없애 달라 하셧던 거 같아서 렌더링 안 되는 문제 걍 냅두고 주석처리 햇습니다! 하단 보더도 없애고 로그아웃 버튼만 남겻어용

  • 슬랙에도 남겻는데 dialog z-index가 바뀌면서 기존에 사용되던 모달들이 안 보여서.. 고치긴 햇으나 미리보기 해보시고 안 보이는 부분 잇으면 말씀 주세용ㅜㅜ!!

  • 세션 api는 ui와 마찬가지로 분리해서 구현하겠습니다!

  • 피그마 보고 타이포 다른 부분 체크 해서 고쳐놓았습니다..!

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 일정 관리 API 통합(월별 조회/상세/생성/수정/삭제) 및 월 이동 네비게이터 추가
    • 클라이언트 측 로그아웃 훅 및 제어되는 일정 탭 UI 추가
    • 위치 아이콘 추가
  • 버그 수정

    • 일정 식별자·시간 필드 표준화 및 목록 키 개선
    • 서버 오류 코드에 따른 사용자 메시지 매핑 추가
  • 스타일

    • 타이포그래피 조정 및 팝오버/드롭다운 z-index 개선
  • Refactor

    • 일정 폼/모달 재구성, 입력 검증 강화(길이 제한·실시간 카운트)

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6cc50cd9-ced8-46ac-b203-671df61cc815

📥 Commits

Reviewing files that changed from the base of the PR and between cf0b750 and 6b2e877.

📒 Files selected for processing (3)
  • src/components/admin/schedule/general/SchedulePageContent.tsx
  • src/components/admin/schedule/modal/EditScheduleModal.tsx
  • src/hooks/queries/admin/useAdminScheduleQueries.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/hooks/queries/admin/useAdminScheduleQueries.ts
  • src/components/admin/schedule/modal/EditScheduleModal.tsx
  • src/components/admin/schedule/general/SchedulePageContent.tsx

📝 Walkthrough

Walkthrough

관리자 일정 기능이 정적 데이터에서 API 기반으로 전환되었고, 일정 타입/필드 명세가 변경되며 폼 검증, 길이 제한, 모달 제어 로직 및 클라이언트 로그아웃 훅이 추가되었습니다.

Changes

Cohort / File(s) Summary
CSS / 타이포그래피
src/app/globals.css
관리자 오버라이드에 --sub3-weight 추가 및 --font-weight--sub3-weight 매핑 변경.
아이콘
src/assets/icons/index.ts
LocationIcon 재내보내기 추가.
관리자 UI 타이포그래피 교체
src/components/admin/CardinalDropdown.tsx, src/components/admin/layout/NavItem.tsx, src/components/admin/schedule/general/MonthNavigator.tsx, src/components/admin/schedule/general/ScheduleFormField.tsx, src/components/ui/DateTimeInput.tsx
다수 컴포넌트의 typography 클래스 교체(예: typo-sub2typo-sub3, typo-button1 사용 등).
헤더·내비게이션 로그아웃 전환
src/components/admin/layout/Header.tsx, src/components/layout/header/MobileNavSheet.tsx, src/components/mypage/MyPageDropdownMenu.tsx, src/hooks/useLogout.ts, src/hooks/index.ts
서버 폼 제출에서 클라이언트 useLogout 훅 호출로 변경, 마크업/핸들러 조정 및 훅 재내보내기 추가.
스케줄 타입·타입정의 변경
src/types/admin/schedule.d.ts, src/constants/admin/schedule.constants.ts
ScheduleType 키 변경(GENERALEVENT), Schedule 필드 리네이밍(scheduleIdid, startDateTimestart 등), 오류 메시지 상수 추가.
API 클라이언트 및 쿼리 훅 추가
src/lib/apis/adminSchedule.ts, src/lib/apis/index.ts, src/hooks/queries/admin/useAdminScheduleQueries.ts
adminScheduleApi 추가(월간/상세/생성/수정/삭제) 및 React Query 훅들(월간 조회, 상세 suspense, create/update/delete) 구현. 에러 코드→메시지 매핑 및 캐시 무효화 포함.
스케줄 폼 유틸·검증
src/utils/admin/scheduleFormUtils.ts
SCHEDULE_FIELD_LIMITS와 제목/위치/내용 검증 함수 추가, toInitialScheduleFormScheduleDetail 기반으로 변경.
폼 컴포넌트 개선
src/components/admin/schedule/general/ScheduleTextField.tsx, src/components/admin/schedule/general/ScheduleTextareaField.tsx, src/components/admin/schedule/modal/ScheduleFormBody.tsx
optional maxLength 지원, 문자 수 표시, 날짜 범위 검증 및 inline 오류 표시, 일부 레이아웃 클래스 조정.
스케줄 목록·아이템 변경
src/components/admin/schedule/general/ScheduleList.tsx, src/components/admin/schedule/general/ScheduleItem.tsx
리스트 keyschedule.id로 변경. 날짜/시간 파생에 start 사용.
모달·생성·편집 흐름 리팩터
src/components/admin/schedule/general/SchedulePageContent.tsx, src/components/admin/schedule/modal/CreateScheduleModal.tsx, src/components/admin/schedule/modal/CreateGeneralScheduleForm.tsx, src/components/admin/schedule/modal/CreateSessionScheduleForm.tsx, src/components/admin/schedule/modal/EditScheduleModal.tsx, src/components/admin/schedule/modal/EditSessionModal.tsx, src/components/admin/index.ts
SchedulePageContentuseAdminMonthlySchedules 사용으로 전환, 생성/편집/삭제 뮤테이션 사용으로 수정, CreateModal이 제어형(activeTab/onActiveTabChange)으로 변경, 편집 모달은 비동기 상세 로딩·검증·저장 뮤테이션 적용. ModalIconButton 추가 및 사용, 일부 모듈 경로 조정.
팝오버/드롭다운 레이어링
src/components/alert/CustomAlertDialog.tsx, src/components/ui/CalendarPicker.tsx, src/components/ui/DropdownMenu.tsx, src/components/ui/TimePicker.tsx
팝오버/드롭다운 z-index 유틸리티 상향(z-50z-90).
기타 UI 조정
src/components/admin/schedule/general/ScheduleTag.tsx, src/components/admin/schedule/modal/SessionScheduleForm.tsx
PinIconLocationIcon 교체, 드롭다운 폭 및 import 경로/정리 변경.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant UI as SchedulePageContent
    participant Modal as CreateScheduleModal
    participant Hooks as useAdminMonthlySchedules / useCreateSchedule
    participant API as adminScheduleApi
    participant Toast as ToastNotification

    User->>UI: 페이지 로드 (년/월)
    UI->>Hooks: useAdminMonthlySchedules(year, month)
    Hooks->>API: getMonthly(clubId, start, end)
    API-->>Hooks: schedules[]
    Hooks-->>UI: schedules 제공
    User->>Modal: 일정 생성 클릭 (activeTab 제어)
    Modal->>User: 모달 열림
    User->>Modal: 폼 입력 후 제출
    Modal->>Hooks: useCreateSchedule.mutate(body)
    Hooks->>API: createEvent(clubId, body)
    API-->>Hooks: 성공 응답
    Hooks->>Toast: 성공 알림
    Hooks->>Hooks: invalidate(['admin','schedules'])
    Hooks->>API: getMonthly(...) 재요청
    API-->>Hooks: 갱신된 schedules[]
    Hooks-->>UI: 목록 갱신
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

✨ Feature

Suggested reviewers

  • nabbang6
  • dalzzy
  • woneeeee

Poem

🐰 새 달력에 API 바람 불어와,
제목 길이 재고 글자수 센다네,
모달은 부모가 탭을 지휘하고,
로그아웃은 훅으로 살포시,
당근 들고 관리자 춤추네 🥕✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed 제목은 PR의 주요 변경사항(어드민 일정 API 연결)을 명확하고 간결하게 요약하고 있습니다.
Description check ✅ Passed 설명은 PR 타입, 관련 이슈, 주요 변경사항을 포함하고 있으나 스크린샷/실행영상 섹션이 비어있습니다.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/WTH-306-어드민-일정-api-연결

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 and usage tips.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/components/admin/CardinalDropdown.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/layout/Header.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/layout/NavItem.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/schedule/general/MonthNavigator.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/schedule/general/ScheduleFormField.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-hgqg6glih-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

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

⚠️ Outside diff range comments (2)
src/components/admin/schedule/modal/EditSessionModal.tsx (1)

60-68: ⚠️ Potential issue | 🟡 Minor

비반복 세션 저장 경로에서 onSave가 호출되지 않음 (TODO).

비반복 세션인 경우 handleClose()만 호출되고 onSave?.(...)가 호출되지 않아, 사용자가 "저장"을 눌러도 실제로 저장 액션이 상위에 전달되지 않습니다. PR 설명에 "세션 수정 API 미구현"으로 명시되어 있으나, API 연동 전이라도 비반복 분기에서 onSave?.('this')를 호출해 상위 훅/모의 처리에 신호를 보내 흐름을 일관되게 두는 것을 고려해 보세요.

원하시면 API 연동이 완료된 이후로 대체 가능하도록 최소한의 onSave?.('this') 호출을 추가한 패치를 만들어 드릴까요?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/schedule/modal/EditSessionModal.tsx` around lines 60 -
68, The non-recurring save path in handleSubmit doesn't notify the parent
because onSave is not called; update the else branch of handleSubmit to call
onSave (using the component's onSave prop) with the appropriate payload (e.g.,
the current form or identifier you use elsewhere) before closing the modal,
ensuring you still validate via isScheduleTitleValid(form.title) and keep the
recurring branch behavior (setSaveConfirmOpen) unchanged; reference
handleSubmit, onSave, form.title, isRecurring and handleClose to locate and
update the logic.
src/components/admin/schedule/general/SchedulePageContent.tsx (1)

162-168: ⚠️ Potential issue | 🟠 Major

onCreateSession 콜백이 전달되지 않아 세션 생성 기능이 작동하지 않습니다.

CreateScheduleModalonCreateSession prop을 올바르게 CreateSessionScheduleForm으로 전달하고 있으나, SchedulePageContent에서 CreateScheduleModal을 렌더링할 때 이 prop을 전달하지 않고 있습니다. 결과적으로 "세션" 탭에서 제출 시 onCreateSession?.(body)는 조용히 아무것도 하지 않아 세션이 생성되지 않습니다. 세션 생성 핸들러를 연결해야 합니다.

제안 수정
+import { useCreateSchedule } from '@/hooks/queries/admin/useAdminScheduleQueries';
...
+  const { mutate: createSchedule } = useCreateSchedule();
...
       <CreateScheduleModal
         open={createModalOpen}
         onOpenChange={setCreateModalOpen}
         cardinalNumber={activeCardinal?.cardinalNumber ?? null}
         activeTab={createModalTab}
         onActiveTabChange={setCreateModalTab}
+        onCreateSession={(body) => {
+          createSchedule(body, { onSuccess: () => setCreateModalOpen(false) });
+        }}
       />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/schedule/general/SchedulePageContent.tsx` around lines
162 - 168, CreateScheduleModal is missing the onCreateSession prop so session
creation is never invoked; pass the page's session creation handler (e.g., the
existing createSession or handleCreateSession function in SchedulePageContent)
into CreateScheduleModal as onCreateSession={createSessionHandler} (use the
exact handler name used in this file) so the modal forwards it to
CreateSessionScheduleForm and session submissions call that handler.
🧹 Nitpick comments (9)
src/constants/admin/schedule.constants.ts (1)

8-10: 에러 코드 상수를 명명된 유니온 타입으로 좁히는 것을 고려

현재 Record<number, string>이라 임의의 숫자 키가 허용됩니다. 에러 코드가 증가할수록 허용되는 값 집합을 타입으로 제약해 두면 호출부에서 오타/누락을 조기에 잡을 수 있습니다. 지금은 코드가 1개뿐이므로 선택 사항입니다.

♻️ 제안
-export const SCHEDULE_ERROR_MESSAGE: Record<number, string> = {
+export type ScheduleErrorCode = 20800;
+
+export const SCHEDULE_ERROR_MESSAGE: Record<ScheduleErrorCode, string> = {
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/constants/admin/schedule.constants.ts` around lines 8 - 10,
SCHEDULE_ERROR_MESSAGE is typed as Record<number,string>, allowing any numeric
key; narrow it by introducing a named union or enum for allowed error codes
(e.g., type ScheduleErrorCode = 20800 or enum ScheduleErrorCode { NotFound =
20800 }) and change the declaration to use Record<ScheduleErrorCode, string> (or
Partial<Record<ScheduleErrorCode,string>> if keys may be missing), then update
usages that index SCHEDULE_ERROR_MESSAGE to use the ScheduleErrorCode symbol (or
cast) so callers get compile-time checking; reference SCHEDULE_ERROR_MESSAGE and
the new ScheduleErrorCode/enum when making the changes.
src/utils/admin/scheduleFormUtils.ts (1)

12-14: 제목 길이 검증 시 trim 일관성 확인

title.trim().length > 0로 공백 전용 입력은 거부하면서, 최대 길이는 trim 전 title.length로 비교하고 있습니다. 예를 들어 유효 문자 30자 + 뒤쪽 공백이 붙은 경우 실제 표시/저장되는 제목은 30자 이내이지만 검증은 실패합니다. 일관되게 trim 후 길이로 비교하거나, maxLength가 입력 단계에서 잘리는 규칙을 명확히 정리해두면 edge case를 줄일 수 있습니다.

♻️ 제안
 export function isScheduleTitleValid(title: string): boolean {
-  return title.trim().length > 0 && title.length <= SCHEDULE_FIELD_LIMITS.title;
+  const trimmed = title.trim();
+  return trimmed.length > 0 && trimmed.length <= SCHEDULE_FIELD_LIMITS.title;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/admin/scheduleFormUtils.ts` around lines 12 - 14, The validation
for schedule titles uses trimmed length for emptiness but untrimmed length for
the max limit, causing false negatives when trailing/leading spaces push the raw
length over the limit; update isScheduleTitleValid to compute a trimmed string
(e.g., const trimmed = title.trim()) and use trimmed.length for both the
non-empty check and comparison against SCHEDULE_FIELD_LIMITS.title, and ensure
any related callers/storage expectations align with the trimmed behavior
(function: isScheduleTitleValid, constant: SCHEDULE_FIELD_LIMITS.title).
src/hooks/queries/admin/useAdminScheduleQueries.ts (2)

42-45: 동일한 onError 핸들러 중복 — 헬퍼로 추출 권장

세 mutation에서 axios 에러 → 코드 → 토스트 변환 로직이 동일하게 반복됩니다. 모듈 내부 헬퍼로 뽑아두면 향후 에러 코드 확장 시 한 곳에서 관리할 수 있고, SCHEDULE_ERROR_MESSAGE[code] 자체가 미존재 키에 대해 undefined를 반환하므로 ?? undefined도 생략 가능합니다.

♻️ 제안
+function handleScheduleError(error: unknown) {
+  const code = isAxiosError(error) ? error.response?.data?.code : undefined;
+  toastError(code != null ? SCHEDULE_ERROR_MESSAGE[code] : undefined);
+}
@@
-    onError: (error) => {
-      const code = isAxiosError(error) ? error.response?.data?.code : undefined;
-      toastError(code ? (SCHEDULE_ERROR_MESSAGE[code] ?? undefined) : undefined);
-    },
+    onError: handleScheduleError,

Also applies to: 59-62, 75-78

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/queries/admin/useAdminScheduleQueries.ts` around lines 42 - 45,
Extract the duplicated onError logic into a single module-level helper (e.g.,
handleScheduleError) that takes the caught error, uses isAxiosError to read
response?.data?.code, looks up SCHEDULE_ERROR_MESSAGE[code] (no need for "??
undefined"), and calls toastError with the resolved message; then replace the
inline onError blocks in the three mutations with onError: handleScheduleError
to centralize error-to-toast mapping and make future message additions one place
(reference symbols: isAxiosError, SCHEDULE_ERROR_MESSAGE, toastError, and the
three onError handlers in useAdminScheduleQueries).

38-38: clubId! non-null 단언 대비 가드 필요

useAdminMonthlySchedulesenabled: !!clubId로 보호되지만, 세 개 mutation은 clubId가 없을 때 호출되면 clubId! 단언이 런타임에 undefined를 URL에 끼워 넣어 잘못된 요청이 나갈 수 있습니다. UI에서 호출 지점이 보장되더라도, mutation 시작 시 clubId를 확인하고 조기 반환(또는 에러 throw)하는 가드를 추가해두면 회귀에 안전합니다.

♻️ 예시
-    mutationFn: (body: CreateEventBody) => adminScheduleApi.createEvent(clubId!, body),
+    mutationFn: (body: CreateEventBody) => {
+      if (!clubId) throw new Error('clubId is not set');
+      return adminScheduleApi.createEvent(clubId, body);
+    },

Also applies to: 55-55, 71-71

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/queries/admin/useAdminScheduleQueries.ts` at line 38, Add a guard
inside each mutationFn that currently uses the non-null asserted clubId (the
mutationFn for adminScheduleApi.createEvent, adminScheduleApi.updateEvent, and
adminScheduleApi.deleteEvent) to check if clubId is present before calling the
API; if clubId is missing, short-circuit by throwing or returning a rejected
Promise with a clear error (e.g., "Missing clubId") so you never call
adminScheduleApi.* with undefined in the URL.
src/components/layout/header/MobileNavSheet.tsx (1)

47-47: 임의값 h-[calc(100dvh-64px)] 정리 고려

PR 목표에서 "arbitrary 값을 토큰 클래스로 변환"이라고 명시되어 있는데, 해당 줄에는 여전히 하드코딩된 64px가 임의값으로 남아 있습니다. top-16(= 4rem = 64px)과 암묵적으로 결합되어 있어, 추후 헤더 높이 변경 시 두 값이 어긋날 위험이 있습니다. 전역 CSS 변수(예: --header-height)로 추출하거나 사용자 정의 유틸리티로 분리하는 것을 권장합니다.

As per coding guidelines: "Always use design token classes first; no hardcoded values. Ask user before adding new tokens".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/layout/header/MobileNavSheet.tsx` at line 47, The hardcoded
height h-[calc(100dvh-64px)] in MobileNavSheet.tsx (paired with top-16) should
be replaced with a design-token-based value to avoid divergence when header
height changes; update the class to compute height from a shared header height
token/variable (e.g., use a CSS variable like --header-height or a predefined
token utility instead of 64px) and coordinate with the team before introducing a
new token/utility so top-16 and the height remain consistent.
src/components/admin/schedule/modal/ScheduleFormBody.tsx (1)

49-53: 에러 메시지에 role="alert" 또는 aria-live 고려 (선택)

날짜 범위 에러 메시지가 입력 변경에 따라 동적으로 나타나지만, 스크린 리더에는 알림이 전달되지 않습니다. 접근성 향상을 위해 role="alert" 또는 aria-live="polite"를 추가하는 것을 권장합니다.

♿ 제안 diff
-        {!isDateRangeValid(form) && (
-          <span className="typo-caption2 text-state-error px-400 pt-200">
+        {!isDateRangeValid(form) && (
+          <span role="alert" className="typo-caption2 text-state-error px-400 pt-200">
             종료 일시는 시작 일시보다 이후여야 합니다.
           </span>
         )}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/schedule/modal/ScheduleFormBody.tsx` around lines 49 -
53, The date-range error <span> in ScheduleFormBody (the conditional that
renders when !isDateRangeValid(form)) isn’t announced to screen readers; update
that element to include an accessible live region by adding role="alert" or
aria-live="polite" (and keep the same className and message) so assistive tech
receives dynamic updates when the validation message appears; ensure you only
modify the span rendered by the ScheduleFormBody's !isDateRangeValid(form)
branch.
src/components/admin/layout/Header.tsx (1)

1-56: 주석 처리된 PAGE_METADATA/메타데이터 렌더링 블록 정리 필요

135줄의 PAGE_METADATA 상수와 42, 44, 4856줄의 관련 렌더링 로직이 전부 주석 처리되어 데드 코드로 남아 있습니다. 유지한다면 TODO 코멘트로 복구 사유와 일정을 명시해 주고, 당분간 복구 계획이 없다면 버전 관리에 맡기고 삭제하는 것을 권장합니다. 현재 상태에서는 관리자 헤더의 페이지별 타이틀/설명이 완전히 사라지게 되는데, 이것이 이번 PR 범위에서 의도된 동작인지 확인이 필요합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/layout/Header.tsx` around lines 1 - 56, The commented
PAGE_METADATA constant and the commented rendering logic inside the Header
component (references: PAGE_METADATA, metadata, pathname/usePathname) are dead
code—either remove these commented blocks entirely or add a clear TODO comment
above PAGE_METADATA explaining why it’s commented, who will restore it, and a
target date; if you keep it, also add a short comment in Header noting that per
PR scope the per-page title/description rendering is intentionally disabled.
Ensure the Header component’s current behavior is explicitly documented in a
one-line comment so reviewers know this change is intentional.
src/components/admin/schedule/modal/CreateGeneralScheduleForm.tsx (1)

63-63: max-h-175 px-15 커스텀 스페이싱 값 검토

max-h-175, px-15는 프로젝트 스페이싱 토큰 스케일(p-100~500) 범위 밖 값입니다. Tailwind v4의 숫자 스페이싱이 동작하더라도 디자인 토큰 정합성 측면에서는 토큰 값으로 정규화하거나, 필요한 경우 디자인 시스템에 새 토큰 추가를 논의하는 것이 좋겠습니다.

As per coding guidelines: "Always use design token classes first; no hardcoded values. Ask user before adding new tokens".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/schedule/modal/CreateGeneralScheduleForm.tsx` at line
63, The div in CreateGeneralScheduleForm.tsx uses hardcoded spacing classes
"max-h-175" and "px-15" which are outside the project’s design token scale;
replace those with the nearest existing token classes (e.g., use the appropriate
token-based max-height and padding classes from the p-100..p-500 scale) or, if
no suitable token exists, raise a proposal to add new design tokens and get
approval before adding them; ensure the className on the same element
("scrollbar-custom ... overflow-y-auto") is updated to use only approved token
classes and confirm with the designer/PM before introducing new token names.
src/components/admin/index.ts (1)

3-3: ModalIconButton를 공유 컴포넌트로 이동할 것을 검토하세요

ModalIconButton은 schedule과 board 두 개 이상의 기능 영역에서 사용되는 범용 컴포넌트임이 확인되었습니다. 그런데 현재는 src/components/admin/modal/ModalIconButton.tsx에 위치하고 있어, feature별 modal 폴더(member/modal, schedule/modal, board/modal)와 구조적으로 일치하지 않습니다. 추가로 임포트 경로도 불일치합니다:

  • EditScheduleModal, EditSessionModal은 barrel(@/components/admin)에서 임포트
  • TrashBoardModal, BoardFormHeader는 직접 경로(@/components/admin/modal/ModalIconButton)에서 임포트

구조 일관성을 위해 이 컴포넌트를 src/components/ui/ 또는 공유 유틸리티 위치로 이동하고, 모든 임포트 경로를 일원화하는 것을 추천합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/index.ts` at line 3, Move the ModalIconButton component
out of the admin feature and into a shared UI location (e.g. src/components/ui/)
and update all exports/imports to use the unified path; specifically, relocate
the file modal/ModalIconButton.tsx to the shared folder, export ModalIconButton
and ModalIconButtonProps from the shared barrel, update the existing re-export
in src/components/admin/index.ts to point to the new shared export (or remove it
if admin barrel should not re-export shared UI), and change all consumers (e.g.,
EditScheduleModal, EditSessionModal, TrashBoardModal, BoardFormHeader) to import
ModalIconButton from the new shared barrel so all import paths are consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/app/globals.css`:
- Line 215: The typo-sub3 utility currently hardcodes font-weight:
var(--font-weight-semibold) so your [data-admin] override of --sub3-weight has
no effect; update the `@utility` typo-sub3 rule to use font-weight:
var(--sub3-weight) (instead of var(--font-weight-semibold)) and add a default
--sub3-weight variable to :root (e.g., set it to var(--font-weight-semibold)) so
non-admin themes keep the same appearance while [data-admin] overrides apply.

In `@src/components/admin/layout/Header.tsx`:
- Line 47: In the Header component's header element update the className to use
the design spacing token instead of Tailwind's pt-2 (e.g., replace "pt-2" with
the appropriate token like "pt-100" or "pt-200" to match the intended padding)
and add a border width utility (for example "border-b") so the existing
"border-line" color token actually renders a visible border; locate the header
JSX in Header.tsx and modify the className string on the header element
accordingly.

In `@src/components/admin/schedule/general/SchedulePageContent.tsx`:
- Around line 87-89: handleDelete currently just calls
deleteSchedule(schedule.id) and never clears editTarget or surfaces errors;
update handleDelete to await the deleteSchedule call, on success set editTarget
to null (so EditScheduleModal/EditSessionModal closes) and on failure catch the
error and show user feedback (e.g., set an error state or call the existing
notification/toast helper). Apply the same pattern to the other delete
handler(s) in the file (the block around lines 171-201) so modals close only on
successful deletion and failures are reported to the user.
- Around line 184-201: The EditSessionModal call hardcodes target.status to
'SCHEDULED' which ignores the AdminSession's actual SessionStatus; update the
target object passed to EditSessionModal to use the session's real status (e.g.,
target.status = editTarget.status) and ensure types align with SessionStatus/
AdminSession (add a safe fallback like editTarget.status ?? 'SCHEDULED' if
needed). Keep the rest of the props (id, cardinal, title, start, end) the same
and still call handleDelete(editTarget) for onDelete.

In `@src/hooks/useLogout.ts`:
- Around line 4-11: The current useLogout implementation calls reset() before
awaiting logoutAction(), risking client-server state mismatch and no user
feedback on failure; change useLogout so it awaits logoutAction() first, wraps
the call in try/catch, only calls reset() on successful logout, and surface
errors (return a boolean or throw a descriptive error) so callers (onClick
handlers) can show feedback—update callers to await the returned promise and
handle rejection/false to show an error to the user.

In `@src/types/admin/schedule.d.ts`:
- Around line 3-11: The Schedule type is missing the content field which
CreateEventBody and UpdateEventBody include; add a content?: string (or
appropriate type) property to the Schedule interface so fetched events can
prefill the edit modal without casting (update the Schedule interface
declaration and ensure any code expecting schedule.content handles optionality),
and open or reference an issue to track adding/confirming this field in the API
spec if not finalized.

---

Outside diff comments:
In `@src/components/admin/schedule/general/SchedulePageContent.tsx`:
- Around line 162-168: CreateScheduleModal is missing the onCreateSession prop
so session creation is never invoked; pass the page's session creation handler
(e.g., the existing createSession or handleCreateSession function in
SchedulePageContent) into CreateScheduleModal as
onCreateSession={createSessionHandler} (use the exact handler name used in this
file) so the modal forwards it to CreateSessionScheduleForm and session
submissions call that handler.

In `@src/components/admin/schedule/modal/EditSessionModal.tsx`:
- Around line 60-68: The non-recurring save path in handleSubmit doesn't notify
the parent because onSave is not called; update the else branch of handleSubmit
to call onSave (using the component's onSave prop) with the appropriate payload
(e.g., the current form or identifier you use elsewhere) before closing the
modal, ensuring you still validate via isScheduleTitleValid(form.title) and keep
the recurring branch behavior (setSaveConfirmOpen) unchanged; reference
handleSubmit, onSave, form.title, isRecurring and handleClose to locate and
update the logic.

---

Nitpick comments:
In `@src/components/admin/index.ts`:
- Line 3: Move the ModalIconButton component out of the admin feature and into a
shared UI location (e.g. src/components/ui/) and update all exports/imports to
use the unified path; specifically, relocate the file modal/ModalIconButton.tsx
to the shared folder, export ModalIconButton and ModalIconButtonProps from the
shared barrel, update the existing re-export in src/components/admin/index.ts to
point to the new shared export (or remove it if admin barrel should not
re-export shared UI), and change all consumers (e.g., EditScheduleModal,
EditSessionModal, TrashBoardModal, BoardFormHeader) to import ModalIconButton
from the new shared barrel so all import paths are consistent.

In `@src/components/admin/layout/Header.tsx`:
- Around line 1-56: The commented PAGE_METADATA constant and the commented
rendering logic inside the Header component (references: PAGE_METADATA,
metadata, pathname/usePathname) are dead code—either remove these commented
blocks entirely or add a clear TODO comment above PAGE_METADATA explaining why
it’s commented, who will restore it, and a target date; if you keep it, also add
a short comment in Header noting that per PR scope the per-page
title/description rendering is intentionally disabled. Ensure the Header
component’s current behavior is explicitly documented in a one-line comment so
reviewers know this change is intentional.

In `@src/components/admin/schedule/modal/CreateGeneralScheduleForm.tsx`:
- Line 63: The div in CreateGeneralScheduleForm.tsx uses hardcoded spacing
classes "max-h-175" and "px-15" which are outside the project’s design token
scale; replace those with the nearest existing token classes (e.g., use the
appropriate token-based max-height and padding classes from the p-100..p-500
scale) or, if no suitable token exists, raise a proposal to add new design
tokens and get approval before adding them; ensure the className on the same
element ("scrollbar-custom ... overflow-y-auto") is updated to use only approved
token classes and confirm with the designer/PM before introducing new token
names.

In `@src/components/admin/schedule/modal/ScheduleFormBody.tsx`:
- Around line 49-53: The date-range error <span> in ScheduleFormBody (the
conditional that renders when !isDateRangeValid(form)) isn’t announced to screen
readers; update that element to include an accessible live region by adding
role="alert" or aria-live="polite" (and keep the same className and message) so
assistive tech receives dynamic updates when the validation message appears;
ensure you only modify the span rendered by the ScheduleFormBody's
!isDateRangeValid(form) branch.

In `@src/components/layout/header/MobileNavSheet.tsx`:
- Line 47: The hardcoded height h-[calc(100dvh-64px)] in MobileNavSheet.tsx
(paired with top-16) should be replaced with a design-token-based value to avoid
divergence when header height changes; update the class to compute height from a
shared header height token/variable (e.g., use a CSS variable like
--header-height or a predefined token utility instead of 64px) and coordinate
with the team before introducing a new token/utility so top-16 and the height
remain consistent.

In `@src/constants/admin/schedule.constants.ts`:
- Around line 8-10: SCHEDULE_ERROR_MESSAGE is typed as Record<number,string>,
allowing any numeric key; narrow it by introducing a named union or enum for
allowed error codes (e.g., type ScheduleErrorCode = 20800 or enum
ScheduleErrorCode { NotFound = 20800 }) and change the declaration to use
Record<ScheduleErrorCode, string> (or Partial<Record<ScheduleErrorCode,string>>
if keys may be missing), then update usages that index SCHEDULE_ERROR_MESSAGE to
use the ScheduleErrorCode symbol (or cast) so callers get compile-time checking;
reference SCHEDULE_ERROR_MESSAGE and the new ScheduleErrorCode/enum when making
the changes.

In `@src/hooks/queries/admin/useAdminScheduleQueries.ts`:
- Around line 42-45: Extract the duplicated onError logic into a single
module-level helper (e.g., handleScheduleError) that takes the caught error,
uses isAxiosError to read response?.data?.code, looks up
SCHEDULE_ERROR_MESSAGE[code] (no need for "?? undefined"), and calls toastError
with the resolved message; then replace the inline onError blocks in the three
mutations with onError: handleScheduleError to centralize error-to-toast mapping
and make future message additions one place (reference symbols: isAxiosError,
SCHEDULE_ERROR_MESSAGE, toastError, and the three onError handlers in
useAdminScheduleQueries).
- Line 38: Add a guard inside each mutationFn that currently uses the non-null
asserted clubId (the mutationFn for adminScheduleApi.createEvent,
adminScheduleApi.updateEvent, and adminScheduleApi.deleteEvent) to check if
clubId is present before calling the API; if clubId is missing, short-circuit by
throwing or returning a rejected Promise with a clear error (e.g., "Missing
clubId") so you never call adminScheduleApi.* with undefined in the URL.

In `@src/utils/admin/scheduleFormUtils.ts`:
- Around line 12-14: The validation for schedule titles uses trimmed length for
emptiness but untrimmed length for the max limit, causing false negatives when
trailing/leading spaces push the raw length over the limit; update
isScheduleTitleValid to compute a trimmed string (e.g., const trimmed =
title.trim()) and use trimmed.length for both the non-empty check and comparison
against SCHEDULE_FIELD_LIMITS.title, and ensure any related callers/storage
expectations align with the trimmed behavior (function: isScheduleTitleValid,
constant: SCHEDULE_FIELD_LIMITS.title).
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b5ddc768-f183-4b26-8ba3-510730200fc0

📥 Commits

Reviewing files that changed from the base of the PR and between ebf6da0 and 5c13e13.

⛔ Files ignored due to path filters (1)
  • src/assets/icons/location.svg is excluded by !**/*.svg
📒 Files selected for processing (36)
  • src/app/globals.css
  • src/assets/icons/index.ts
  • src/components/admin/CardinalDropdown.tsx
  • src/components/admin/index.ts
  • src/components/admin/layout/Header.tsx
  • src/components/admin/layout/NavItem.tsx
  • src/components/admin/schedule/general/MonthNavigator.tsx
  • src/components/admin/schedule/general/ScheduleFormField.tsx
  • src/components/admin/schedule/general/ScheduleItem.tsx
  • src/components/admin/schedule/general/ScheduleList.tsx
  • src/components/admin/schedule/general/SchedulePageContent.tsx
  • src/components/admin/schedule/general/ScheduleTag.tsx
  • src/components/admin/schedule/general/ScheduleTextField.tsx
  • src/components/admin/schedule/general/ScheduleTextareaField.tsx
  • src/components/admin/schedule/modal/CreateGeneralScheduleForm.tsx
  • src/components/admin/schedule/modal/CreateScheduleModal.tsx
  • src/components/admin/schedule/modal/CreateSessionScheduleForm.tsx
  • src/components/admin/schedule/modal/EditScheduleModal.tsx
  • src/components/admin/schedule/modal/EditSessionModal.tsx
  • src/components/admin/schedule/modal/ScheduleFormBody.tsx
  • src/components/admin/schedule/modal/SessionScheduleForm.tsx
  • src/components/alert/CustomAlertDialog.tsx
  • src/components/layout/header/MobileNavSheet.tsx
  • src/components/mypage/MyPageDropdownMenu.tsx
  • src/components/ui/CalendarPicker.tsx
  • src/components/ui/DateTimeInput.tsx
  • src/components/ui/DropdownMenu.tsx
  • src/components/ui/TimePicker.tsx
  • src/constants/admin/schedule.constants.ts
  • src/hooks/index.ts
  • src/hooks/queries/admin/useAdminScheduleQueries.ts
  • src/hooks/useLogout.ts
  • src/lib/apis/adminSchedule.ts
  • src/lib/apis/index.ts
  • src/types/admin/schedule.d.ts
  • src/utils/admin/scheduleFormUtils.ts

Comment thread src/app/globals.css Outdated
Comment thread src/components/admin/layout/Header.tsx
Comment thread src/components/admin/schedule/general/SchedulePageContent.tsx
Comment thread src/components/admin/schedule/general/SchedulePageContent.tsx
Comment thread src/hooks/useLogout.ts
Comment on lines +4 to +11
export function useLogout() {
const { reset } = useClubActions();

return async () => {
reset();
await logoutAction();
};
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# logoutAction 구현과 호출 방식 확인
fd -t f 'auth' src/lib/actions --exec cat {}
echo "---"
rg -nP -C3 '\buseLogout\s*\(' --type=ts --type=tsx

Repository: Team-Weeth/weeth-client

Length of output: 1593


🏁 Script executed:

rg -n -C3 'useLogout' --type=ts --type=tsx src/

Repository: Team-Weeth/weeth-client

Length of output: 94


🏁 Script executed:

rg -n 'Header|MobileNavSheet|MyPageDropdownMenu' --type=ts --type=tsx src/ | grep -E '\.(ts|tsx):' | head -20

Repository: Team-Weeth/weeth-client

Length of output: 94


🏁 Script executed:

rg -n 'useLogout' src/

Repository: Team-Weeth/weeth-client

Length of output: 684


🏁 Script executed:

fd -type f \( -name 'Header*' -o -name 'MobileNavSheet*' -o -name 'MyPageDropdownMenu*' \) src/

Repository: Team-Weeth/weeth-client

Length of output: 237


🏁 Script executed:

sed -n '20,35p' src/components/mypage/MyPageDropdownMenu.tsx

Repository: Team-Weeth/weeth-client

Length of output: 647


🏁 Script executed:

sed -n '20,35p' src/components/layout/header/MobileNavSheet.tsx

Repository: Team-Weeth/weeth-client

Length of output: 719


🏁 Script executed:

sed -n '38,55p' src/components/admin/layout/Header.tsx

Repository: Team-Weeth/weeth-client

Length of output: 751


🏁 Script executed:

rg -n 'handleLogout\(' src/components/mypage/MyPageDropdownMenu.tsx -A2 -B2

Repository: Team-Weeth/weeth-client

Length of output: 49


🏁 Script executed:

rg -n 'handleLogout\(' src/components/layout/header/MobileNavSheet.tsx -A2 -B2

Repository: Team-Weeth/weeth-client

Length of output: 49


🏁 Script executed:

rg -n 'handleLogout\(' src/components/admin/layout/Header.tsx -A2 -B2

Repository: Team-Weeth/weeth-client

Length of output: 49


🏁 Script executed:

rg -n 'handleLogout' src/components/mypage/MyPageDropdownMenu.tsx -B1 -A3

Repository: Team-Weeth/weeth-client

Length of output: 371


🏁 Script executed:

rg -n 'handleLogout' src/components/layout/header/MobileNavSheet.tsx -B1 -A3

Repository: Team-Weeth/weeth-client

Length of output: 538


🏁 Script executed:

rg -n 'handleLogout' src/components/admin/layout/Header.tsx -B1 -A3

Repository: Team-Weeth/weeth-client

Length of output: 561


🏁 Script executed:

rg -n 'AlertDialogAction|onClick.*handleLogout' src/ -B2 -A2 | head -40

Repository: Team-Weeth/weeth-client

Length of output: 2385


🏁 Script executed:

cat -n src/components/mypage/MyPageDropdownMenu.tsx | sed -n '60,75p'

Repository: Team-Weeth/weeth-client

Length of output: 564


로그아웃 실패 시 상태 불일치 가능성

reset()이 먼저 동기적으로 실행된 후 logoutAction()을 await합니다. 만약 서버 측 로그아웃이 실패(네트워크/쿠키 삭제 오류)하면, 클라이언트 스토어는 초기화됐지만 인증 세션은 남아있는 불일치 상태가 됩니다. 또한 에러 피드백이 없어 사용자는 로그아웃 성공 여부를 알 수 없습니다.

일반적으로 서버 로그아웃을 먼저 수행하고 성공 시에만 클라이언트 상태를 정리하는 편이 안전합니다. 현재 onClick 핸들러들도 비동기 콜백의 실패를 처리하지 않고 있으므로 에러 처리 추가가 필요합니다.

🔧 제안: 서버 로그아웃 선행 + 에러 처리
 import { logoutAction } from '@/lib/actions/auth';
 import { useClubActions } from '@/stores/useClubStore';

 export function useLogout() {
   const { reset } = useClubActions();

   return async () => {
-    reset();
-    await logoutAction();
+    try {
+      await logoutAction();
+      reset();
+    } catch (error) {
+      // TODO: 사용자에게 에러 알림 (toastError 등)
+      console.error('Logout failed', error);
+    }
   };
 }
📝 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
export function useLogout() {
const { reset } = useClubActions();
return async () => {
reset();
await logoutAction();
};
}
import { logoutAction } from '@/lib/actions/auth';
import { useClubActions } from '@/stores/useClubStore';
export function useLogout() {
const { reset } = useClubActions();
return async () => {
try {
await logoutAction();
reset();
} catch (error) {
// TODO: 사용자에게 에러 알림 (toastError 등)
console.error('Logout failed', error);
}
};
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/hooks/useLogout.ts` around lines 4 - 11, The current useLogout
implementation calls reset() before awaiting logoutAction(), risking
client-server state mismatch and no user feedback on failure; change useLogout
so it awaits logoutAction() first, wraps the call in try/catch, only calls
reset() on successful logout, and surface errors (return a boolean or throw a
descriptive error) so callers (onClick handlers) can show feedback—update
callers to await the returned promise and handle rejection/false to show an
error to the user.

Comment on lines 3 to +11
export interface Schedule {
scheduleId: number;
id: number;
title: string;
type: ScheduleType;
startDateTime: string;
endDateTime: string;
start: string;
end: string;
location: string;
cardinalNumber: number;
cardinal: number;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Schedulecontent 필드 누락 — 후속 작업 필요

CreateEventBody/UpdateEventBodycontent를 포함하지만, 조회 응답 타입인 Schedule에는 content가 없습니다. 수정 모달에서 기존 content 프리필이 필요할 경우 타입 캐스팅이 불가피해집니다. PR 설명에서도 언급된 TODO인데, API 스펙 확정 후 추가 반영을 잊지 않도록 별도 이슈로 추적하는 것을 권장합니다.

원하시면 API 스펙 업데이트를 추적할 이슈를 생성해 드릴까요?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/admin/schedule.d.ts` around lines 3 - 11, The Schedule type is
missing the content field which CreateEventBody and UpdateEventBody include; add
a content?: string (or appropriate type) property to the Schedule interface so
fetched events can prefill the edit modal without casting (update the Schedule
interface declaration and ensure any code expecting schedule.content handles
optionality), and open or reference an issue to track adding/confirming this
field in the API spec if not finalized.

@JIN921 JIN921 changed the title [feat] 어드민 일정 API 연결 (#306) [Feat] WTH-306: 어드민 일정 API 연결 Apr 24, 2026
@JIN921 JIN921 requested review from dalzzy, nabbang6 and woneeeee April 24, 2026 02:03
@JIN921 JIN921 self-assigned this Apr 24, 2026
@JIN921 JIN921 added 🐞 BugFix Something isn't working 📬 API 서버 API 통신 🔨 Refactor 코드 리팩토링 labels Apr 24, 2026
Copy link
Copy Markdown
Collaborator

@nabbang6 nabbang6 left a comment

Choose a reason for hiding this comment

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

와아앙 고생하셧습니다!!~!
어드민도 거의 완성되어 가네용... 최고 👍,,,

Comment thread src/components/admin/schedule/general/SchedulePageContent.tsx Outdated
Comment on lines 101 to 102
onCreateSession?.(body);
onClose();
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

요기도 CreateGeneralScheduleFormmutate(body, { onSuccess: onClose });처럼 mutation 성공/실패를 기다린 다음 닫히게 두면 조을 것 같습니당!

Copy link
Copy Markdown
Member

@woneeeee woneeeee left a comment

Choose a reason for hiding this comment

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

수고하셨습니당!! 제가 세션 UI 피알이랑 순간 헷갈려서 동일 피알인줄 알고 이미 본 피알인줄 알았었어요 ㅠㅠㅠㅠ 리뷰가 늦어서 죄송함니당 ㅜㅜㅜㅜ

Comment thread src/hooks/useLogout.ts
Copy link
Copy Markdown
Member

@dalzzy dalzzy left a comment

Choose a reason for hiding this comment

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

확인했습니다!! 리뷰가 늦어 죄송합니닷...
다른 분들이 리뷰 남겨주신 부분만 수정해주시면 될 것 같습니당! 이 외에는 수정할 부분 딱히 없는 것 같애요 수고하셨습니다~~

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/components/admin/CardinalDropdown.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/layout/Header.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/layout/NavItem.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/schedule/general/MonthNavigator.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/schedule/general/ScheduleFormField.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 실패
Build: 통과

⚠️ 일부 검증에 실패했습니다. 확인 후 수정해주세요.

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-7tsa8ld6d-weethsite-4975s-projects.vercel.app

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
src/components/admin/schedule/general/SchedulePageContent.tsx (1)

176-186: ⚠️ Potential issue | 🟡 Minor

status: 'SCHEDULED' 하드코딩이 여전히 남아 있습니다.

editTargetSchedule 타입이라 현재 타입 정의상 status 필드가 없어 부득이하게 하드코딩하고 있는 것으로 보입니다. 다만 이렇게 되면 실제 진행 상태(SessionStatus)와 무관하게 항상 'SCHEDULED' 로 표시되어 잘못된 정보가 노출될 수 있습니다. 월간 조회 응답에 세션 status를 포함시키도록 API/Schedule 타입을 확장하거나, 편집 모달 진입 시 세션 상세 조회로 실제 status를 가져오는 방향으로 풀어주세요. (PR 설명의 Schedule.content 누락 이슈와 같은 맥락이라 함께 추적하면 좋습니다.)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/schedule/general/SchedulePageContent.tsx` around lines
176 - 186, 현재 editTarget에 status를 하드코딩('SCHEDULED')하고 있어 실제 세션 상태와 불일치합니다; 수정
방법은 두 가지 중 하나를 선택해 적용하세요: (1) API와 Schedule 타입을 확장해 월간 조회 응답에
SessionStatus(status) 필드를 포함시키고 Schedule 타입에 status를 추가한 뒤 SchedulePageContent의
editTarget 객체를 API에서 받은 값으로 넘기도록 변경하거나, (2) 편집 모달을 열 때(예: editTarget을 세팅하거나 모달을
여는 함수) 세션 상세 조회를 호출해 실제 SessionStatus를 받아와 target.status에 채운 뒤 Modal 컴포넌트에 전달하도록
수정하세요; 관련 식별자는 editTarget, Schedule 타입(혹은 Schedule.content), SessionStatus, 모달
열기 로직(편집 핸들러)입니다.
🧹 Nitpick comments (3)
src/types/admin/schedule.d.ts (1)

13-18: name 필드의 의미가 모호해요.

ScheduleDetailname이 추가되었는데, 일정의 생성자 이름인지, 별도의 별칭인지 호출부에서 직관적으로 알기 어렵습니다. 가능하다면 creatorName, authorName 처럼 의미가 드러나는 이름으로 변경하거나, 최소한 JSDoc 주석으로 의미를 적어두는 걸 권장드립니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/types/admin/schedule.d.ts` around lines 13 - 18, The ScheduleDetail
interface exposes an ambiguous field "name"; update the type to make intent
explicit by either renaming "name" to a clearer identifier (e.g., "creatorName"
or "authorName") across all usages or add a concise JSDoc comment above the
ScheduleDetail interface describing exactly what "name" represents, then update
any code that references ScheduleDetail.name to the new identifier or keep .name
but ensure the JSDoc documents it (adjusting imports/usages in related modules
as needed).
src/components/admin/schedule/general/SchedulePageContent.tsx (1)

69-74: 리뷰 코멘트성 주석이 코드에 그대로 남아있어요.

Line 72의 // onError 처리도 토스트 유틸과 연결하는 것을 권장합니다. 는 이전 리뷰 피드백 문구가 그대로 코드에 남은 것으로 보입니다. useDeleteSchedule 훅 내부에서 이미 onErrortoastError(...)를 호출하고 있으므로(src/hooks/queries/admin/useAdminScheduleQueries.ts), 추가 onError 연결은 불필요하고 이 주석은 오해를 유발합니다. 정리해주세요.

🧹 제안 수정 예시
   const handleDelete = (schedule: Schedule) => {
     deleteSchedule(schedule.id, {
       onSuccess: () => setEditTarget(null),
-      // onError 처리도 토스트 유틸과 연결하는 것을 권장합니다.
     });
   };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/admin/schedule/general/SchedulePageContent.tsx` around lines
69 - 74, Remove the leftover review comment in the handleDelete function: delete
the inline comment "// onError 처리도 토스트 유틸과 연결하는 것을 권장합니다." next to the
deleteSchedule call in SchedulePageContent.tsx because the useDeleteSchedule
hook already handles onError (toastError) and the comment is misleading; leave
the deleteSchedule(schedule.id, { onSuccess: () => setEditTarget(null) }) call
as-is.
src/utils/admin/scheduleFormUtils.ts (1)

12-14: 제목 길이 비교 기준이 살짝 어긋날 수 있어요.

title.trim().length > 0 로 비어있는지 확인하면서 최대 길이는 title.length 로 비교하고 있어서, 양쪽 공백이 포함된 입력의 경우 화면에 보이는 글자수와 검증 기준이 달라질 수 있습니다. 일관되게 title.trim().length 로 비교하거나 입력 시점에 .trim() 처리한 값을 검증하도록 통일하는 것을 고려해 주세요.

♻️ 제안 수정 예시
 export function isScheduleTitleValid(title: string): boolean {
-  return title.trim().length > 0 && title.length <= SCHEDULE_FIELD_LIMITS.title;
+  const trimmed = title.trim();
+  return trimmed.length > 0 && trimmed.length <= SCHEDULE_FIELD_LIMITS.title;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/admin/scheduleFormUtils.ts` around lines 12 - 14, The length check
in isScheduleTitleValid is inconsistent: you call title.trim().length to check
emptiness but compare the max against title.length, causing mismatch when the
input has leading/trailing spaces; fix by normalizing once (e.g., const trimmed
= title.trim()) and use trimmed.length for both the >0 check and the <=
SCHEDULE_FIELD_LIMITS.title comparison (or require the caller to pass a
pre-trimmed title), updating isScheduleTitleValid accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/components/admin/schedule/general/SchedulePageContent.tsx`:
- Around line 176-186: 현재 editTarget에 status를 하드코딩('SCHEDULED')하고 있어 실제 세션 상태와
불일치합니다; 수정 방법은 두 가지 중 하나를 선택해 적용하세요: (1) API와 Schedule 타입을 확장해 월간 조회 응답에
SessionStatus(status) 필드를 포함시키고 Schedule 타입에 status를 추가한 뒤 SchedulePageContent의
editTarget 객체를 API에서 받은 값으로 넘기도록 변경하거나, (2) 편집 모달을 열 때(예: editTarget을 세팅하거나 모달을
여는 함수) 세션 상세 조회를 호출해 실제 SessionStatus를 받아와 target.status에 채운 뒤 Modal 컴포넌트에 전달하도록
수정하세요; 관련 식별자는 editTarget, Schedule 타입(혹은 Schedule.content), SessionStatus, 모달
열기 로직(편집 핸들러)입니다.

---

Nitpick comments:
In `@src/components/admin/schedule/general/SchedulePageContent.tsx`:
- Around line 69-74: Remove the leftover review comment in the handleDelete
function: delete the inline comment "// onError 처리도 토스트 유틸과 연결하는 것을 권장합니다." next
to the deleteSchedule call in SchedulePageContent.tsx because the
useDeleteSchedule hook already handles onError (toastError) and the comment is
misleading; leave the deleteSchedule(schedule.id, { onSuccess: () =>
setEditTarget(null) }) call as-is.

In `@src/types/admin/schedule.d.ts`:
- Around line 13-18: The ScheduleDetail interface exposes an ambiguous field
"name"; update the type to make intent explicit by either renaming "name" to a
clearer identifier (e.g., "creatorName" or "authorName") across all usages or
add a concise JSDoc comment above the ScheduleDetail interface describing
exactly what "name" represents, then update any code that references
ScheduleDetail.name to the new identifier or keep .name but ensure the JSDoc
documents it (adjusting imports/usages in related modules as needed).

In `@src/utils/admin/scheduleFormUtils.ts`:
- Around line 12-14: The length check in isScheduleTitleValid is inconsistent:
you call title.trim().length to check emptiness but compare the max against
title.length, causing mismatch when the input has leading/trailing spaces; fix
by normalizing once (e.g., const trimmed = title.trim()) and use trimmed.length
for both the >0 check and the <= SCHEDULE_FIELD_LIMITS.title comparison (or
require the caller to pass a pre-trimmed title), updating isScheduleTitleValid
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5062bac7-1e98-4b86-bb00-a25db222a955

📥 Commits

Reviewing files that changed from the base of the PR and between 5c13e13 and cf0b750.

📒 Files selected for processing (9)
  • src/app/globals.css
  • src/components/admin/schedule/general/SchedulePageContent.tsx
  • src/components/admin/schedule/modal/EditScheduleModal.tsx
  • src/hooks/index.ts
  • src/hooks/queries/admin/useAdminScheduleQueries.ts
  • src/hooks/useMonthNavigator.ts
  • src/lib/apis/adminSchedule.ts
  • src/types/admin/schedule.d.ts
  • src/utils/admin/scheduleFormUtils.ts
✅ Files skipped from review due to trivial changes (1)
  • src/hooks/useMonthNavigator.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/hooks/index.ts
  • src/app/globals.css
  • src/lib/apis/adminSchedule.ts
  • src/hooks/queries/admin/useAdminScheduleQueries.ts
  • src/components/admin/schedule/modal/EditScheduleModal.tsx

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/components/admin/CardinalDropdown.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/layout/Header.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/layout/NavItem.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/schedule/general/MonthNavigator.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/schedule/general/ScheduleFormField.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-pcfsidg44-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 실패
Build: 통과

⚠️ 일부 검증에 실패했습니다. 확인 후 수정해주세요.

@github-actions
Copy link
Copy Markdown

🤖 Claude 테스트 제안

모델: claude-sonnet-4-6 | 토큰: 0 입력 / 0 출력

변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.

src/components/admin/CardinalDropdown.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/layout/Header.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/layout/NavItem.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/schedule/general/MonthNavigator.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


src/components/admin/schedule/general/ScheduleFormField.tsx

오류: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.


이 코멘트는 Claude API를 통해 자동 생성되었습니다. 반드시 검토 후 사용하세요.

@github-actions
Copy link
Copy Markdown

PR 테스트 결과

Jest: 통과

🎉 모든 테스트를 통과했습니다!

@github-actions
Copy link
Copy Markdown

구현한 기능 Preview: https://weeth-65dpv0w8f-weethsite-4975s-projects.vercel.app

@github-actions
Copy link
Copy Markdown

PR 검증 결과

TypeScript: 통과
ESLint: 통과
Prettier: 통과
Build: 통과

🎉 모든 검증을 통과했습니다!

@JIN921 JIN921 merged commit 85041c3 into develop Apr 26, 2026
5 checks passed
@JIN921 JIN921 deleted the feat/WTH-306-어드민-일정-api-연결 branch April 26, 2026 04:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📬 API 서버 API 통신 🐞 BugFix Something isn't working 🔨 Refactor 코드 리팩토링

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants