Skip to content

[feat] 달력 UI 변경#167

Merged
hyejun0228 merged 6 commits intodevelopfrom
151-feat-달력-ui-변경
Mar 8, 2026

Hidden character warning

The head ref may contain hidden characters: "151-feat-\ub2ec\ub825-ui-\ubcc0\uacbd"
Merged

[feat] 달력 UI 변경#167
hyejun0228 merged 6 commits intodevelopfrom
151-feat-달력-ui-변경

Conversation

@hyejun0228
Copy link
Copy Markdown
Collaborator

@hyejun0228 hyejun0228 commented Mar 8, 2026

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 주간 기반 달력 행(주 단위 레이아웃) 추가
    • 하단 시트(피킹 높이)로 일정 상세 보기 지원
    • 월 선택 UI 개선(인라인 월 피커 트리거)
    • 날짜별 오버플로우 표시("+N개") 도입
    • 날짜 문자열 파싱 유틸 추가
  • 개선 사항

    • URL 검색파라미터 기반 월/연도 상태 관리
    • 선택된 날짜로 자동 스크롤 및 강조 처리
    • 캘린더 및 일정 렌더링 단순화 및 레이아웃 정렬 최적화

@hyejun0228 hyejun0228 self-assigned this Mar 8, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 8, 2026

Walkthrough

월간 모달 기반 UI를 제거하고 URL searchParams로 연/월 상태를 관리하도록 변경했습니다. 월별 선택을 숨긴 input + 버튼으로 대체하고, 월간 그리드에서 주간 그리드(CalendarWeekRow) 기반 렌더링으로 전환하며 하단 시트와 일정 상세 자동 스크롤, 날짜 파싱 유틸(parseDateDot)을 추가했습니다.

Changes

Cohort / File(s) Summary
헤더 UI 및 날짜 상태
src/components/layout/Header/components/ScheduleHeader.tsx
모달 상태 제거, useSearchParamsyear/month 파라미터 관리. 숨긴 input[type="month"] + 버튼으로 월 선택 트리거(오토 포커스/showPicker() 호출). ChevronDownIcon 도입, 레이아웃 조정.
주간 캘린더 렌더링 추가
src/pages/Schedule/components/CalendarWeekRow.tsx
새 컴포넌트 추가: 7열 주간 그리드, 일정 바 레이아웃(시작/종료 컬럼, 레인 할당), 오버플로우 "+N개" 표시, CSS grid로 배치. (default export)
DateBox 단순화
src/pages/Schedule/components/DateBox.tsx
isToday, schedules 제거, isSelected 추가. 일정 배지 제거 및 날짜 버튼 단순화.조건부 클래스 정리(isCurrentMonth, isSelected, isSunday).
일정 상세 및 스크롤 동작
src/pages/Schedule/components/ScheduleDetail.tsx
일정 정렬에 parseDateDot 사용, 선택 날짜를 포함하는 항목 하이라이트, ref 기반 첫 하이라이트로 자동 스크롤 구현. 색상 바로 항목 표시 및 레이아웃 정리.
Schedule 페이지 재구성
src/pages/Schedule/index.tsx
월간 뷰 → 주간 CalendarWeekRow 기반으로 변경. 일정 배열을 주 컴포넌트로 전달, 하단 시트(PEEK_HEIGHT) 추가 및 스와이프/터치 기반 확장/축소 로직 도입. COLOR_LEGENDS 범례 표시 추가.
훅/유틸 변경
src/utils/hooks/useSchedule.ts, src/utils/ts/date.ts
달력 생성에서 다음 달 표시일 수를 remainingDaysInWeek 기반으로 동적 계산으로 변경. 새 유틸 parseDateDot(value: string): Date 추가(YYYY.MM.DD[ HH:MM] 파싱).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 주요 변경사항을 명확히 요약하고 있습니다. 달력 UI 변경이라는 핵심 내용을 간결하게 전달합니다.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 151-feat-달력-ui-변경

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.

Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (5)
src/components/layout/Header/components/ScheduleHeader.tsx (2)

37-43: Hidden input 접근성 고려

opacity-0pointer-events-none으로 숨긴 input은 스크린 리더에서 여전히 접근 가능합니다. 의도적이라면 괜찮지만, 완전히 숨기려면 aria-hidden="true" 또는 tabIndex={-1} 추가를 고려해보세요.

♻️ 접근성 개선 예시
         <input
           ref={inputRef}
           type="month"
           value={`${year}-${String(month).padStart(2, '0')}`}
           onChange={handleMonthChange}
-          className="pointer-events-none absolute inset-0 opacity-0"
+          className="pointer-events-none absolute inset-0 opacity-0"
+          tabIndex={-1}
+          aria-hidden="true"
         />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/layout/Header/components/ScheduleHeader.tsx` around lines 37 -
43, The hidden month input in ScheduleHeader (the input using inputRef with
value derived from year/month and onChange handler handleMonthChange) is
visually hidden via CSS but remains reachable by screen readers; update the
input to be truly hidden from assistive tech by adding either aria-hidden="true"
or tabIndex={-1} (or both) depending on whether you still need it focusable, and
document the intent in a short comment near the input so future readers know why
it’s excluded from accessibility tree.

44-47: Typography token 사용 고려

가이드라인에서 semantic typography utilities(text-h1, text-body1 등)를 우선 사용하도록 권장하고 있습니다. font-bold 대신 적절한 typography token 사용을 검토해보세요.

♻️ Typography token 적용 예시
-        <button type="button" onClick={openMonthPicker} className="flex items-center gap-1 font-bold">
+        <button type="button" onClick={openMonthPicker} className="flex items-center gap-1 text-h5">

(실제 디자인 시스템에 맞는 token으로 대체)

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

In `@src/components/layout/Header/components/ScheduleHeader.tsx` around lines 44 -
47, Replace the utility class "font-bold" on the button (the element that calls
openMonthPicker and renders year/month with ChevronDownIcon) with the
appropriate semantic typography token from our design system (e.g., text-body1
or text-h1 depending on intended prominence); locate the button in
ScheduleHeader (the one with onClick={openMonthPicker}) and swap the raw
font-weight utility for the correct semantic token so typography is consistent
with the guideline.
src/pages/Schedule/components/CalendarWeekRow.tsx (1)

4-4: 새 파일 import도 @/* alias로 통일해주세요.

이 경로가 계속 상대 경로로 남으면 동일 레이어 컴포넌트들에서 import 규칙이 쉽게 깨집니다.

As per coding guidelines, "Use path alias @/* for imports instead of relative paths".

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

In `@src/pages/Schedule/components/CalendarWeekRow.tsx` at line 4, The import in
CalendarWeekRow.tsx currently pulls DateBox via a relative path; replace that
relative import with the project path-alias form using the `@/`* alias (i.e.
import DateBox from '@/...') so imports are consistent across the layer and
follow the coding guideline; update the import statement that references the
DateBox symbol accordingly.
src/pages/Schedule/index.tsx (1)

4-4: 새 import는 @/* alias로 맞춰주세요.

추가된 CalendarWeekRow import만 상대 경로라서 이 파일의 import 규칙이 다시 섞입니다.

As per coding guidelines, "Use path alias @/* for imports instead of relative paths".

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

In `@src/pages/Schedule/index.tsx` at line 4, Replace the relative import of
CalendarWeekRow with the project path-alias form using the `@/*` alias so
imports remain consistent; locate the import statement for CalendarWeekRow in
the Schedule page (symbol: CalendarWeekRow in index.tsx) and change it from a
relative path to the equivalent `@/...` alias import that matches your project's
alias convention.
src/pages/Schedule/components/DateBox.tsx (1)

18-27: 조건부 스타일은 cn()과 토큰으로 정리해주세요.

join(' '), text-[12px], bg-[#1B2B4B]가 같이 들어오면서 이 컴포넌트만 Tailwind 병합 규칙과 디자인 토큰 규칙에서 벗어났습니다. cn()으로 조합하고 날짜 텍스트/선택 상태는 semantic typography/color token으로 맞추는 편이 유지보수에 좋습니다.

As per coding guidelines, "Use cn() utility from src/utils/ts/cn.ts to merge Tailwind CSS classes", "Prioritize color tokens from src/styles/theme.css (indigo-, blue-, background, primary, etc.)", and "Use typography tokens (text-h1 through text-h5, text-sub1 through text-sub4, text-body1 through text-body3, text-cap1 through text-cap2) from src/styles/theme.css".

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

In `@src/pages/Schedule/components/DateBox.tsx` around lines 18 - 27, Refactor the
className assembly in the DateBox component to use the cn() utility instead of
manual join(' '), replace raw utility tokens like text-[12px] and bg-[`#1B2B4B`]
with the appropriate typography and color tokens from theme.css (e.g., a
suitable text-cap or text-body token for the date text and a semantic
background/primary token for selected state), and keep conditional logic using
the existing props isCurrentMonth, isSelected, and isSunday so the expression
reads like: cn(baseTypographyToken, conditionalTokensFor(!isCurrentMonth,
isSelected, isSunday)). Ensure you import cn from src/utils/ts/cn.ts and use the
theme token class names (typography and color) instead of inline values.
🤖 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/pages/Schedule/components/ScheduleDetail.tsx`:
- Around line 39-45: The auto-scroll useEffect only depends on day, so it won't
re-run when the async schedule data arrives and the first highlighted ref
becomes available; update the effect to also depend on the first-highlight
change (e.g., include the firstHighlightedIndex value returned from
useScheduleList or include firstHighlightedRef.current) and keep the existing
guard checks (if (!firstHighlightedRef.current || !containerRef.current) return)
so the effect runs again when the highlighted item is set; locate the useEffect
in ScheduleDetail, add the new dependency (firstHighlightedIndex or
firstHighlightedRef.current) to the dependency array, and ensure the scroll
logic remains unchanged.

In `@src/pages/Schedule/index.tsx`:
- Around line 87-130: The page root currently uses a hardcoded container and
100vh-based calc (the outer <div> with class "h-[calc(100vh-44px)]" and the
bottom sheet style using PEEK_HEIGHT and translateY) which bypasses app Layout
conventions; instead wrap this page's root content in the app Layout component
and pass the Layout props showBottomNav and contentClassName from this page,
remove hard 100vh math and use the CSS viewport/safe-area vars (e.g.
--viewport-height and safe-area insets) for height calculations for the main
container and the bottom sheet transform (keep references to isSheetExpanded and
PEEK_HEIGHT for sheet behavior), so the page respects the common Layout contract
and mobile/webview safe-area adjustments.
- Around line 134-139: Replace the non-interactive div used as the bottom-sheet
handle with a real button element (keeping the same className styling) so
keyboard and mouse users can toggle the sheet; wire the existing handlers
(handleSheetTouchStart, handleSheetTouchEnd) into the new button plus an onClick
handler that toggles the sheet open/closed and keyboard handlers (e.g.,
onKeyDown for Enter/Space) so the sheet can expand past PEEK_HEIGHT; add ARIA
attributes aria-expanded and aria-controls (pointing to the sheet content id)
and type="button" to prevent form submission, and ensure any visual styles
preserved (e.g., remove default outline only via focus-visible-friendly styles)
so accessibility and behavior work across touch, mouse, and keyboard.

---

Nitpick comments:
In `@src/components/layout/Header/components/ScheduleHeader.tsx`:
- Around line 37-43: The hidden month input in ScheduleHeader (the input using
inputRef with value derived from year/month and onChange handler
handleMonthChange) is visually hidden via CSS but remains reachable by screen
readers; update the input to be truly hidden from assistive tech by adding
either aria-hidden="true" or tabIndex={-1} (or both) depending on whether you
still need it focusable, and document the intent in a short comment near the
input so future readers know why it’s excluded from accessibility tree.
- Around line 44-47: Replace the utility class "font-bold" on the button (the
element that calls openMonthPicker and renders year/month with ChevronDownIcon)
with the appropriate semantic typography token from our design system (e.g.,
text-body1 or text-h1 depending on intended prominence); locate the button in
ScheduleHeader (the one with onClick={openMonthPicker}) and swap the raw
font-weight utility for the correct semantic token so typography is consistent
with the guideline.

In `@src/pages/Schedule/components/CalendarWeekRow.tsx`:
- Line 4: The import in CalendarWeekRow.tsx currently pulls DateBox via a
relative path; replace that relative import with the project path-alias form
using the `@/`* alias (i.e. import DateBox from '@/...') so imports are consistent
across the layer and follow the coding guideline; update the import statement
that references the DateBox symbol accordingly.

In `@src/pages/Schedule/components/DateBox.tsx`:
- Around line 18-27: Refactor the className assembly in the DateBox component to
use the cn() utility instead of manual join(' '), replace raw utility tokens
like text-[12px] and bg-[`#1B2B4B`] with the appropriate typography and color
tokens from theme.css (e.g., a suitable text-cap or text-body token for the date
text and a semantic background/primary token for selected state), and keep
conditional logic using the existing props isCurrentMonth, isSelected, and
isSunday so the expression reads like: cn(baseTypographyToken,
conditionalTokensFor(!isCurrentMonth, isSelected, isSunday)). Ensure you import
cn from src/utils/ts/cn.ts and use the theme token class names (typography and
color) instead of inline values.

In `@src/pages/Schedule/index.tsx`:
- Line 4: Replace the relative import of CalendarWeekRow with the project
path-alias form using the `@/*` alias so imports remain consistent; locate the
import statement for CalendarWeekRow in the Schedule page (symbol:
CalendarWeekRow in index.tsx) and change it from a relative path to the
equivalent `@/...` alias import that matches your project's alias convention.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 907c0b2b-583e-469b-8206-be6302fe5046

📥 Commits

Reviewing files that changed from the base of the PR and between af77fef and 0c9fddb.

📒 Files selected for processing (7)
  • src/components/layout/Header/components/ScheduleHeader.tsx
  • src/pages/Schedule/components/CalendarWeekRow.tsx
  • src/pages/Schedule/components/DateBox.tsx
  • src/pages/Schedule/components/ScheduleDetail.tsx
  • src/pages/Schedule/index.tsx
  • src/utils/hooks/useSchedule.ts
  • src/utils/ts/date.ts

Comment thread src/pages/Schedule/components/ScheduleDetail.tsx Outdated
Comment on lines +87 to +130
<div className="relative flex h-[calc(100vh-44px)] flex-col overflow-hidden bg-white">
<main
className="flex w-full shrink-0 touch-pan-y flex-col gap-6 bg-white"
className="flex w-full shrink-0 touch-pan-y flex-col bg-white pt-[23px]"
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
>
<header className="flex items-center justify-center">
<MonthPicker year={year} month={month} setDate={setDate} />
</header>

<ul className="grid grid-cols-7 justify-items-center px-6 text-indigo-600">
{DAYS.map((day) => (
<li key={day}>{day}</li>
<li key={day} className="text-[13px] leading-5">
{day}
</li>
))}
</ul>

<ul className="grid grid-cols-7 gap-x-1.5 gap-y-3 px-6">
{dateList.map((date) => (
<DateBox
key={date.toISOString()}
date={date}
isCurrentMonth={isCurrentMonth(date)}
isToday={isSelectedDay(date)}
isSunday={isSunday(date)}
onClick={handleDateClick}
schedules={getSchedulesForDate(date)}
<div className="flex flex-col px-6">
{weeks.map((weekDates) => (
<CalendarWeekRow
key={weekDates[0].toISOString()}
dates={weekDates}
schedules={schedules}
isCurrentMonth={isCurrentMonth}
isSelectedDay={isSelectedDay}
isSunday={isSunday}
onDateClick={handleDateClick}
/>
))}
</ul>
</div>
</main>

<section className="flex-1 overflow-y-auto">
<ScheduleDetail year={year} month={month} day={day} />
<ul className="text-cap2 flex shrink-0 gap-3 overflow-x-auto px-6 py-3 font-medium text-[#4B5563]">
{COLOR_LEGENDS.map(({ name, color }) => (
<li key={name} className="flex shrink-0 items-center gap-1">
<span className="h-2.5 w-2.5 rounded-full" style={{ backgroundColor: color }} />
{name}
</li>
))}
</ul>

{/* 바텀시트 */}
<section
className="absolute inset-x-0 bottom-0 z-10 flex flex-col rounded-t-3xl bg-white shadow-[0_-4px_12px_rgba(0,0,0,0.06)] transition-transform duration-300 ease-out"
style={{
height: `calc(100% - 200px)`,
transform: isSheetExpanded ? 'translateY(0)' : `translateY(calc(100% - ${PEEK_HEIGHT}px))`,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

페이지 루트는 Layout/viewport 계약을 타는 게 안전합니다.

지금처럼 <div> + 100vh/고정 오프셋으로 높이를 직접 계산하면 공통 showBottomNav/contentClassName, safe-area, webview viewport 보정을 우회하게 됩니다. 바텀시트가 모바일 웹뷰에서 잘리거나 겹칠 가능성이 큽니다.

As per coding guidelines, "Pass showBottomNav (bottom tab display) and contentClassName (background color, etc.) props to Layout component" and "Maintain mobile webview responsive design with --viewport-height and safe area variables".

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

In `@src/pages/Schedule/index.tsx` around lines 87 - 130, The page root currently
uses a hardcoded container and 100vh-based calc (the outer <div> with class
"h-[calc(100vh-44px)]" and the bottom sheet style using PEEK_HEIGHT and
translateY) which bypasses app Layout conventions; instead wrap this page's root
content in the app Layout component and pass the Layout props showBottomNav and
contentClassName from this page, remove hard 100vh math and use the CSS
viewport/safe-area vars (e.g. --viewport-height and safe-area insets) for height
calculations for the main container and the bottom sheet transform (keep
references to isSheetExpanded and PEEK_HEIGHT for sheet behavior), so the page
respects the common Layout contract and mobile/webview safe-area adjustments.

Comment on lines +134 to +139
<div
className="flex shrink-0 justify-center pt-3 pb-2"
onTouchStart={handleSheetTouchStart}
onTouchEnd={handleSheetTouchEnd}
>
<div className="h-1 w-8 rounded-full bg-[#D1D5DB]" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

바텀시트 핸들은 실제 버튼이어야 합니다.

지금은 터치 이벤트만 달린 div라서 마우스/키보드 환경에서는 확장/축소가 불가능합니다. 현재 구조상 상세 일정이 PEEK_HEIGHT 이상으로 열리지 않아 접근성이 막힙니다.

🔧 예시
-        <div
-          className="flex shrink-0 justify-center pt-3 pb-2"
-          onTouchStart={handleSheetTouchStart}
-          onTouchEnd={handleSheetTouchEnd}
-        >
-          <div className="h-1 w-8 rounded-full bg-[`#D1D5DB`]" />
-        </div>
+        <button
+          type="button"
+          aria-expanded={isSheetExpanded}
+          className="flex shrink-0 justify-center pt-3 pb-2"
+          onClick={() => setIsSheetExpanded((prev) => !prev)}
+          onTouchStart={handleSheetTouchStart}
+          onTouchEnd={handleSheetTouchEnd}
+        >
+          <span className="h-1 w-8 rounded-full bg-[`#D1D5DB`]" />
+        </button>
📝 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
<div
className="flex shrink-0 justify-center pt-3 pb-2"
onTouchStart={handleSheetTouchStart}
onTouchEnd={handleSheetTouchEnd}
>
<div className="h-1 w-8 rounded-full bg-[#D1D5DB]" />
<button
type="button"
aria-expanded={isSheetExpanded}
className="flex shrink-0 justify-center pt-3 pb-2"
onClick={() => setIsSheetExpanded((prev) => !prev)}
onTouchStart={handleSheetTouchStart}
onTouchEnd={handleSheetTouchEnd}
>
<span className="h-1 w-8 rounded-full bg-[`#D1D5DB`]" />
</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Schedule/index.tsx` around lines 134 - 139, Replace the
non-interactive div used as the bottom-sheet handle with a real button element
(keeping the same className styling) so keyboard and mouse users can toggle the
sheet; wire the existing handlers (handleSheetTouchStart, handleSheetTouchEnd)
into the new button plus an onClick handler that toggles the sheet open/closed
and keyboard handlers (e.g., onKeyDown for Enter/Space) so the sheet can expand
past PEEK_HEIGHT; add ARIA attributes aria-expanded and aria-controls (pointing
to the sheet content id) and type="button" to prevent form submission, and
ensure any visual styles preserved (e.g., remove default outline only via
focus-visible-friendly styles) so accessibility and behavior work across touch,
mouse, and keyboard.

Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (4)
src/pages/Schedule/components/ScheduleDetail.tsx (4)

7-7: 타입명 PascalCase 컨벤션

TypeScript 타입명은 PascalCase를 사용하는 것이 관례입니다.

수정 제안
-type scheduleDetailProps = {
+type ScheduleDetailProps = {

Line 13도 함께 수정:

-function ScheduleDetail({ year, month, day }: scheduleDetailProps) {
+function ScheduleDetail({ year, month, day }: ScheduleDetailProps) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Schedule/components/ScheduleDetail.tsx` at line 7, Rename the
TypeScript type scheduleDetailProps to PascalCase (ScheduleDetailProps) and
update every usage to the new name (including the props type annotation for the
ScheduleDetail component and any other references in the file), ensuring
exported/imported names are adjusted if this type is exported or used elsewhere;
keep the same shape of the type while only changing its identifier to
ScheduleDetailProps.

59-59: 조건부 클래스에 cn() 유틸리티 사용 권장

코딩 가이드라인에 따라 Tailwind 클래스 병합 시 cn() 유틸리티를 사용해야 합니다.

수정 예시 (Line 59)
+import { cn } from '@/utils/ts/cn';

-className={`flex h-[70px] shrink-0 items-stretch self-stretch overflow-hidden rounded-lg border border-[`#F4F6F9`] bg-white transition-opacity ${!highlighted ? 'opacity-40' : ''}`}
+className={cn(
+  'flex h-[70px] shrink-0 items-stretch self-stretch overflow-hidden rounded-lg border border-[`#F4F6F9`] bg-white transition-opacity',
+  !highlighted && 'opacity-40'
+)}

Also applies to: 63-63, 66-66

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

In `@src/pages/Schedule/components/ScheduleDetail.tsx` at line 59, Replace the
inline template-string Tailwind class concatenations with the cn() utility in
the ScheduleDetail component: for the element that currently uses
className={`flex h-[70px] ... ${!highlighted ? 'opacity-40' : ''}`} switch to
cn(...) and pass static classes plus a conditional object/array for the opacity
class, and do the same for the other two occurrences in this component (the
other className props around the same block). Ensure you import cn if not
already and keep the exact class tokens but let cn handle merging/conditional
inclusion.

49-49: 시맨틱 타이포그래피 토큰 사용 고려

text-[16px], text-[14px] 등 커스텀 크기 대신 theme.css의 타이포그래피 토큰(text-body1, text-body2 등)을 우선 사용하면 일관성을 유지할 수 있습니다.

Also applies to: 63-63, 66-66

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

In `@src/pages/Schedule/components/ScheduleDetail.tsx` at line 49, In
ScheduleDetail.tsx replace hard-coded utility classes like "text-[16px]" and
"text-[14px]" on the span elements in the ScheduleDetail component with the
semantic typography tokens defined in theme.css (e.g., "text-body1",
"text-body2" or the project's equivalent) to ensure consistent typography;
update each span that currently uses "text-[16px]" / "text-[14px]" (and nearby
spans that use similar custom sizes) to the appropriate semantic class and
remove the custom bracketed size classes.

26-34: isOnSelectedDayfirstHighlightedIndex 메모이제이션 고려

isOnSelectedDay가 매 렌더마다 새로 생성되고, map 내에서 반복 호출됩니다. firstHighlightedIndex와 함께 useMemo로 감싸면 불필요한 재계산을 줄일 수 있습니다.

예시
const { firstHighlightedIndex, isOnSelectedDay } = useMemo(() => {
  const isOnDay = (startedAt: string, endedAt: string) => {
    const start = parseDateDot(startedAt);
    const end = parseDateDot(endedAt);
    return start <= selectedDate && selectedDate <= end;
  };
  const idx = sortedSchedules.findIndex(({ startedAt, endedAt }) => isOnDay(startedAt, endedAt));
  return { firstHighlightedIndex: idx, isOnSelectedDay: isOnDay };
}, [sortedSchedules, selectedDate]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Schedule/components/ScheduleDetail.tsx` around lines 26 - 34, Wrap
the creation of isOnSelectedDay and the computation of firstHighlightedIndex in
a useMemo to avoid recreating the function and re-scanning sortedSchedules on
every render: create an inner function (e.g., isOnDay) that uses parseDateDot
and selectedDate, compute idx = sortedSchedules.findIndex(...) inside the same
useMemo, and return both { firstHighlightedIndex: idx, isOnSelectedDay: isOnDay
}; use dependencies [sortedSchedules, selectedDate] so callers of
isOnSelectedDay and the firstHighlightedIndex value use the memoized versions.
🤖 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/pages/Schedule/components/ScheduleDetail.tsx`:
- Line 5: Replace the relative import in ScheduleDetail.tsx with the path-alias
version: change the import of useScheduleList from '../hooks/useGetSchedules' to
the project alias form (e.g. '@/pages/Schedule/hooks/useGetSchedules') so it
uses the `@/`* alias; update the import statement referencing the useScheduleList
symbol accordingly and ensure any ESLint/TS path mappings remain valid.

---

Nitpick comments:
In `@src/pages/Schedule/components/ScheduleDetail.tsx`:
- Line 7: Rename the TypeScript type scheduleDetailProps to PascalCase
(ScheduleDetailProps) and update every usage to the new name (including the
props type annotation for the ScheduleDetail component and any other references
in the file), ensuring exported/imported names are adjusted if this type is
exported or used elsewhere; keep the same shape of the type while only changing
its identifier to ScheduleDetailProps.
- Line 59: Replace the inline template-string Tailwind class concatenations with
the cn() utility in the ScheduleDetail component: for the element that currently
uses className={`flex h-[70px] ... ${!highlighted ? 'opacity-40' : ''}`} switch
to cn(...) and pass static classes plus a conditional object/array for the
opacity class, and do the same for the other two occurrences in this component
(the other className props around the same block). Ensure you import cn if not
already and keep the exact class tokens but let cn handle merging/conditional
inclusion.
- Line 49: In ScheduleDetail.tsx replace hard-coded utility classes like
"text-[16px]" and "text-[14px]" on the span elements in the ScheduleDetail
component with the semantic typography tokens defined in theme.css (e.g.,
"text-body1", "text-body2" or the project's equivalent) to ensure consistent
typography; update each span that currently uses "text-[16px]" / "text-[14px]"
(and nearby spans that use similar custom sizes) to the appropriate semantic
class and remove the custom bracketed size classes.
- Around line 26-34: Wrap the creation of isOnSelectedDay and the computation of
firstHighlightedIndex in a useMemo to avoid recreating the function and
re-scanning sortedSchedules on every render: create an inner function (e.g.,
isOnDay) that uses parseDateDot and selectedDate, compute idx =
sortedSchedules.findIndex(...) inside the same useMemo, and return both {
firstHighlightedIndex: idx, isOnSelectedDay: isOnDay }; use dependencies
[sortedSchedules, selectedDate] so callers of isOnSelectedDay and the
firstHighlightedIndex value use the memoized versions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: de39b110-bdf0-4bde-b978-95bdc3967ea0

📥 Commits

Reviewing files that changed from the base of the PR and between 0c9fddb and 1298066.

📒 Files selected for processing (1)
  • src/pages/Schedule/components/ScheduleDetail.tsx

import { SCHEDULE_COLOR } from '@/constants/schedule';
import { formatScheduleTime } from '@/utils/hooks/useFormatTime';
import { parseDateDot } from '@/utils/ts/date';
import { useScheduleList } from '../hooks/useGetSchedules';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

상대 경로 대신 @/* 별칭 사용 필요

코딩 가이드라인에 따라 상대 경로 대신 path alias를 사용해야 합니다.

수정 제안
-import { useScheduleList } from '../hooks/useGetSchedules';
+import { useScheduleList } from '@/pages/Schedule/hooks/useGetSchedules';
📝 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
import { useScheduleList } from '../hooks/useGetSchedules';
import { useScheduleList } from '@/pages/Schedule/hooks/useGetSchedules';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/Schedule/components/ScheduleDetail.tsx` at line 5, Replace the
relative import in ScheduleDetail.tsx with the path-alias version: change the
import of useScheduleList from '../hooks/useGetSchedules' to the project alias
form (e.g. '@/pages/Schedule/hooks/useGetSchedules') so it uses the `@/`* alias;
update the import statement referencing the useScheduleList symbol accordingly
and ensure any ESLint/TS path mappings remain valid.

@hyejun0228 hyejun0228 merged commit f0f3bf1 into develop Mar 8, 2026
2 checks passed
@ff1451 ff1451 deleted the 151-feat-달력-ui-변경 branch April 7, 2026 09:33
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.

1 participant