[feature] 관리자 캘린더 Google·Notion 연동 UI 개편 - #1894
Conversation
개발 모드에서 항상 MSW가 켜져 실서버 연동을 확인할 수 없었다. 기본값은 그대로 목이고, .env에 VITE_ENABLE_MSW=false를 두면 실서버로 붙는다.
손잡이 바만으로는 집기 어려워 위아래 패딩을 준 영역을 두고 포인터 이벤트를 붙였다. 100px 이상 내리면 닫히고 그 아래면 제자리로 돌아간다. 빠르게 끌 때 포인터가 손잡이를 벗어나 드래그가 끊기지 않도록 포인터를 캡처한다.
연동 UI가 일정 시트 안으로 옮겨가면서 우측 하단 Google·Notion 팝오버와 상단 데이터 오류 배너가 필요 없어졌다. 함께 고아가 된 스타일도 정리했다.
일정 추가 시트 '일반' 탭 하단에 연동 카드를 붙였다. 카드는 미연동·불러오는 중·연동됨 3상태이고, 연동되면 가져온 일정이 체크 목록으로 펼쳐진다. 체크는 캘린더 표시 여부다. 숨김 목록에 없으면 표시 상태이므로 기본은 전부 체크이고, 해제하면 숨기고 다시 체크하면 되살린다. 숨김 해제는 백엔드에 이미 있던 DELETE /calendar-events/hidden을 이제야 연결했다. 연동 해제는 되돌릴 수 없어 확인 모달을 거치게 했다. Notion 해제 엔드포인트는 백엔드에 아직 없어 Google과 같은 경로 규칙으로 붙여뒀다. 저장 버튼은 연동 목록이 길어져도 가려지지 않게 시트 하단에 고정했다.
'없음'만으로는 무엇이 없는지 읽히지 않아 문구를 명확히 했다.
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough캘린더 연동 UI를 카드와 이벤트 선택 목록 중심으로 재구성했습니다. Notion 연결 해제와 숨김 일정 표시 해제 API를 추가했습니다. Mixpanel 추적과 모바일 BottomSheet의 중첩·드래그 동작을 추가했습니다. Changes캘린더 관리 기능
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Sequence Diagram(s)sequenceDiagram
participant CalendarSyncTab
participant AddEventSheet
participant CalendarLinkPanel
participant CalendarLinkCard
participant DisconnectConfirmModal
participant useDisconnectNotionCalendar
CalendarSyncTab->>AddEventSheet: 일정 추가 시트 표시
AddEventSheet->>CalendarLinkPanel: 캘린더 연동 패널 렌더링
CalendarLinkPanel->>CalendarLinkCard: 연결 상태와 이벤트 전달
CalendarLinkCard->>DisconnectConfirmModal: 연결 해제 확인 표시
DisconnectConfirmModal->>useDisconnectNotionCalendar: 연결 해제 mutation 실행
useDisconnectNotionCalendar-->>CalendarLinkPanel: 완료 상태와 캐시 갱신
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 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 |
✅ UI 변경사항 없음
전체 119개 스토리 · 47개 컴포넌트 |
저장 버튼을 시트 하단에 고정하면서 실패 안내는 흐름에 남겨둬, 연동 섹션 때문에 스크롤이 생기면 안내가 화면 밖으로 밀려 눌러도 아무 반응이 없어 보였다. 안내를 고정 영역 안으로 옮겼다. 버튼 영역 배경은 투명으로 바꿨다. 안내 문구는 투명 배경에서 뒤 내용과 겹쳐 읽히지 않아 문구에만 시트 배경색을 깔았다. 날짜 시트가 위에 열려 있는 동안에는 저장할 일이 없어 버튼을 숨긴다.
반복 필드의 미지정 표시는 '종료 기간 없음'인데 그걸 되돌리는 시트 버튼만 '종료 없음'이라 같은 상태를 서로 다른 말로 가리키고 있었다.
바텀시트는 내용만큼만 높이를 차지해, 반복 탭처럼 내용이 짧으면 위에 뜬 날짜 시트가 뒤 시트를 통째로 덮었다. 저장 버튼까지 숨기면서 더 짧아져 손잡이와 제목 입력창 일부(73px)만 남았다. BottomSheet에 expanded를 두어 위에 시트를 겹칠 때 높이를 최대로 펴고, AddEventSheet가 날짜 시트를 열 때 이를 켠다. 딤드는 두 시트가 각자 오버레이를 그리므로 이미 두 겹이다.
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (6)
frontend/src/components/common/BottomSheet/BottomSheet.styles.ts (1)
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value타입 import를 별도 블록으로 분리하십시오.
Line 1은 React 런타임 import와 타입 import를 함께 선언합니다. 타입 import는 내부 모듈 import 뒤, styles import 앞에 배치하십시오.
수정 예시
-import { MouseEvent, PointerEvent, ReactNode, useRef, useState } from 'react'; +import { useRef, useState } from 'react'; import useBodyScrollLock from '`@/hooks/useBodyScrollLock`'; import useTopmostEscape from '`@/hooks/useTopmostEscape`'; import Portal from '../Portal/Portal'; +import type { MouseEvent, PointerEvent, ReactNode } from 'react'; import * as Styled from './BottomSheet.styles';코딩 가이드에 따라
frontend/**/*.{ts,tsx}는 “Order imports as external libraries, internal modules, types, then styles.”를 따라야 합니다.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/common/BottomSheet/BottomSheet.styles.ts` at line 1, Update the imports in BottomSheet.styles.ts to separate type-only imports into their own block, ordered after external and internal module imports and before styles imports, while keeping the existing runtime styled-components import separate.Source: Coding guidelines
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.tsx (1)
1-5: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueimport 순서를 가이드라인에 맞추십시오.
세 파일에서 type-only import가 internal module import와 섞여 있습니다.
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.tsx#L1-L5:CalendarLinkStatustype import를 internal value import 뒤, styles import 앞에 배치하십시오.frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.tsx#L1-L16:HiddenCalendarEvent,CalendarLinkStatus,CalendarLinkEventtype import를 internal module import 뒤에 모으십시오.frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.tsx#L1-L20:CalendarLinkPanel을 포함한 internal module import를 type import보다 앞에 배치하십시오.As per coding guidelines, “Order imports as external libraries, internal modules, types, then styles.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.tsx` around lines 1 - 5, 세 파일의 import 순서를 외부 라이브러리, internal module, type, styles 순서로 정리하십시오. frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.tsx 1-5에서는 CalendarLinkStatus를 internal value import 뒤 styles import 앞에 배치하고, frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.tsx 1-16에서는 HiddenCalendarEvent, CalendarLinkStatus, CalendarLinkEvent를 모든 internal module import 뒤에 모으십시오. frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.tsx 1-20에서는 CalendarLinkPanel을 포함한 internal module import를 type import보다 앞에 배치하십시오.Source: Coding guidelines
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.stories.tsx (1)
16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win스토리 데코레이터의 인라인 스타일을 theme 기반 styled-components로 교체하세요.
width,background,padding을 styled wrapper로 이동하세요. 배경색은 하드코드한'#F5F5F5'대신 theme 색상을 사용하세요. 이렇게 하면 스토리도 제품 UI와 같은 디자인 토큰을 사용합니다.As per coding guidelines:
Use styled-components and the theme system for styling.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.stories.tsx` at line 16, Update the story decorator’s wrapper around the calendar link card to use a styled-components wrapper instead of the inline style object. Move the width, padding, and background styling into that wrapper, using the theme’s appropriate color token instead of hardcoded `#F5F5F5` while preserving the existing dimensions and spacing.Source: Coding guidelines
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkButton/CalendarLinkButton.stories.tsx (1)
1-2: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win타입 import를 내부 모듈 import 뒤로 이동하세요.
각 스토리 파일에서 external library import 다음에 내부 모듈 import를 두세요. 그 뒤에
import type를 두세요.
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkButton/CalendarLinkButton.stories.tsx#L1-L2:CalendarLinkButtonimport 뒤로Meta,StoryObjtype import를 이동하세요.frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.stories.tsx#L1-L3:CalendarLinkEvent를 별도 type import로 분리하고CalendarLinkCardimport 뒤로 이동하세요.frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DisconnectConfirmModal/DisconnectConfirmModal.stories.tsx#L1-L2:DisconnectConfirmModalimport 뒤로Meta,StoryObjtype import를 이동하세요.As per coding guidelines:
Order imports as external libraries, internal modules, types, then styles.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkButton/CalendarLinkButton.stories.tsx` around lines 1 - 2, Reorder imports to follow external libraries, internal modules, types, then styles. In frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkButton/CalendarLinkButton.stories.tsx#L1-L2, move the Meta and StoryObj type import after CalendarLinkButton; in frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.stories.tsx#L1-L3, separate CalendarLinkEvent into a type import and place it after CalendarLinkCard; in frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DisconnectConfirmModal/DisconnectConfirmModal.stories.tsx#L1-L2, move the Meta and StoryObj type import after DisconnectConfirmModal.Source: Coding guidelines
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkSection/CalendarLinkSection.stories.tsx (1)
13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win스토리 프리뷰 스타일을 styled-components와 테마로 이동하세요.
두 스토리가 인라인
style과 색상 리터럴을 사용합니다. 프리뷰 래퍼를 styled-components로 정의하고 색상과 간격에 테마 값을 사용하세요.
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkSection/CalendarLinkSection.stories.tsx#L13-L13: decorator의 인라인 스타일을 테마 기반 래퍼 컴포넌트로 교체하세요.frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/EventCheckItem/EventCheckItem.stories.tsx#L25-L25: Template의 인라인 스타일을 같은 방식으로 교체하세요.As per coding guidelines, "Use styled-components and the theme system for styling."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkSection/CalendarLinkSection.stories.tsx` at line 13, Replace the inline preview styles in CalendarLinkSection.stories.tsx at line 13 and EventCheckItem.stories.tsx at line 25 with a shared or equivalent styled-components wrapper using theme-provided colors and spacing. Preserve the existing preview dimensions and layout while removing hard-coded style values and color literals.Source: Coding guidelines
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.styles.ts (1)
6-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win오류 색상을 테마 토큰으로 통일하세요.
두 위치가 동일한 오류 색상
#dc2626을 직접 정의합니다. 오류용 테마 토큰을 사용하세요.
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.styles.ts#L6-L6:#dc2626을 오류 색상 테마 토큰으로 교체하세요.frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.styles.ts#L43-L43: 같은 오류 색상 테마 토큰을 사용하세요.As per coding guidelines, "Use styled-components and the theme system for styling."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.styles.ts` at line 6, Replace the hardcoded `#dc2626` error color with the established error color theme token in both CalendarLinkPanel.styles.ts:6-6 and AddEventSheet.styles.ts:43-43, preserving the existing styled-components usage and applying the same token at both sites.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/components/common/BottomSheet/BottomSheet.tsx`:
- Line 74: Update the BottomSheet pointer event handling so onPointerCancel uses
a dedicated handler that only resets dragOffset and does not invoke onClose or
perform close 판단. Keep close 판단 exclusively in handlePointerUp, while preserving
the existing pointerup behavior.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.tsx`:
- Around line 51-75: Update CalendarLinkPanel’s useGetHiddenCalendarEvents
handling so loading or error states are not defaulted to an empty list and
interpreted as all events visible. Track and handle isLoading and isError,
disable visibility toggles until hidden-event data is successfully available,
and display an error state when the query fails.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DisconnectConfirmModal/DisconnectConfirmModal.tsx`:
- Around line 17-24: Update the dialog element in DisconnectConfirmModal,
currently rendered as Styled.Card with role="dialog", to provide an accessible
name via aria-labelledby referencing Styled.Title or an appropriate fixed
aria-label.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/EventCheckItem/EventCheckItem.styles.ts`:
- Around line 13-19: Update the Input styled component so its :focus-visible
state applies an outline to the adjacent visual checkbox element. Preserve the
existing hidden-input behavior and target the neighboring checkbox selector used
by EventCheckItem rather than changing the input’s visibility.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionCalendarData.ts`:
- Around line 50-56: Update disconnectNotion so a successful disconnect also
clears the parent notionWorkspaceName state, while preserving the existing
pickedDatabaseId reset and error handling. Pass the success callback through the
relevant useCalendarSync and CalendarLinkPanel integration, and add coverage
confirming the card returns to idle only after a successful disconnect.
---
Nitpick comments:
In `@frontend/src/components/common/BottomSheet/BottomSheet.styles.ts`:
- Line 1: Update the imports in BottomSheet.styles.ts to separate type-only
imports into their own block, ordered after external and internal module imports
and before styles imports, while keeping the existing runtime styled-components
import separate.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkButton/CalendarLinkButton.stories.tsx`:
- Around line 1-2: Reorder imports to follow external libraries, internal
modules, types, then styles. In
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkButton/CalendarLinkButton.stories.tsx#L1-L2,
move the Meta and StoryObj type import after CalendarLinkButton; in
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.stories.tsx#L1-L3,
separate CalendarLinkEvent into a type import and place it after
CalendarLinkCard; in
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DisconnectConfirmModal/DisconnectConfirmModal.stories.tsx#L1-L2,
move the Meta and StoryObj type import after DisconnectConfirmModal.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.stories.tsx`:
- Line 16: Update the story decorator’s wrapper around the calendar link card to
use a styled-components wrapper instead of the inline style object. Move the
width, padding, and background styling into that wrapper, using the theme’s
appropriate color token instead of hardcoded `#F5F5F5` while preserving the
existing dimensions and spacing.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.tsx`:
- Around line 1-5: 세 파일의 import 순서를 외부 라이브러리, internal module, type, styles 순서로
정리하십시오.
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.tsx
1-5에서는 CalendarLinkStatus를 internal value import 뒤 styles import 앞에 배치하고,
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.tsx
1-16에서는 HiddenCalendarEvent, CalendarLinkStatus, CalendarLinkEvent를 모든 internal
module import 뒤에 모으십시오.
frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.tsx
1-20에서는 CalendarLinkPanel을 포함한 internal module import를 type import보다 앞에 배치하십시오.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.styles.ts`:
- Line 6: Replace the hardcoded `#dc2626` error color with the established error
color theme token in both CalendarLinkPanel.styles.ts:6-6 and
AddEventSheet.styles.ts:43-43, preserving the existing styled-components usage
and applying the same token at both sites.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkSection/CalendarLinkSection.stories.tsx`:
- Line 13: Replace the inline preview styles in CalendarLinkSection.stories.tsx
at line 13 and EventCheckItem.stories.tsx at line 25 with a shared or equivalent
styled-components wrapper using theme-provided colors and spacing. Preserve the
existing preview dimensions and layout while removing hard-coded style values
and color literals.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 19f55257-ab8f-48b7-bc9c-e6e0095b0cdb
⛔ Files ignored due to path filters (2)
frontend/src/assets/images/icons/calendar_sync_checkbox_checked.svgis excluded by!**/*.svgfrontend/src/assets/images/icons/calendar_sync_warning.svgis excluded by!**/*.svg
📒 Files selected for processing (36)
frontend/src/apis/calendarOAuth.tsfrontend/src/apis/hiddenCalendarEvents.tsfrontend/src/components/common/BottomSheet/BottomSheet.styles.tsfrontend/src/components/common/BottomSheet/BottomSheet.tsxfrontend/src/components/common/ResponsiveSheet/ResponsiveSheet.tsxfrontend/src/hooks/Queries/useHiddenCalendarEvents.tsfrontend/src/hooks/Queries/useNotionCalendar.tsfrontend/src/index.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/CalendarSyncTab.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/CalendarSyncTab.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.test.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkButton/CalendarLinkButton.stories.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkButton/CalendarLinkButton.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkButton/CalendarLinkButton.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.stories.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkCard/CalendarLinkCard.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkSection/CalendarLinkSection.stories.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkSection/CalendarLinkSection.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkSection/CalendarLinkSection.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DatePickerSheet/DatePickerSheet.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DisconnectConfirmModal/DisconnectConfirmModal.stories.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DisconnectConfirmModal/DisconnectConfirmModal.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DisconnectConfirmModal/DisconnectConfirmModal.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/EventCheckItem/EventCheckItem.stories.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/EventCheckItem/EventCheckItem.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/EventCheckItem/EventCheckItem.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/LoadingDots/LoadingDots.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/LoadingDots/LoadingDots.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/RecurrenceFields/RecurrenceFields.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useCalendarSync.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionCalendarData.ts
💤 Files with no reviewable changes (2)
- frontend/src/pages/AdminPage/tabs/CalendarSyncTab/CalendarSyncTab.styles.ts
- frontend/src/pages/AdminPage/tabs/CalendarSyncTab/CalendarSyncTab.tsx
| onPointerDown={handlePointerDown} | ||
| onPointerMove={handlePointerMove} | ||
| onPointerUp={handlePointerUp} | ||
| onPointerCancel={handlePointerUp} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Files matching BottomSheet:"
fd -a 'BottomSheet\.tsx$' . | sed 's#^\./##'
file=$(fd 'BottomSheet\.tsx$' frontend/src/components/common/BottomSheet | head -n 1 || true)
echo "selected: ${file:-none}"
if [ -n "${file:-}" ]; then
wc -l "$file"
echo "Outline:"
ast-grep outline "$file" --view compact || true
echo "Relevant lines 1-140:"
sed -n '1,140p' "$file" | nl -ba
fiRepository: Moadong/moadong
Length of output: 613
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file="frontend/src/components/common/BottomSheet/BottomSheet.tsx"
echo "File: $file"
lines=$(wc -l < "$file")
echo "Line count: $lines"
echo "Relevant lines:"
awk '{printf "%5d\t%s\n", NR, $0}' "$file"
echo "Pointer handler text:"
rg -n "handle(Pointer|PointerCancel|PointerUp|PointerMove|PointerDown)|setDragOffset|onClose|dragOffset|pointercancel" "$file" || trueRepository: Moadong/moadong
Length of output: 4192
pointercancel에서는 시트를 닫지 마십시오.
pointercancel은 취소된 포인터 인터랙션을 의미하므로 onClose를 실행하지 않아야 합니다. onPointerCancel 전용 핸들러에서 dragOffset만 초기화하고, 닫기 판단은 pointerup에서만 하십시오.
수정 예시
+ const handlePointerCancel = () => {
+ if (dragOffset === null) return;
+ setDragOffset(null);
+ };
+
<Styled.DragHandle
onPointerDown={handlePointerDown}
onPointerMove={handlePointerMove}
onPointerUp={handlePointerUp}
- onPointerCancel={handlePointerUp}
+ onPointerCancel={handlePointerCancel}
>📝 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.
| onPointerCancel={handlePointerUp} | |
| const handlePointerCancel = () => { | |
| if (dragOffset === null) return; | |
| setDragOffset(null); | |
| }; | |
| onPointerCancel={handlePointerCancel} |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/common/BottomSheet/BottomSheet.tsx` at line 74,
Update the BottomSheet pointer event handling so onPointerCancel uses a
dedicated handler that only resets dragOffset and does not invoke onClose or
perform close 판단. Keep close 판단 exclusively in handlePointerUp, while preserving
the existing pointerup behavior.
| const { data: hiddenCalendarEvents = [] } = useGetHiddenCalendarEvents(); | ||
| const hideMutation = useHideCalendarEvent(); | ||
| const unhideMutation = useUnhideCalendarEvent(); | ||
|
|
||
| /** 연동 해제는 되돌릴 수 없어 확인 모달을 거친다 */ | ||
| const [disconnectTarget, setDisconnectTarget] = useState< | ||
| HiddenCalendarEvent['source'] | null | ||
| >(null); | ||
|
|
||
| const confirmDisconnect = () => { | ||
| if (disconnectTarget === 'GOOGLE') disconnectGoogle(); | ||
| if (disconnectTarget === 'NOTION') disconnectNotion(); | ||
| setDisconnectTarget(null); | ||
| }; | ||
|
|
||
| const isNotionConnected = | ||
| notionDatabaseOptions.length > 0 || Boolean(notionWorkspaceName); | ||
|
|
||
| const hiddenKeys = new Set( | ||
| hiddenCalendarEvents.map((event) => `${event.source}:${event.eventId}`), | ||
| ); | ||
|
|
||
| /** 체크 = 캘린더에 표시. 숨김 목록에 없으면 표시 상태다 */ | ||
| const toVisibleIds = (source: HiddenCalendarEvent['source'], ids: string[]) => | ||
| ids.filter((id) => !hiddenKeys.has(`${source}:${id}`)); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
숨김 일정 조회 오류를 빈 목록으로 처리하지 마십시오.
Line 51은 조회 중이거나 조회가 실패한 상태를 []로 처리합니다. 그러면 Line 69-75는 모든 이벤트를 표시 상태로 계산합니다. 사용자는 실제 숨김 상태와 다른 체크 상태를 볼 수 있습니다. isLoading과 isError를 처리하고, 상태를 확인하기 전에는 토글을 비활성화하거나 오류 상태를 표시하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.tsx`
around lines 51 - 75, Update CalendarLinkPanel’s useGetHiddenCalendarEvents
handling so loading or error states are not defaulted to an empty list and
interpreted as all events visible. Track and handle isLoading and isError,
disable visibility toggles until hidden-event data is successfully available,
and display an error state when the query fails.
| <Styled.Card role='dialog' aria-modal='true'> | ||
| <Styled.Message> | ||
| <Styled.Icon aria-hidden> | ||
| <WarningIcon /> | ||
| </Styled.Icon> | ||
| <Styled.Title>연동을 해제할까요?</Styled.Title> | ||
| <Styled.Description> | ||
| 가져온 일정은 캘린더에서 사라집니다. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
대화상자에 접근 가능한 이름을 추가하십시오.
Line 17의 대화상자에는 aria-label 또는 aria-labelledby가 없습니다. 스크린 리더는 이름 없는 대화상자로 안내합니다. Styled.Title을 참조하는 aria-labelledby를 추가하거나 고정 aria-label을 설정하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DisconnectConfirmModal/DisconnectConfirmModal.tsx`
around lines 17 - 24, Update the dialog element in DisconnectConfirmModal,
currently rendered as Styled.Card with role="dialog", to provide an accessible
name via aria-labelledby referencing Styled.Title or an appropriate fixed
aria-label.
| export const Input = styled.input` | ||
| position: absolute; | ||
| width: 1px; | ||
| height: 1px; | ||
| opacity: 0; | ||
| pointer-events: none; | ||
| `; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
키보드 포커스 표시를 추가하세요.
실제 input은 투명하므로 Tab으로 포커스해도 현재 선택 위치가 보이지 않습니다. :focus-visible 상태에서 인접한 시각적 checkbox에 outline을 적용하세요.
수정 예시
export const Input = styled.input`
position: absolute;
width: 1px;
height: 1px;
opacity: 0;
pointer-events: none;
+
+ &:focus-visible + span {
+ outline: 2px solid ${colors.primary[900]};
+ outline-offset: 2px;
+ }
`;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const Input = styled.input` | |
| position: absolute; | |
| width: 1px; | |
| height: 1px; | |
| opacity: 0; | |
| pointer-events: none; | |
| `; | |
| export const Input = styled.input` | |
| position: absolute; | |
| width: 1px; | |
| height: 1px; | |
| opacity: 0; | |
| pointer-events: none; | |
| &:focus-visible + span { | |
| outline: 2px solid ${colors.primary[900]}; | |
| outline-offset: 2px; | |
| } | |
| `; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/EventCheckItem/EventCheckItem.styles.ts`
around lines 13 - 19, Update the Input styled component so its :focus-visible
state applies an outline to the adjacent visual checkbox element. Preserve the
existing hidden-input behavior and target the neighboring checkbox selector used
by EventCheckItem rather than changing the input’s visibility.
| const disconnectNotion = () => { | ||
| clearError(); | ||
| disconnectMutation.mutate(undefined, { | ||
| onSuccess: () => setPickedDatabaseId(''), | ||
| onError: (error: Error) => onError(error.message), | ||
| }); | ||
| }; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Notion 연결 해제 성공 시 workspace 상태도 초기화하십시오.
Line 53은 pickedDatabaseId만 초기화합니다. useCalendarSync.ts의 notionWorkspaceName은 OAuth 완료 후 유지되고, CalendarLinkPanel.tsx Line 66-67은 이 값만으로도 연결 상태를 true로 계산합니다. 따라서 DELETE가 성공해도 Notion 카드가 계속 연결됨으로 표시됩니다. 성공 콜백을 상위로 전달해 notionWorkspaceName을 성공 후에만 초기화하고, 카드가 idle 상태로 전환되는 테스트를 추가하십시오.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/hooks/useNotionCalendarData.ts`
around lines 50 - 56, Update disconnectNotion so a successful disconnect also
clears the parent notionWorkspaceName state, while preserving the existing
pickedDatabaseId reset and error handling. Pass the success callback through the
relevant useCalendarSync and CalendarLinkPanel integration, and add coverage
confirming the card returns to idle only after a successful disconnect.
높이를 최대로 펴니 내용이 짧은 반복 탭에서 시트만 커지고 아래쪽이 통째로 빈 회색으로 남았다. 높이는 내용에 맡기고 위치만 올리도록 바꿨다. expanded를 stacked로 바꾸고 margin-bottom으로 180px 들어올린다. 들어올린 만큼 생기는 아래 틈은 위에 겹친 시트가 가린다.
미지정 상태 표시와 되돌리는 버튼에 '종료 기간 없음'을 같이 쓰니, 종료일을 고른 뒤에도 버튼이 현재 상태를 가리키는 것처럼 읽혔다. 버튼은 동작이므로 '종료 날짜 지우기'로 분리한다. 표시 문구는 그대로 둔다.
회색 배경에 회색 글자라 비활성으로 읽혔다. 저장 버튼과 같은 주황 배경에 흰 글자로 바꿔 누를 수 있는 동작임을 드러낸다.
종료 날짜가 없을 때도 주황 버튼이라 누를 수 있는 것처럼 보였지만 실제로는 아무 일도 일어나지 않았다. 지울 대상이 있을 때만 활성으로 바꾼다.
캘린더 화면에는 트래킹이 하나도 없어 어떤 기능이 쓰이는지 볼 수 없었다. 페이지뷰와 주요 동작 11종을 붙인다. 저장·삭제·숨김은 요청이 성공했을 때만 보내 실패를 성공으로 세지 않는다. 일정 유형, 반복 주기, 연동 제공자처럼 나눠 볼 값은 속성으로 함께 보낸다.
앞서 노이즈를 우려해 빼뒀던 입력 단계 동작까지 붙인다. 제목 초기화, 날짜 선택, 색상, 반복 주기·요일, 날짜 시트 열기, 종료 날짜 지우기, 스와이프, 연동 해제 취소 9종이다. 제목 지우기 버튼은 값이 비는 것만으로는 직접 지운 경우와 구분되지 않아 TitleInput에 onClear를 뒀다. 월 이동은 캘린더와 날짜 시트 양쪽에서 일어나 location으로 나눈다.
location은 이벤트마다 자동으로 붙는 url과 헷갈려 어느 달력인지 가리키는 이름으로 바꿨다. 값(board·picker)은 그대로다. 트래킹 헬퍼가 쓰는 곳보다 아래 선언돼 있어 위로 옮겼다.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.tsx`:
- Around line 300-305: Update each MiniCalendar instance in AddEventSheet,
including the calendars used for regular, range, and multi-event selection, so
its onMonthChange handler tracks ADMIN_EVENT.CALENDAR_MONTH_CHANGED before
calling setMonth. Pass a distinct calendarType for each calendar view, matching
the existing picker tracking behavior while preserving the current month state
updates.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 6d771103-f967-4ab2-b146-e75f5689c241
📒 Files selected for processing (13)
frontend/src/components/common/BottomSheet/BottomSheet.styles.tsfrontend/src/components/common/BottomSheet/BottomSheet.tsxfrontend/src/components/common/ResponsiveSheet/ResponsiveSheet.tsxfrontend/src/constants/eventName.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/CalendarSyncTab.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.test.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarBoard/CalendarBoard.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DatePickerSheet/DatePickerSheet.styles.tsfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DatePickerSheet/DatePickerSheet.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DayEventsModal/DayEventsModal.tsxfrontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/TitleInput/TitleInput.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
- frontend/src/pages/AdminPage/tabs/CalendarSyncTab/CalendarSyncTab.tsx
- frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/CalendarLinkPanel/CalendarLinkPanel.tsx
- frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/DatePickerSheet/DatePickerSheet.tsx
| onMonthChange={(nextMonth) => { | ||
| trackEvent(ADMIN_EVENT.CALENDAR_MONTH_CHANGED, { | ||
| calendarType: 'picker', | ||
| }); | ||
| setMonth(nextMonth); | ||
| }} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
인라인 캘린더의 월 이동도 추적해야 합니다.
Line 300은 날짜 선택기 월 이동만 CALENDAR_MONTH_CHANGED로 기록합니다. 그러나 Line 211, Line 226, Line 239의 MiniCalendar는 setMonth를 직접 호출합니다. 일반, 기간, 다중 일정에서 월 이동 데이터가 누락됩니다.
각 MiniCalendar의 onMonthChange에도 같은 이벤트를 기록하고, calendarType으로 화면을 구분하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@frontend/src/pages/AdminPage/tabs/CalendarSyncTab/components/AddEventSheet/AddEventSheet.tsx`
around lines 300 - 305, Update each MiniCalendar instance in AddEventSheet,
including the calendars used for regular, range, and multi-event selection, so
its onMonthChange handler tracks ADMIN_EVENT.CALENDAR_MONTH_CHANGED before
calling setMonth. Pass a distinct calendarType for each calendar view, matching
the existing picker tracking behavior while preserving the current month state
updates.
#️⃣연관된 이슈
📝작업 내용
관리자 캘린더의 Google·Notion 연동 UI를 Figma 시안대로 다시 만들었습니다. 기존에는 캘린더 우측 하단 아이콘 팝오버 안에 연동/해제/캘린더 선택이 숨어 있었는데, 일정 추가 시트 '일반' 탭 하단의
일정 연동하기섹션으로 옮겼습니다.1. 연동 섹션 (
CalendarLinkSection)Figma에서 주신 컴포넌트를 조합했습니다.
CalendarLinkButton미연동(일정 가져오기) /불러오는 중/연동됨(일정 연동 해제) 3상태LoadingDotsEventCheckItemCalendarLinkCardDisconnectConfirmModalCalendarLinkPanel불러오는 중과취소는 둘 다 88×37에 dot이 붙어 있어 hover 관계로 해석했습니다. 로딩 중 버튼에 마우스를 올리면 회색취소로 바뀝니다.2. 체크 = 캘린더 표시
이벤트 왼쪽 체크박스가 캘린더 표시 여부입니다. 기존
hiddenCalendarEvents(숨김 목록)를 뒤집어 씁니다.숨김 해제 API가 프론트에만 없었는데, 백엔드에는 이미
DELETE /api/integration/calendar-events/hidden이 있어서(HiddenCalendarEventController.unhideEvent) 그것만 연결했습니다.3. 시트 인터랙션
BottomSheet는ResponsiveSheet만 쓰므로 모바일·태블릿의 모든 시트에 함께 적용됩니다.없음→종료 기간 없음으로 변경4. 개발 편의
VITE_ENABLE_MSW=false를.env에 두면 개발 모드에서도 실서버로 붙습니다. 팀 기본값은 그대로 목이라 옵트인 없이는 동작이 바뀌지 않습니다.검증
tsc --noEmit통과, ESLint 0 issues, prettier 통과좌우 24px / 아래 20px로 측정 확인중점적으로 리뷰받고 싶은 부분(선택)
저장 버튼 sticky의 음수 마진입니다. 시트(
BottomSheet)·카드(DesktopCard)의 20px 패딩을 상쇄하려고SaveArea에서 좌우-20px,bottom·margin-bottom을calc(-20px - safe-area)로 잡았습니다. 부모 패딩값을 자식이 알고 있는 구조라 깨지기 쉬운데, 더 나은 방법이 있을지 봐주시면 좋겠습니다.FixedBottomButtonArea는 재사용을 검토했지만 풀페이지용이라(버튼 517px 고정, 태블릿 이하에서position: fixed로 뷰포트에 붙음) 시트 안에서는 맞지 않아 새로 만들었습니다.논의하고 싶은 부분(선택)
체크박스 기본값은 "전부 표시"로 확정했습니다. (논의 완료)
숨김 목록(
hiddenCalendarEvents)에 없는 = 한 번도 안 건드린 이벤트는 표시 상태이므로 기본이 전부 체크입니다. Figma 목업은 전부 미체크였지만, 그대로 따르면 연동 직후 캘린더가 비고 기존 사용자의 일정도 전부 사라져서 기존 동작을 유지했습니다.🫡 참고사항
DELETE /api/integration/notion/connection이 #1893의NotionOAuthController.deleteConnection(@DeleteMapping("/connection"))과 경로가 일치하는 것을 확인했습니다. #1893이 머지되기 전에는 Notion 해제가 모달까지만 뜨고 호출은 실패합니다. (Google 해제는 이 PR만으로 정상 동작)useCalendarSync가CalendarSyncTab과 패널에서 두 번 인스턴스화됩니다.useNotionOAuth가 URL 콜백 파라미터를 처리하는 effect를 갖고 있어 구조상 취약하지만, 시트는 열렸을 때만 마운트되고 OAuth는 페이지를 떠났다 돌아오므로 실제로는 겹치지 않습니다.ProviderPopover,ProviderIcons는 미사용 상태가 됐지만 삭제하지 않고 남겨뒀습니다.Summary by CodeRabbit
새 기능
개선