Skip to content

feat: web-11 캘린더 일정 조회 페이지#18

Open
chlgusdn0203 wants to merge 5 commits into
developfrom
feature-web-11
Open

feat: web-11 캘린더 일정 조회 페이지#18
chlgusdn0203 wants to merge 5 commits into
developfrom
feature-web-11

Conversation

@chlgusdn0203

Copy link
Copy Markdown

📌 작업 내용

🔗 관련 이슈

📸 스크린샷 / 화면 녹화

🧪 테스트 방법

✅ 체크리스트

  • npm run lint 통과를 확인했습니다
  • npm run format 을 적용했습니다
  • 디버깅용 console.log 를 제거했습니다
  • 사용하지 않는 import / 변수를 정리했습니다
  • 컨벤션 (README.md) 을 준수했습니다
  • base 브랜치가 develop 인지 확인했습니다

💬 리뷰어에게 한 마디

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces an administrative calendar and schedule management system, adding the AdminSchedulePage along with components like AdminCalendarView, AllScheduleView, and associated dropdowns, checkboxes, and tabs. The review feedback highlights several critical bugs and usability improvements: using schedule.id as a unique key in AllScheduleView to prevent selection and duplicate key issues, adding a "Cancel" button for editing and adding schedules, keying AdminCalendarView with the current year and month to reset selection states on navigation, extending the year options to allow planning for the next year, removing redundant Date instantiations in utility functions, and replacing a hardcoded width with w-full in the dropdown list.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +9 to +11
function getScheduleKey(schedule) {
return `${schedule.startDate}-${schedule.endDate}-${schedule.title}`;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

getScheduleKey 함수에서 startDate, endDate, title 조합으로 키를 생성하고 있습니다. 만약 동일한 날짜에 동일한 제목을 가진 일정이 중복 등록될 경우, React key 중복 경고가 발생할 뿐만 아니라 체크박스 선택 및 삭제 시 중복된 모든 일정이 함께 선택되거나 삭제되는 버그가 발생할 수 있습니다.\n\nlocalSchedules 생성 시 고유한 id를 부여하고 있으므로, schedule.id가 존재한다면 이를 우선적으로 키로 사용하도록 개선하는 것이 안전합니다.

Suggested change
function getScheduleKey(schedule) {
return `${schedule.startDate}-${schedule.endDate}-${schedule.title}`;
}
function getScheduleKey(schedule) {
return schedule.id || `${schedule.startDate}-${schedule.endDate}-${schedule.title}`;
}

Comment thread src/pages/admin/AdminCalendarView.jsx Outdated
Comment on lines +263 to +272
{/* 수정/완료 버튼: shrink-0 으로 항상 하단 고정 */}
<div className="flex shrink-0 justify-end pt-8">
<button
type="button"
onClick={handleConfirm}
className="h-[32px] w-[96px] rounded-[6px] bg-brand px-[14px] py-[6px] text-[14px] text-white transition-opacity hover:opacity-80"
>
{isEditMode || isAddingSchedule ? '완료' : '수정'}
</button>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

일정 수정 모드(isEditMode) 또는 일정 추가 모드(isAddingSchedule)일 때, 사용자가 작업을 취소하고 이전 상태로 되돌릴 수 있는 "취소" 버튼이 없습니다. 이로 인해 사용자는 원치 않는 수정을 강제로 완료하거나 페이지를 새로고침해야 하는 불편함이 있습니다.\n\n"완료" 버튼 왼쪽에 변경 사항을 폐기하고 모드를 종료할 수 있는 "취소" 버튼을 추가하는 것을 권장합니다.

          {/* 수정/완료 버튼: shrink-0 으로 항상 하단 고정 */}
          <div className="flex shrink-0 justify-end gap-2 pt-8">
            {(isEditMode || isAddingSchedule) && (
              <button
                type="button"
                onClick={() => {
                  setIsEditMode(false);
                  setIsAddingSchedule(false);
                  setEditedSchedules([]);
                  setNewScheduleTitle('');
                }}
                className="h-[32px] w-[96px] rounded-[6px] border border-brand bg-white px-[14px] py-[6px] text-[14px] text-brand transition-opacity hover:opacity-80"
              >
                취소
              </button>
            )}
            <button
              type="button"
              onClick={handleConfirm}
              className="h-[32px] w-[96px] rounded-[6px] bg-brand px-[14px] py-[6px] text-[14px] text-white transition-opacity hover:opacity-80"
            >
              {isEditMode || isAddingSchedule ? '완료' : '수정'}
            </button>
          </div>

Comment thread src/pages/admin/AdminSchedulePage.jsx Outdated
Comment on lines +187 to +212
<AdminCalendarView
calendar={{ year, month, yearOptions, calendarDates, todayKey }}
view={{ viewMode, onViewModeChange: handleViewModeChange }}
schedules={{ scheduleDateKeys, visibleSchedules }}
monthDropdown={{
isOpen: isMonthDropdownOpen,
handlers: {
onToggle: () => setOpenedDropdown(isMonthDropdownOpen ? null : DROPDOWN_TYPE.MONTH),
onSelect: (monthOption) => { setMonth(monthOption); setOpenedDropdown(null); },
onScroll: handleMonthScroll,
},
scrollRefs: { listRef: monthListRef, trackRef: monthTrackRef, thumbRef: monthThumbRef },
}}
yearDropdown={{
isOpen: isYearDropdownOpen,
handlers: {
onToggle: () => setOpenedDropdown(isYearDropdownOpen ? null : DROPDOWN_TYPE.YEAR),
onSelect: (yearOption) => { setYear(yearOption); setOpenedDropdown(null); },
onScroll: handleYearScroll,
},
scrollRefs: { listRef: yearListRef, trackRef: yearTrackRef, thumbRef: yearThumbRef },
}}
onAddSchedule={handleAddSchedule}
onDeleteSchedule={handleDeleteSchedule}
onSaveEdits={handleSaveEdits}
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

사용자가 특정 월에서 일정을 선택한 상태(예: selectedIndexes에 인덱스가 저장된 상태)에서 드롭다운을 통해 다른 월이나 연도로 이동할 경우, AdminCalendarView 내부의 selectedIndexes 상태가 초기화되지 않고 그대로 유지됩니다. 이로 인해 새로 바뀐 월의 동일한 인덱스에 있는 일정이 의도치 않게 선택되어 표시되거나, 삭제 시 엉뚱한 일정이 삭제되는 심각한 버그가 발생할 수 있습니다.\n\nAdminCalendarView 컴포넌트에 key={${year}-${month}}를 부여하면, 연도나 월이 변경될 때 컴포넌트가 자동으로 언마운트 후 재마운트되면서 내부 상태(selectedIndexes, isEditMode, isAddingSchedule 등)가 깔끔하게 초기화되어 이 문제를 우아하게 해결할 수 있습니다.

        <AdminCalendarView
        key={`${year}-${month}`}
        calendar={{ year, month, yearOptions, calendarDates, todayKey }}
        view={{ viewMode, onViewModeChange: handleViewModeChange }}
        schedules={{ scheduleDateKeys, visibleSchedules }}
        monthDropdown={{
            isOpen: isMonthDropdownOpen,
            handlers: {
            onToggle: () => setOpenedDropdown(isMonthDropdownOpen ? null : DROPDOWN_TYPE.MONTH),
            onSelect: (monthOption) => { setMonth(monthOption); setOpenedDropdown(null); },
            onScroll: handleMonthScroll,
            },
            scrollRefs: { listRef: monthListRef, trackRef: monthTrackRef, thumbRef: monthThumbRef },
        }}
        yearDropdown={{
            isOpen: isYearDropdownOpen,
            handlers: {
            onToggle: () => setOpenedDropdown(isYearDropdownOpen ? null : DROPDOWN_TYPE.YEAR),
            onSelect: (yearOption) => { setYear(yearOption); setOpenedDropdown(null); },
            onScroll: handleYearScroll,
            },
            scrollRefs: { listRef: yearListRef, trackRef: yearTrackRef, thumbRef: yearThumbRef },
        }}
        onAddSchedule={handleAddSchedule}
        onDeleteSchedule={handleDeleteSchedule}
        onSaveEdits={handleSaveEdits}
        />

Comment on lines +34 to +37
const yearOptions = useMemo(
() => Array.from({ length: currentYear - 2022 + 1 }, (_, index) => 2022 + index),
[currentYear]
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

현재 yearOptions는 currentYear까지만 생성하도록 제한되어 있습니다. 연말이나 학기 말에는 다음 연도(내년)의 일정을 미리 계획하고 등록해야 하는 경우가 많으므로, 관리자가 내년 일정을 조회하고 추가할 수 있도록 currentYear + 1까지 연도 옵션을 제공하는 것이 좋습니다.

Suggested change
const yearOptions = useMemo(
() => Array.from({ length: currentYear - 2022 + 1 }, (_, index) => 2022 + index),
[currentYear]
);
const yearOptions = useMemo(
() => Array.from({ length: (currentYear + 1) - 2022 + 1 }, (_, index) => 2022 + index),
[currentYear]
);

Comment thread src/utils/schedule.js
Comment on lines +73 to +75
for (const current = new Date(start); current <= end; current.setDate(current.getDate() + 1)) {
keys.add(formatDateKey(new Date(current)));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

current 변수는 이미 new Date(start)로 생성된 Date 객체 인스턴스입니다. 따라서 formatDateKey를 호출할 때 굳이 new Date(current)로 새로운 객체를 중복 생성할 필요가 없습니다. 불필요한 객체 생성을 줄이기 위해 current를 직접 전달하도록 수정하는 것이 효율적입니다.

Suggested change
for (const current = new Date(start); current <= end; current.setDate(current.getDate() + 1)) {
keys.add(formatDateKey(new Date(current)));
}
for (const current = new Date(start); current <= end; current.setDate(current.getDate() + 1)) {
keys.add(formatDateKey(current));
}

<ul
ref={listRef}
onScroll={onScroll}
className="h-full w-[376px] divide-y divide-[#F1F3F7] overflow-y-auto"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

ul 태그의 너비가 w-[376px]로 하드코딩되어 있습니다. 상위 패널의 너비는 panelWidthClass 프로프를 통해 동적으로 전달받고 있으므로, 내부 리스트인 ul은 하드코딩된 고정 너비 대신 w-full을 사용하여 패널 크기에 맞게 유연하게 늘어나도록 설정하는 것이 더 견고하고 유지보수하기 좋습니다.

Suggested change
className="h-full w-[376px] divide-y divide-[#F1F3F7] overflow-y-auto"
className="h-full w-full divide-y divide-[#F1F3F7] overflow-y-auto"

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

image 수정완료

# Conflicts:
#	src/components/schedule/AllScheduleView.jsx
#	src/components/schedule/ScheduleDropdown.jsx
#	src/components/schedule/ScheduleTabs.jsx
#	src/constants/schedule.js
#	src/styles/ScheduleStyles.jsx
#	src/utils/schedule.js
# Conflicts:
#	src/App.jsx
#	src/components/schedule/AllScheduleView.jsx
#	src/components/schedule/ScheduleTabs.jsx
#	src/constants/schedule.js
#	tailwind.config.js
@takjinwu

takjinwu commented Jul 4, 2026

Copy link
Copy Markdown
Contributor
image

시작일은 선택이 가능한데, 종료일은 어떻게 선택하나요?? 종료일도 선택 가능하게 변경해주세요

@takjinwu

takjinwu commented Jul 4, 2026

Copy link
Copy Markdown
Contributor
image

오른쪽의 의미 없는 완료 버튼은 무엇인가요? 클릭해도 아무 동작하지 않아서요

@takjinwu

takjinwu commented Jul 4, 2026

Copy link
Copy Markdown
Contributor
  1. pages/ 폴더 규칙 위반 (§2)
  • AdminCalendarView.jsx가 pages/admin/에 있음. 이 파일은 라우트와 1:1 매칭되는 페이지가 아니라 AdminSchedulePage가 렌더하는 뷰 컴포넌트입니다. §2: "pages/ 안에는 라우트와 1:1 매칭 파일만. 그 외 로직은 components/로 분리." → components/schedule/(또는 components/admin/)로 이동해야 함. (나머지 schedule 컴포넌트들은 components/schedule/에 올바르게 위치)

@takjinwu

takjinwu commented Jul 4, 2026

Copy link
Copy Markdown
Contributor
  1. 이미 존재하는 상수를 두고 하드코딩 (§3 매직값 금지 + 일관성)

constants/schedule.js에 상수가 정의돼 있고 방문자 SchedulePage.jsx는 이 상수를 쓰는데, 관리자 페이지만 리터럴을 하드코딩:

  • VIEW_MODE 미사용 → AdminSchedulePage.jsx:42, 53, 124, 136, 151, 155, AllScheduleView.jsx:36에서 'calendar' / 'all' 문자열 하드코딩. (SchedulePage는 VIEW_MODE.CALENDAR/ALL 사용 → 불일치)
  • CLUB_START_YEAR(=2022) 미사용 → AdminSchedulePage.jsx:35 currentYear - 2022 + 2, 2022 + index로 매직넘버 2022 하드코딩.

@takjinwu

takjinwu commented Jul 4, 2026

Copy link
Copy Markdown
Contributor
image

일정이 많아질 시에 스크롤 기능이 작동하는 것이 아니라 밑에까지 침범하여 깨지는 부분 수정해주세요.

@chlgusdn0203 chlgusdn0203 reopened this Jul 5, 2026
@chlgusdn0203

Copy link
Copy Markdown
Author

@takjinwu
시작일과 종료일 설정할 수 있게 수정했고
완료 버튼은 피그마 디자인에 똑같은 버튼이있어서 추가했습니다
일정 많아질 시에 스크롤 기능 작동하게 수정했습니다.
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants