[Feat] WTH-306: 어드민 일정 API 연결 #69
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthrough관리자 일정 기능이 정적 데이터에서 API 기반으로 전환되었고, 일정 타입/필드 명세가 변경되며 폼 검증, 길이 제한, 모달 제어 로직 및 클라이언트 로그아웃 훅이 추가되었습니다. Changes
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: 목록 갱신
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
|
구현한 기능 Preview: https://weeth-hgqg6glih-weethsite-4975s-projects.vercel.app |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
There was a problem hiding this comment.
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콜백이 전달되지 않아 세션 생성 기능이 작동하지 않습니다.
CreateScheduleModal은onCreateSessionprop을 올바르게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 단언 대비 가드 필요
useAdminMonthlySchedules는enabled: !!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/메타데이터 렌더링 블록 정리 필요1
35줄의56줄의 관련 렌더링 로직이 전부 주석 처리되어 데드 코드로 남아 있습니다. 유지한다면 TODO 코멘트로 복구 사유와 일정을 명시해 주고, 당분간 복구 계획이 없다면 버전 관리에 맡기고 삭제하는 것을 권장합니다. 현재 상태에서는 관리자 헤더의 페이지별 타이틀/설명이 완전히 사라지게 되는데, 이것이 이번 PR 범위에서 의도된 동작인지 확인이 필요합니다.PAGE_METADATA상수와 42, 44, 48🤖 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
⛔ Files ignored due to path filters (1)
src/assets/icons/location.svgis excluded by!**/*.svg
📒 Files selected for processing (36)
src/app/globals.csssrc/assets/icons/index.tssrc/components/admin/CardinalDropdown.tsxsrc/components/admin/index.tssrc/components/admin/layout/Header.tsxsrc/components/admin/layout/NavItem.tsxsrc/components/admin/schedule/general/MonthNavigator.tsxsrc/components/admin/schedule/general/ScheduleFormField.tsxsrc/components/admin/schedule/general/ScheduleItem.tsxsrc/components/admin/schedule/general/ScheduleList.tsxsrc/components/admin/schedule/general/SchedulePageContent.tsxsrc/components/admin/schedule/general/ScheduleTag.tsxsrc/components/admin/schedule/general/ScheduleTextField.tsxsrc/components/admin/schedule/general/ScheduleTextareaField.tsxsrc/components/admin/schedule/modal/CreateGeneralScheduleForm.tsxsrc/components/admin/schedule/modal/CreateScheduleModal.tsxsrc/components/admin/schedule/modal/CreateSessionScheduleForm.tsxsrc/components/admin/schedule/modal/EditScheduleModal.tsxsrc/components/admin/schedule/modal/EditSessionModal.tsxsrc/components/admin/schedule/modal/ScheduleFormBody.tsxsrc/components/admin/schedule/modal/SessionScheduleForm.tsxsrc/components/alert/CustomAlertDialog.tsxsrc/components/layout/header/MobileNavSheet.tsxsrc/components/mypage/MyPageDropdownMenu.tsxsrc/components/ui/CalendarPicker.tsxsrc/components/ui/DateTimeInput.tsxsrc/components/ui/DropdownMenu.tsxsrc/components/ui/TimePicker.tsxsrc/constants/admin/schedule.constants.tssrc/hooks/index.tssrc/hooks/queries/admin/useAdminScheduleQueries.tssrc/hooks/useLogout.tssrc/lib/apis/adminSchedule.tssrc/lib/apis/index.tssrc/types/admin/schedule.d.tssrc/utils/admin/scheduleFormUtils.ts
| export function useLogout() { | ||
| const { reset } = useClubActions(); | ||
|
|
||
| return async () => { | ||
| reset(); | ||
| await logoutAction(); | ||
| }; | ||
| } |
There was a problem hiding this comment.
🧩 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=tsxRepository: 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 -20Repository: 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.tsxRepository: Team-Weeth/weeth-client
Length of output: 647
🏁 Script executed:
sed -n '20,35p' src/components/layout/header/MobileNavSheet.tsxRepository: Team-Weeth/weeth-client
Length of output: 719
🏁 Script executed:
sed -n '38,55p' src/components/admin/layout/Header.tsxRepository: Team-Weeth/weeth-client
Length of output: 751
🏁 Script executed:
rg -n 'handleLogout\(' src/components/mypage/MyPageDropdownMenu.tsx -A2 -B2Repository: Team-Weeth/weeth-client
Length of output: 49
🏁 Script executed:
rg -n 'handleLogout\(' src/components/layout/header/MobileNavSheet.tsx -A2 -B2Repository: Team-Weeth/weeth-client
Length of output: 49
🏁 Script executed:
rg -n 'handleLogout\(' src/components/admin/layout/Header.tsx -A2 -B2Repository: Team-Weeth/weeth-client
Length of output: 49
🏁 Script executed:
rg -n 'handleLogout' src/components/mypage/MyPageDropdownMenu.tsx -B1 -A3Repository: Team-Weeth/weeth-client
Length of output: 371
🏁 Script executed:
rg -n 'handleLogout' src/components/layout/header/MobileNavSheet.tsx -B1 -A3Repository: Team-Weeth/weeth-client
Length of output: 538
🏁 Script executed:
rg -n 'handleLogout' src/components/admin/layout/Header.tsx -B1 -A3Repository: Team-Weeth/weeth-client
Length of output: 561
🏁 Script executed:
rg -n 'AlertDialogAction|onClick.*handleLogout' src/ -B2 -A2 | head -40Repository: 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.
| 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.
| 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; | ||
| } |
There was a problem hiding this comment.
Schedule에 content 필드 누락 — 후속 작업 필요
CreateEventBody/UpdateEventBody는 content를 포함하지만, 조회 응답 타입인 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.
nabbang6
left a comment
There was a problem hiding this comment.
와아앙 고생하셧습니다!!~!
어드민도 거의 완성되어 가네용... 최고 👍,,,
| onCreateSession?.(body); | ||
| onClose(); |
There was a problem hiding this comment.
요기도 CreateGeneralScheduleForm의 mutate(body, { onSuccess: onClose });처럼 mutation 성공/실패를 기다린 다음 닫히게 두면 조을 것 같습니당!
woneeeee
left a comment
There was a problem hiding this comment.
수고하셨습니당!! 제가 세션 UI 피알이랑 순간 헷갈려서 동일 피알인줄 알고 이미 본 피알인줄 알았었어요 ㅠㅠㅠㅠ 리뷰가 늦어서 죄송함니당 ㅜㅜㅜㅜ
dalzzy
left a comment
There was a problem hiding this comment.
확인했습니다!! 리뷰가 늦어 죄송합니닷...
다른 분들이 리뷰 남겨주신 부분만 수정해주시면 될 것 같습니당! 이 외에는 수정할 부분 딱히 없는 것 같애요 수고하셨습니다~~
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
PR 검증 결과✅ TypeScript: 통과 |
|
구현한 기능 Preview: https://weeth-7tsa8ld6d-weethsite-4975s-projects.vercel.app |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
src/components/admin/schedule/general/SchedulePageContent.tsx (1)
176-186:⚠️ Potential issue | 🟡 Minor
status: 'SCHEDULED'하드코딩이 여전히 남아 있습니다.
editTarget은Schedule타입이라 현재 타입 정의상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필드의 의미가 모호해요.
ScheduleDetail에name이 추가되었는데, 일정의 생성자 이름인지, 별도의 별칭인지 호출부에서 직관적으로 알기 어렵습니다. 가능하다면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훅 내부에서 이미onError로toastError(...)를 호출하고 있으므로(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
📒 Files selected for processing (9)
src/app/globals.csssrc/components/admin/schedule/general/SchedulePageContent.tsxsrc/components/admin/schedule/modal/EditScheduleModal.tsxsrc/hooks/index.tssrc/hooks/queries/admin/useAdminScheduleQueries.tssrc/hooks/useMonthNavigator.tssrc/lib/apis/adminSchedule.tssrc/types/admin/schedule.d.tssrc/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
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
|
구현한 기능 Preview: https://weeth-pcfsidg44-weethsite-4975s-projects.vercel.app |
PR 검증 결과✅ TypeScript: 통과 |
🤖 Claude 테스트 제안
변경된 컴포넌트에 대해 Claude가 생성한 테스트 코드입니다. 검토 후 적합한 부분만 사용하세요.
|
PR 테스트 결과✅ Jest: 통과 🎉 모든 테스트를 통과했습니다! |
|
구현한 기능 Preview: https://weeth-65dpv0w8f-weethsite-4975s-projects.vercel.app |
PR 검증 결과✅ TypeScript: 통과 🎉 모든 검증을 통과했습니다! |
✅ PR 유형
어떤 변경 사항이 있었나요?
📌 관련 이슈번호
✅ Key Changes
API 연결
useAdminMonthlySchedules)useCreateSchedule,useUpdateSchedule,useDeleteSchedule)기능 추가
SessionTabContent) 및 세션 수정/삭제 모달 (EditSessionModal) 추가CreateScheduleModalactiveTab 제어)CardinalDropdown) 적용버그 수정
코드 리팩토링
@/alias 통일 (EditSessionModal)isScheduleTitleValid()로 일관화ModalIconButton컴포넌트로 교체SessionScheduleForm내 로컬Cardinal인터페이스 제거 →@/types/admin/cardinalimportCalendarPicker,DateTimeInput직접 경로 import →@/components/uibarrel importSchedulePageContent중복@/components/uiimport 병합admin/index.ts에ModalIconButtonexport 추가max-h-[700px]→max-h-175등)📸 스크린샷 or 실행영상
🎸 기타 사항 or 추가 코멘트
세션 수정 API는 미구현 상태로 TODO 주석 처리 (
EditSessionModal— API 스펙 확정 후 연동 예정)Schedule타입에content필드가 없어 수정 모달에서 기존 설명을 불러올 수 없음 — API 응답 스펙 업데이트 필요일단 어드민 레이아웃 헤더는 없애 달라 하셧던 거 같아서 렌더링 안 되는 문제 걍 냅두고 주석처리 햇습니다! 하단 보더도 없애고 로그아웃 버튼만 남겻어용
슬랙에도 남겻는데 dialog z-index가 바뀌면서 기존에 사용되던 모달들이 안 보여서.. 고치긴 햇으나 미리보기 해보시고 안 보이는 부분 잇으면 말씀 주세용ㅜㅜ!!
세션 api는 ui와 마찬가지로 분리해서 구현하겠습니다!
피그마 보고 타이포 다른 부분 체크 해서 고쳐놓았습니다..!
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 수정
스타일
Refactor