Skip to content

[FEAT] 히스토리 페이지 UI구현#47

Open
andada-creator wants to merge 3 commits into
developfrom
feat/#45-history-dashboard
Open

[FEAT] 히스토리 페이지 UI구현#47
andada-creator wants to merge 3 commits into
developfrom
feat/#45-history-dashboard

Conversation

@andada-creator

@andada-creator andada-creator commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

#️⃣ 관련 이슈


📝 작업 요약

이번 PR에서 수행한 작업을 간단히 설명해주세요.

대시보드 히스토리 페이지 및 하위 컴포넌트 UI 구현

🔧 변경 사항

-히스토리 페이지 UI구현

  • 최근 4주 학습 추이 그래프 구현(WeeklyTrendChart)
  • 영역별 성장 변화 그래프 구현(GrowthProgressSection)
  • 이번주 연주 요약 카드 구현(HistorySummaryCards)
  • 최근 연주 리스트 구현(HistoryRecentPractices)
  • 최근 연주 카드 클릭 시 해당 상세 페이지(/history/:historyId)로 이동하는 라우팅 기능 연결

💬 리뷰어 참고 사항

리뷰 시 참고하면 좋을 점이나 고민했던 부분이 있다면 작성해주세요.


✅ 체크리스트

  • 커밋 메시지 컨벤션을 준수했습니다.
  • 로컬에서 정상 동작을 확인했습니다.
  • UI 변경 사항을 직접 확인했습니다.
  • 불필요한 console.log를 제거했습니다.
  • 관련 이슈를 연결했습니다.

Summary by CodeRabbit

  • 새 기능
    • 대시보드에 출석 현황, 연습 시간, 진행 중인 학습, 추천 학습, 최근 연습 및 알림을 표시합니다.
    • 학습·연습·알림 항목에서 관련 상세 화면으로 바로 이동할 수 있습니다.
    • 히스토리 화면에 요약 카드, 성장 변화, 주간 추이 차트, 최근 연습 기록을 추가했습니다.
    • 최근 연습 항목의 재생 및 일시정지 기능을 제공합니다.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

히스토리 페이지에 통계 카드, 성장 지표, 주간 추이 차트, 최근 연주 목록을 추가했다. 메인 대시보드에는 출석·학습·추천·연습·알림 컴포넌트를 구성하고 라우팅 및 TypeScript 설정을 갱신했다.

Changes

히스토리 화면

Layer / File(s) Summary
히스토리 통계 시각화
src/components/history/HistorySummaryCards.tsx, src/components/history/GrowthProgressSection.tsx, src/components/history/WeeklyTrendChart.tsx
요약 수치 카드, 4개 성장 지표 진행 바, 주간 점수 기반 SVG 추이 차트를 추가했다.
최근 연주 목록
src/components/history/HistoryRecentPractices.tsx
연주 이력 카드에 점수 상태별 아이콘, 메타 정보, 조건부 점수 변화 및 히스토리 이동을 구현했다.
히스토리 페이지 구성
src/pages/history/HistoryPage.tsx
더미 연주 데이터를 정의하고 통계 및 최근 연주 컴포넌트를 전체 히스토리 레이아웃에 배치했다.

메인 대시보드 UI

Layer / File(s) Summary
출석 및 학습 위젯
src/components/main/AttendanceSection.tsx, src/components/main/LearningBanner.tsx, src/pages/main/components/RecommandLearn.tsx
출석 상태·연습 시간, 진행 중 학습 상태, 추천 학습 카드를 데이터 기반으로 표시한다.
연습 및 알림 상호작용
src/components/main/RecentPractices.tsx, src/components/main/DashboardNoti.tsx
연습 항목 재생 토글과 알림 읽음 처리 후 히스토리 라우팅을 추가했다.
대시보드 연결 및 컴파일 설정
src/pages/main/MainPage.tsx, tsconfig.app.json
메인 컴포넌트 import를 @/components/... 별칭으로 변경하고 ignoreDeprecations 설정을 추가했다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant HistoryPage
  participant HistoryRecentPractices
  participant Router
  HistoryPage->>HistoryRecentPractices: 더미 연주 데이터 전달
  HistoryRecentPractices->>Router: 카드 클릭 시 /history/{practiceId} 이동
Loading

Suggested labels: ✨ Feat, 🎨 Style

Suggested reviewers: kxxnayun

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning tsconfig의 ignoreDeprecations 추가는 히스토리 대시보드 UI 구현 목표와 직접 관련이 없어 범위를 벗어납니다. 해당 설정 변경의 필요성을 명확히 하거나, 히스토리 UI 구현에 필요한 경우에만 최소한으로 포함하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed 히스토리 페이지 UI, 요약 카드, 성장/추이 차트, 최근 연주 목록, 더미 데이터, 라우팅이 이슈 #45 요구와 일치합니다.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 히스토리 페이지 UI와 관련 하위 컴포넌트 구현을 잘 요약한 제목입니다.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#45-history-dashboard

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.

@coderabbitai coderabbitai 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.

Actionable comments posted: 9

🧹 Nitpick comments (7)
src/components/history/HistorySummaryCards.tsx (1)

5-169: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

카드 3개가 완전히 동일한 구조를 반복하고, className과 inline style이 같은 속성을 중복 선언합니다.

정확도/연습 시간/완료 세션 카드가 값만 다르고 JSX 구조가 동일하며, 각 카드마다 className="flex flex-col ..."style={{ display:'flex', flexDirection:'column', ... }}가 같은 레이아웃을 중복 표현하고 있습니다. 데이터 배열을 만들어 map으로 렌더링하고, 스타일은 Tailwind 클래스 또는 inline style 한쪽으로 통일하는 것을 권장합니다. 향후 실제 API 데이터를 받도록 props화하기도 더 쉬워집니다.

♻️ 제안 리팩터 예시
+const cards = [
+  { icon: <AccuracyIcon />, label: '정확도', value: '91%', diffText: '전 주보다 4% 증가했어요' },
+  { icon: <PracticeTimeIcon />, label: '연습 시간', value: '65분', diffText: '전 주보다 18분 증가했어요' },
+  { icon: <SessionIcon />, label: '완료 세션', value: '6개', diffText: '전 주보다 2개 증가했어요' },
+];
+
 export default function HistorySummaryCards() {
   return (
     <div className="flex w-[1196px] gap-[16px]">
-      {/* 1번 카드: 정확도 */}
-      <div className="flex flex-1 flex-col border border-[`#2E3142`]" style={{ ... }}>
-        ...
-      </div>
-      {/* 2번 카드, 3번 카드 반복 */}
+      {cards.map((card) => (
+        <SummaryCard key={card.label} {...card} />
+      ))}
     </div>
   );
 }
🤖 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 `@src/components/history/HistorySummaryCards.tsx` around lines 5 - 169,
Refactor the repeated card JSX in HistorySummaryCards into a shared data
structure and map-rendered card component, keeping each card’s label, icon,
value, and comparison text distinct. Remove duplicated layout declarations by
using either the existing Tailwind classes or inline styles, not both, while
preserving the current visual output and making the data easy to replace with
props or API values later.
src/components/history/WeeklyTrendChart.tsx (1)

4-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

포인트 좌표(x, y)가 score로부터 계산되지 않고 수동으로 근사되어 Y축 그리드와 미세하게 어긋납니다.

예를 들어 지난주 score=63인데 y=100으로 지정되어 있어, Y축 레이블(60→104px, 80→52px) 기준 선형 계산값(약 96px)과 차이가 있습니다. score 기반으로 y를 계산하는 헬퍼 함수를 두면 그래프 정확도와 유지보수성이 개선됩니다.

🤖 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 `@src/components/history/WeeklyTrendChart.tsx` around lines 4 - 9, Update the
points definition in WeeklyTrendChart so each point’s y coordinate is calculated
from its score using a shared helper that applies the chart’s Y-axis linear
mapping, rather than hardcoded approximations; preserve the existing x
coordinates, labels, and scores.
src/components/history/GrowthProgressSection.tsx (1)

11-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

4개 블록이 동일 구조를 반복하며, 진행 바 폭이 실제 값과 일정한 공식으로 매핑되지 않습니다.

스케일/텐션/진행/코드 연결 블록이 값만 다르고 구조가 동일하여 배열 기반 렌더링으로 단순화할 수 있습니다. (HistorySummaryCards.tsx와 동일한 근본 원인이므로 상세 제안은 consolidated 코멘트를 참고해주세요.)

🤖 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 `@src/components/history/GrowthProgressSection.tsx` around lines 11 - 56,
Refactor the repeated blocks in GrowthProgressSection into an array-driven
render, storing each item’s label, value, color, and direction-specific progress
data. Render the shared layout and bar markup from that array, and replace
hardcoded widths with a consistent value-to-progress mapping formula so every
bar reflects its actual value while preserving positive and negative alignment.
src/components/main/AttendanceSection.tsx (1)

20-66: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

"오늘" 판별이 완료 상태(TODAY_COMPLETED)에만 의존합니다.

오늘 아직 출석/연습을 완료하지 않은 경우 weeklyAttendanceTODAY_COMPLETED 상태를 가진 항목이 없어 todayIndex가 -1이 되고, 오늘 요일이 미래 요일과 동일하게(점선 원 + 회색 라벨) 렌더링되어 구분되지 않습니다. "오늘 여부"와 "완료 여부"를 분리한 별도 필드(예: isToday)로 모델링하는 것을 권장합니다.

♻️ 제안 수정 예시
 interface AttendanceDay {
   dayOfWeek: string;
   label: string;
   status: string;
+  isToday: boolean;
 }
...
-  const todayItem = streak.weeklyAttendance.find((d) => d.status === 'TODAY_COMPLETED');
-  const todayIndex = todayItem ? dayOrder.indexOf(todayItem.dayOfWeek) : -1;
+  const todayItem = streak.weeklyAttendance.find((d) => d.isToday);
+  const todayIndex = todayItem ? dayOrder.indexOf(todayItem.dayOfWeek) : -1;
...
-              const isToday = item.status === 'TODAY_COMPLETED';
+              const isToday = item.isToday;
🤖 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 `@src/components/main/AttendanceSection.tsx` around lines 20 - 66, Update the
AttendanceSection today-detection logic so it does not derive todayIndex solely
from the TODAY_COMPLETED status. Use the attendance model’s explicit today
indicator, such as isToday, to identify today regardless of completion state,
while keeping isAttended based on completion statuses and preserving the
existing missed-day calculation and rendering behavior.
src/components/main/LearningBanner.tsx (1)

51-52: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

level 이분화 로직이 다른 값을 "중급"으로 잘못 표시할 수 있습니다.

level이 문자열 타입이라 ADVANCED가 아닌 값은 전부 "중급"으로 렌더링됩니다. 만약 BEGINNER 등 세 번째 레벨이 존재한다면 잘못된 뱃지가 표시됩니다. 동일한 뱃지 로직이 RecommandLearn.tsx(라인 22, 57-79)에도 중복되어 있어 근본 원인이 같습니다(하단 통합 코멘트 참고).

Also applies to: 77-100

🤖 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 `@src/components/main/LearningBanner.tsx` around lines 51 - 52, Update the
level-to-badge logic in LearningBanner and the duplicated logic in
RecommandLearn so only explicitly supported levels render their corresponding
badges; do not treat every non-ADVANCED string as intermediate. Handle values
such as BEGINNER with the correct badge mapping or an intentional fallback,
preserving existing ADVANCED behavior.
src/components/main/RecommandLearn.tsx (1)

22-22: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

난이도 뱃지 이분화 로직이 두 파일에 중복되고, ADVANCED 외 값은 전부 "중급"으로 표시됩니다. 공유 LevelBadge 컴포넌트로 추출하고, level을 실제 가능한 값들의 union 타입으로 명시하는 것을 권장합니다.

  • src/components/main/LearningBanner.tsx#L51-52,77-100: isAdvanced 판별 및 뱃지 렌더링을 공유 컴포넌트로 이전.
  • src/components/main/RecommandLearn.tsx#L22,57-79: 동일한 뱃지 렌더링을 같은 공유 컴포넌트로 이전.
🤖 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 `@src/components/main/RecommandLearn.tsx` at line 22, LearningBanner과
RecommandLearn에 중복된 난이도 뱃지 로직을 공유 LevelBadge 컴포넌트로 추출하세요. LevelBadge의 level
prop은 실제 가능한 난이도 값들의 union 타입으로 선언하고, 각 값이 올바른 뱃지로 매핑되도록 하여 ADVANCED 외 값을 모두
중급으로 처리하지 않게 하세요. 두 컴포넌트의 isAdvanced 판별과 뱃지 렌더링을 제거하고 동일한 LevelBadge를 사용하도록
변경하세요.
tsconfig.app.json (1)

13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

현재 프로젝트에서는 ignoreDeprecations 추가가 필요하지 않습니다.

프로젝트는 TypeScript ~5.8.3를 사용하고 있어 6.0 deprecation 정책과 호환되지 않으며, tsconfig.app.json에도 해당 플래그를 유발하는 6.0 deprecated 설정이 보이지 않습니다. 이 플래그는 현재 TypeScript 버전과 project 환경에서 삭제하는 편이 낫습니다.

🤖 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 `@tsconfig.app.json` at line 13, Remove the unnecessary ignoreDeprecations
option from tsconfig.app.json, leaving the remaining compiler configuration
unchanged.
🤖 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 `@src/components/history/HistoryRecentPractices.tsx`:
- Around line 26-41: Make the clickable card in the recent practices rendering
keyboard accessible by adding button semantics, a focusable tabIndex, and key
handling that triggers the existing navigate action for Enter and Space.
Preserve the current click navigation to the practice history route.

In `@src/components/history/WeeklyTrendChart.tsx`:
- Around line 17-30: Update the improvement text in WeeklyTrendChart to match
the points data: display +30점 instead of the hardcoded +9점, preserving the
existing styling and surrounding Korean text.
- Around line 122-171: Move the conditional <defs> containing
filter0_d_331_16405 inside the corresponding <svg> in the weekly trend point
rendering. Keep the 이번주 filter reference and glow behavior unchanged, and remove
the <defs> rendered as a direct child of the surrounding div.

In `@src/components/main/DashboardNoti.tsx`:
- Around line 18-28: Update the clickable notification div in DashboardNoti to
support keyboard activation using the same accessibility pattern as
LearningBanner, RecommandLearn, and RecentPractices, while preserving the
existing onReadItem and history navigation behavior.
- Around line 47-52: Update the notification text rendering in DashboardNoti to
use each NotiItem’s type and content instead of always appending the hardcoded
comment message. Preserve the title and timeLabel display, and ensure comment,
analysis, achievement, and complete notifications each render their
corresponding content.

In `@src/components/main/LearningBanner.tsx`:
- Line 1: Update the clickable card/row elements in LearningBanner,
RecommandLearn, RecentPractices, and DashboardNoti to be keyboard accessible by
adding role="button", tabIndex={0}, and onKeyDown handling that triggers the
existing click/navigation behavior for Enter and Space while preserving mouse
behavior.
- Around line 54-71: Update the clickable card in LearningBanner’s returned div
to provide keyboard accessibility: add button semantics, make it focusable, and
handle Enter and Space key presses by triggering the same
navigate(`/learn/curriculum/${learningId}`) action as onClick. Apply the same
interaction pattern to the corresponding clickable elements in RecommandLearn,
RecentPractices, and DashboardNoti.

In `@src/components/main/RecentPractices.tsx`:
- Around line 41-57: Update the clickable row div in RecentPractices to support
keyboard access like the established pattern in LearningBanner, RecommandLearn,
and DashboardNoti: make it focusable and invoke the existing navigate action for
Enter and Space key events while preserving mouse-click behavior.

In `@src/components/main/RecommandLearn.tsx`:
- Around line 25-40: Update the clickable card div in RecommandLearn to provide
keyboard accessibility, following the existing pattern used by LearningBanner.
Add the appropriate focusable and interactive keyboard behavior alongside its
onClick handler, while preserving the current navigation target and styling.

---

Nitpick comments:
In `@src/components/history/GrowthProgressSection.tsx`:
- Around line 11-56: Refactor the repeated blocks in GrowthProgressSection into
an array-driven render, storing each item’s label, value, color, and
direction-specific progress data. Render the shared layout and bar markup from
that array, and replace hardcoded widths with a consistent value-to-progress
mapping formula so every bar reflects its actual value while preserving positive
and negative alignment.

In `@src/components/history/HistorySummaryCards.tsx`:
- Around line 5-169: Refactor the repeated card JSX in HistorySummaryCards into
a shared data structure and map-rendered card component, keeping each card’s
label, icon, value, and comparison text distinct. Remove duplicated layout
declarations by using either the existing Tailwind classes or inline styles, not
both, while preserving the current visual output and making the data easy to
replace with props or API values later.

In `@src/components/history/WeeklyTrendChart.tsx`:
- Around line 4-9: Update the points definition in WeeklyTrendChart so each
point’s y coordinate is calculated from its score using a shared helper that
applies the chart’s Y-axis linear mapping, rather than hardcoded approximations;
preserve the existing x coordinates, labels, and scores.

In `@src/components/main/AttendanceSection.tsx`:
- Around line 20-66: Update the AttendanceSection today-detection logic so it
does not derive todayIndex solely from the TODAY_COMPLETED status. Use the
attendance model’s explicit today indicator, such as isToday, to identify today
regardless of completion state, while keeping isAttended based on completion
statuses and preserving the existing missed-day calculation and rendering
behavior.

In `@src/components/main/LearningBanner.tsx`:
- Around line 51-52: Update the level-to-badge logic in LearningBanner and the
duplicated logic in RecommandLearn so only explicitly supported levels render
their corresponding badges; do not treat every non-ADVANCED string as
intermediate. Handle values such as BEGINNER with the correct badge mapping or
an intentional fallback, preserving existing ADVANCED behavior.

In `@src/components/main/RecommandLearn.tsx`:
- Line 22: LearningBanner과 RecommandLearn에 중복된 난이도 뱃지 로직을 공유 LevelBadge 컴포넌트로
추출하세요. LevelBadge의 level prop은 실제 가능한 난이도 값들의 union 타입으로 선언하고, 각 값이 올바른 뱃지로
매핑되도록 하여 ADVANCED 외 값을 모두 중급으로 처리하지 않게 하세요. 두 컴포넌트의 isAdvanced 판별과 뱃지 렌더링을 제거하고
동일한 LevelBadge를 사용하도록 변경하세요.

In `@tsconfig.app.json`:
- Line 13: Remove the unnecessary ignoreDeprecations option from
tsconfig.app.json, leaving the remaining compiler configuration unchanged.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f80bae3-990e-4f32-9200-afaaf4e3e29a

📥 Commits

Reviewing files that changed from the base of the PR and between f6ab281 and 02b07d7.

📒 Files selected for processing (12)
  • src/components/history/GrowthProgressSection.tsx
  • src/components/history/HistoryRecentPractices.tsx
  • src/components/history/HistorySummaryCards.tsx
  • src/components/history/WeeklyTrendChart.tsx
  • src/components/main/AttendanceSection.tsx
  • src/components/main/DashboardNoti.tsx
  • src/components/main/LearningBanner.tsx
  • src/components/main/RecentPractices.tsx
  • src/components/main/RecommandLearn.tsx
  • src/pages/history/HistoryPage.tsx
  • src/pages/main/MainPage.tsx
  • tsconfig.app.json

@coderabbitai coderabbitai 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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 9

🧹 Nitpick comments (7)
src/components/history/HistorySummaryCards.tsx (1)

5-169: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

카드 3개가 완전히 동일한 구조를 반복하고, className과 inline style이 같은 속성을 중복 선언합니다.

정확도/연습 시간/완료 세션 카드가 값만 다르고 JSX 구조가 동일하며, 각 카드마다 className="flex flex-col ..."style={{ display:'flex', flexDirection:'column', ... }}가 같은 레이아웃을 중복 표현하고 있습니다. 데이터 배열을 만들어 map으로 렌더링하고, 스타일은 Tailwind 클래스 또는 inline style 한쪽으로 통일하는 것을 권장합니다. 향후 실제 API 데이터를 받도록 props화하기도 더 쉬워집니다.

♻️ 제안 리팩터 예시
+const cards = [
+  { icon: <AccuracyIcon />, label: '정확도', value: '91%', diffText: '전 주보다 4% 증가했어요' },
+  { icon: <PracticeTimeIcon />, label: '연습 시간', value: '65분', diffText: '전 주보다 18분 증가했어요' },
+  { icon: <SessionIcon />, label: '완료 세션', value: '6개', diffText: '전 주보다 2개 증가했어요' },
+];
+
 export default function HistorySummaryCards() {
   return (
     <div className="flex w-[1196px] gap-[16px]">
-      {/* 1번 카드: 정확도 */}
-      <div className="flex flex-1 flex-col border border-[`#2E3142`]" style={{ ... }}>
-        ...
-      </div>
-      {/* 2번 카드, 3번 카드 반복 */}
+      {cards.map((card) => (
+        <SummaryCard key={card.label} {...card} />
+      ))}
     </div>
   );
 }
🤖 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 `@src/components/history/HistorySummaryCards.tsx` around lines 5 - 169,
Refactor the repeated card JSX in HistorySummaryCards into a shared data
structure and map-rendered card component, keeping each card’s label, icon,
value, and comparison text distinct. Remove duplicated layout declarations by
using either the existing Tailwind classes or inline styles, not both, while
preserving the current visual output and making the data easy to replace with
props or API values later.
src/components/history/WeeklyTrendChart.tsx (1)

4-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

포인트 좌표(x, y)가 score로부터 계산되지 않고 수동으로 근사되어 Y축 그리드와 미세하게 어긋납니다.

예를 들어 지난주 score=63인데 y=100으로 지정되어 있어, Y축 레이블(60→104px, 80→52px) 기준 선형 계산값(약 96px)과 차이가 있습니다. score 기반으로 y를 계산하는 헬퍼 함수를 두면 그래프 정확도와 유지보수성이 개선됩니다.

🤖 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 `@src/components/history/WeeklyTrendChart.tsx` around lines 4 - 9, Update the
points definition in WeeklyTrendChart so each point’s y coordinate is calculated
from its score using a shared helper that applies the chart’s Y-axis linear
mapping, rather than hardcoded approximations; preserve the existing x
coordinates, labels, and scores.
src/components/history/GrowthProgressSection.tsx (1)

11-56: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

4개 블록이 동일 구조를 반복하며, 진행 바 폭이 실제 값과 일정한 공식으로 매핑되지 않습니다.

스케일/텐션/진행/코드 연결 블록이 값만 다르고 구조가 동일하여 배열 기반 렌더링으로 단순화할 수 있습니다. (HistorySummaryCards.tsx와 동일한 근본 원인이므로 상세 제안은 consolidated 코멘트를 참고해주세요.)

🤖 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 `@src/components/history/GrowthProgressSection.tsx` around lines 11 - 56,
Refactor the repeated blocks in GrowthProgressSection into an array-driven
render, storing each item’s label, value, color, and direction-specific progress
data. Render the shared layout and bar markup from that array, and replace
hardcoded widths with a consistent value-to-progress mapping formula so every
bar reflects its actual value while preserving positive and negative alignment.
src/components/main/AttendanceSection.tsx (1)

20-66: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

"오늘" 판별이 완료 상태(TODAY_COMPLETED)에만 의존합니다.

오늘 아직 출석/연습을 완료하지 않은 경우 weeklyAttendanceTODAY_COMPLETED 상태를 가진 항목이 없어 todayIndex가 -1이 되고, 오늘 요일이 미래 요일과 동일하게(점선 원 + 회색 라벨) 렌더링되어 구분되지 않습니다. "오늘 여부"와 "완료 여부"를 분리한 별도 필드(예: isToday)로 모델링하는 것을 권장합니다.

♻️ 제안 수정 예시
 interface AttendanceDay {
   dayOfWeek: string;
   label: string;
   status: string;
+  isToday: boolean;
 }
...
-  const todayItem = streak.weeklyAttendance.find((d) => d.status === 'TODAY_COMPLETED');
-  const todayIndex = todayItem ? dayOrder.indexOf(todayItem.dayOfWeek) : -1;
+  const todayItem = streak.weeklyAttendance.find((d) => d.isToday);
+  const todayIndex = todayItem ? dayOrder.indexOf(todayItem.dayOfWeek) : -1;
...
-              const isToday = item.status === 'TODAY_COMPLETED';
+              const isToday = item.isToday;
🤖 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 `@src/components/main/AttendanceSection.tsx` around lines 20 - 66, Update the
AttendanceSection today-detection logic so it does not derive todayIndex solely
from the TODAY_COMPLETED status. Use the attendance model’s explicit today
indicator, such as isToday, to identify today regardless of completion state,
while keeping isAttended based on completion statuses and preserving the
existing missed-day calculation and rendering behavior.
src/components/main/LearningBanner.tsx (1)

51-52: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

level 이분화 로직이 다른 값을 "중급"으로 잘못 표시할 수 있습니다.

level이 문자열 타입이라 ADVANCED가 아닌 값은 전부 "중급"으로 렌더링됩니다. 만약 BEGINNER 등 세 번째 레벨이 존재한다면 잘못된 뱃지가 표시됩니다. 동일한 뱃지 로직이 RecommandLearn.tsx(라인 22, 57-79)에도 중복되어 있어 근본 원인이 같습니다(하단 통합 코멘트 참고).

Also applies to: 77-100

🤖 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 `@src/components/main/LearningBanner.tsx` around lines 51 - 52, Update the
level-to-badge logic in LearningBanner and the duplicated logic in
RecommandLearn so only explicitly supported levels render their corresponding
badges; do not treat every non-ADVANCED string as intermediate. Handle values
such as BEGINNER with the correct badge mapping or an intentional fallback,
preserving existing ADVANCED behavior.
src/components/main/RecommandLearn.tsx (1)

22-22: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

난이도 뱃지 이분화 로직이 두 파일에 중복되고, ADVANCED 외 값은 전부 "중급"으로 표시됩니다. 공유 LevelBadge 컴포넌트로 추출하고, level을 실제 가능한 값들의 union 타입으로 명시하는 것을 권장합니다.

  • src/components/main/LearningBanner.tsx#L51-52,77-100: isAdvanced 판별 및 뱃지 렌더링을 공유 컴포넌트로 이전.
  • src/components/main/RecommandLearn.tsx#L22,57-79: 동일한 뱃지 렌더링을 같은 공유 컴포넌트로 이전.
🤖 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 `@src/components/main/RecommandLearn.tsx` at line 22, LearningBanner과
RecommandLearn에 중복된 난이도 뱃지 로직을 공유 LevelBadge 컴포넌트로 추출하세요. LevelBadge의 level
prop은 실제 가능한 난이도 값들의 union 타입으로 선언하고, 각 값이 올바른 뱃지로 매핑되도록 하여 ADVANCED 외 값을 모두
중급으로 처리하지 않게 하세요. 두 컴포넌트의 isAdvanced 판별과 뱃지 렌더링을 제거하고 동일한 LevelBadge를 사용하도록
변경하세요.
tsconfig.app.json (1)

13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

현재 프로젝트에서는 ignoreDeprecations 추가가 필요하지 않습니다.

프로젝트는 TypeScript ~5.8.3를 사용하고 있어 6.0 deprecation 정책과 호환되지 않으며, tsconfig.app.json에도 해당 플래그를 유발하는 6.0 deprecated 설정이 보이지 않습니다. 이 플래그는 현재 TypeScript 버전과 project 환경에서 삭제하는 편이 낫습니다.

🤖 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 `@tsconfig.app.json` at line 13, Remove the unnecessary ignoreDeprecations
option from tsconfig.app.json, leaving the remaining compiler configuration
unchanged.
🤖 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 `@src/components/history/HistoryRecentPractices.tsx`:
- Around line 26-41: Make the clickable card in the recent practices rendering
keyboard accessible by adding button semantics, a focusable tabIndex, and key
handling that triggers the existing navigate action for Enter and Space.
Preserve the current click navigation to the practice history route.

In `@src/components/history/WeeklyTrendChart.tsx`:
- Around line 17-30: Update the improvement text in WeeklyTrendChart to match
the points data: display +30점 instead of the hardcoded +9점, preserving the
existing styling and surrounding Korean text.
- Around line 122-171: Move the conditional <defs> containing
filter0_d_331_16405 inside the corresponding <svg> in the weekly trend point
rendering. Keep the 이번주 filter reference and glow behavior unchanged, and remove
the <defs> rendered as a direct child of the surrounding div.

In `@src/components/main/DashboardNoti.tsx`:
- Around line 18-28: Update the clickable notification div in DashboardNoti to
support keyboard activation using the same accessibility pattern as
LearningBanner, RecommandLearn, and RecentPractices, while preserving the
existing onReadItem and history navigation behavior.
- Around line 47-52: Update the notification text rendering in DashboardNoti to
use each NotiItem’s type and content instead of always appending the hardcoded
comment message. Preserve the title and timeLabel display, and ensure comment,
analysis, achievement, and complete notifications each render their
corresponding content.

In `@src/components/main/LearningBanner.tsx`:
- Line 1: Update the clickable card/row elements in LearningBanner,
RecommandLearn, RecentPractices, and DashboardNoti to be keyboard accessible by
adding role="button", tabIndex={0}, and onKeyDown handling that triggers the
existing click/navigation behavior for Enter and Space while preserving mouse
behavior.
- Around line 54-71: Update the clickable card in LearningBanner’s returned div
to provide keyboard accessibility: add button semantics, make it focusable, and
handle Enter and Space key presses by triggering the same
navigate(`/learn/curriculum/${learningId}`) action as onClick. Apply the same
interaction pattern to the corresponding clickable elements in RecommandLearn,
RecentPractices, and DashboardNoti.

In `@src/components/main/RecentPractices.tsx`:
- Around line 41-57: Update the clickable row div in RecentPractices to support
keyboard access like the established pattern in LearningBanner, RecommandLearn,
and DashboardNoti: make it focusable and invoke the existing navigate action for
Enter and Space key events while preserving mouse-click behavior.

In `@src/components/main/RecommandLearn.tsx`:
- Around line 25-40: Update the clickable card div in RecommandLearn to provide
keyboard accessibility, following the existing pattern used by LearningBanner.
Add the appropriate focusable and interactive keyboard behavior alongside its
onClick handler, while preserving the current navigation target and styling.

---

Nitpick comments:
In `@src/components/history/GrowthProgressSection.tsx`:
- Around line 11-56: Refactor the repeated blocks in GrowthProgressSection into
an array-driven render, storing each item’s label, value, color, and
direction-specific progress data. Render the shared layout and bar markup from
that array, and replace hardcoded widths with a consistent value-to-progress
mapping formula so every bar reflects its actual value while preserving positive
and negative alignment.

In `@src/components/history/HistorySummaryCards.tsx`:
- Around line 5-169: Refactor the repeated card JSX in HistorySummaryCards into
a shared data structure and map-rendered card component, keeping each card’s
label, icon, value, and comparison text distinct. Remove duplicated layout
declarations by using either the existing Tailwind classes or inline styles, not
both, while preserving the current visual output and making the data easy to
replace with props or API values later.

In `@src/components/history/WeeklyTrendChart.tsx`:
- Around line 4-9: Update the points definition in WeeklyTrendChart so each
point’s y coordinate is calculated from its score using a shared helper that
applies the chart’s Y-axis linear mapping, rather than hardcoded approximations;
preserve the existing x coordinates, labels, and scores.

In `@src/components/main/AttendanceSection.tsx`:
- Around line 20-66: Update the AttendanceSection today-detection logic so it
does not derive todayIndex solely from the TODAY_COMPLETED status. Use the
attendance model’s explicit today indicator, such as isToday, to identify today
regardless of completion state, while keeping isAttended based on completion
statuses and preserving the existing missed-day calculation and rendering
behavior.

In `@src/components/main/LearningBanner.tsx`:
- Around line 51-52: Update the level-to-badge logic in LearningBanner and the
duplicated logic in RecommandLearn so only explicitly supported levels render
their corresponding badges; do not treat every non-ADVANCED string as
intermediate. Handle values such as BEGINNER with the correct badge mapping or
an intentional fallback, preserving existing ADVANCED behavior.

In `@src/components/main/RecommandLearn.tsx`:
- Line 22: LearningBanner과 RecommandLearn에 중복된 난이도 뱃지 로직을 공유 LevelBadge 컴포넌트로
추출하세요. LevelBadge의 level prop은 실제 가능한 난이도 값들의 union 타입으로 선언하고, 각 값이 올바른 뱃지로
매핑되도록 하여 ADVANCED 외 값을 모두 중급으로 처리하지 않게 하세요. 두 컴포넌트의 isAdvanced 판별과 뱃지 렌더링을 제거하고
동일한 LevelBadge를 사용하도록 변경하세요.

In `@tsconfig.app.json`:
- Line 13: Remove the unnecessary ignoreDeprecations option from
tsconfig.app.json, leaving the remaining compiler configuration unchanged.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f80bae3-990e-4f32-9200-afaaf4e3e29a

📥 Commits

Reviewing files that changed from the base of the PR and between f6ab281 and 02b07d7.

📒 Files selected for processing (12)
  • src/components/history/GrowthProgressSection.tsx
  • src/components/history/HistoryRecentPractices.tsx
  • src/components/history/HistorySummaryCards.tsx
  • src/components/history/WeeklyTrendChart.tsx
  • src/components/main/AttendanceSection.tsx
  • src/components/main/DashboardNoti.tsx
  • src/components/main/LearningBanner.tsx
  • src/components/main/RecentPractices.tsx
  • src/components/main/RecommandLearn.tsx
  • src/pages/history/HistoryPage.tsx
  • src/pages/main/MainPage.tsx
  • tsconfig.app.json
🛑 Comments failed to post (9)
src/components/history/HistoryRecentPractices.tsx (1)

26-41: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

클릭 가능한 카드가 키보드로 접근할 수 없습니다.

onClick으로만 라우팅이 연결되어 있고 role, tabIndex, onKeyDown(Enter/Space) 처리가 없어 키보드 사용자는 상세 페이지로 이동할 수 없습니다. 네이티브 <button>으로 감싸거나 role="button" tabIndex={0}과 키보드 핸들러를 추가해주세요.

♿ 제안 수정
 <div
   key={item.practiceId}
   onClick={() => navigate(`/history/${item.practiceId}`)}
+  role="button"
+  tabIndex={0}
+  onKeyDown={(e) => {
+    if (e.key === 'Enter' || e.key === ' ') navigate(`/history/${item.practiceId}`);
+  }}
   className="cursor-pointer text-white transition-colors select-none hover:border-[`#3A3F4A`]"
📝 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.

          <div
            key={item.practiceId}
            onClick={() => navigate(`/history/${item.practiceId}`)}
            role="button"
            tabIndex={0}
            onKeyDown={(e) => {
              if (e.key === 'Enter' || e.key === ' ') navigate(`/history/${item.practiceId}`);
            }}
            className="cursor-pointer text-white transition-colors select-none hover:border-[`#3A3F4A`]"
            style={{
              display: 'flex',
              width: '100%',
              maxWidth: '1196px',
              padding: '24px',
              justifyContent: 'space-between',
              alignItems: 'center',
              borderRadius: '6px',
              background: '`#161B22`',
              border: '1px solid `#2E3142`',
              boxSizing: 'border-box',
            }}>
🤖 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 `@src/components/history/HistoryRecentPractices.tsx` around lines 26 - 41, Make
the clickable card in the recent practices rendering keyboard accessible by
adding button semantics, a focusable tabIndex, and key handling that triggers
the existing navigate action for Enter and Space. Preserve the current click
navigation to the practice history route.
src/components/history/WeeklyTrendChart.tsx (2)

17-30: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

"+9점" 안내 텍스트가 실제 데이터와 맞지 않습니다.

points 배열 기준 지난주 63점 → 이번주 93점으로 차이는 +30점인데, 헤더 텍스트는 "+9점 향상"으로 고정되어 있습니다. 더미 데이터라도 화면에 노출되는 값이므로 일치시켜야 합니다.

🔧 제안 수정
-        전 주 대비 <span style={{ color: '`#69FFC0`' }}>+9점</span> 향상
+        전 주 대비 <span style={{ color: '`#69FFC0`' }}>+30점</span> 향상
📝 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.

      {/* 전 주 대비 +9점 향상 텍스트 */}
      <div
        className="mb-[12px] w-[193px]"
        style={{
          fontFamily: 'Pretendard',
          fontSize: '18px',
          fontStyle: 'normal',
          fontWeight: 500,
          lineHeight: '30px',
          letterSpacing: '-0.36px',
          color: '`#AEB1B6`',
        }}>
        전 주 대비 <span style={{ color: '`#69FFC0`' }}>+30점</span> 향상
      </div>
🤖 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 `@src/components/history/WeeklyTrendChart.tsx` around lines 17 - 30, Update the
improvement text in WeeklyTrendChart to match the points data: display +30점
instead of the hardcoded +9점, preserving the existing styling and surrounding
Korean text.

122-171: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
fd -a 'WeeklyTrendChart\.tsx$' . || true

file="$(fd 'WeeklyTrendChart\.tsx$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
  echo "== outline =="
  ast-grep outline "$file" || true
  echo "== relevant lines =="
  nl -ba "$file" | sed -n '1,220p'
fi

echo "== svg defs usage nearby =="
rg -n "<defs>|<svg|filter=|url\(#" "$file" || true

Repository: Musereview/FE

Length of output: 396


🌐 Web query:

MDN SVG defs element filter references must be defined within SVG document scope inside defs

💡 Result:

An SVG 'filter' element does not strictly need to be defined inside a 'defs' element to function [1]. The 'defs' element is used to store graphical objects for later reuse [2]. While placing a 'filter' inside a 'defs' element is a standard practice that promotes document clarity, organization, and accessibility, it is not a technical requirement for the filter to work [2][1]. A 'filter' element is inherently non-rendering—it is never rendered directly by the browser regardless of where it is placed [3][1]. It only becomes "active" when it is referenced by an SVG 'filter' attribute or a CSS 'filter' property [3][4][1]. If a filter is defined outside of a 'defs' block, it will simply remain hidden and unused until explicitly called upon [1]. In summary, while using 'defs' is recommended for structural and semantic consistency, you can technically place 'filter' definitions anywhere within your SVG document, and they will still be available for reference [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="$1"

printf '%s\n' "== relevant source =="
 awk '{printf "%6d\t%s\n", NR, $0}' "$file" | sed -n '118,175p'

printf '%s\n' "== parse outline =="
python3 - <<'PY' "$file"
import re, sys
p = sys.argv[1]
text = open(p, encoding='utf-8').read()
for name in ['<svg', '</svg>', '<defs>', '</defs>', '<filter', 'filter=', 'url(`#filter0_d_331_16405`)']:
    for m in re.finditer(re.escape(name), text, re.I):
        line = text[:m.start()].count('\n') + 1
        print(f'{name}: line {line}')
PY

printf '%s\n' "== static check: svg defs tree around target =="
python3 - <<'PY' "$file"
# Check whether each filter and defs opening tag appears strictly inside an active <svg> element.
import sys, re
text = open(sys.argv[1], encoding='utf-8').read()
stack=[]
patterns=[(re.compile(r'<svg\b(?![^>]*\bxmlns:svg\b)', re.I), 'svg open'), (re.compile(r'</svg>', re.I), 'svg close'),
          (re.compile(r'<defs\b', re.I), 'defs open'), (re.compile(r'</defs>', re.I), 'defs close'),
          (re.compile(r'<filter\b', re.I), 'filter open'), (re.compile(r'filter=[^>]*url\(`#filter0_d_331_16405`\)', re.I), 'ref')]
for m in re.finditer('|'.join(p[0].pattern for p in patterns), text):
    line=text[:m.start()].count('\n')+1
    hit=next((n for pat,n in patterns if pat.search(m.group())), None)
    svg_stack=[(i,v) for i,v in enumerate(stack) if v=='svg open']
    print(f'line {line}: {hit}, svg_stack_depth={len(svg_stack)}')
    if hit=='svg open' or hit=='defs open' or hit=='filter open':
        stack.append(hit)
    elif hit=='svg close':
        while stack and stack[-1]!='svg open':
            print(f'  unclosed sibling before close: {stack.pop()}')
        if stack: stack.pop()
    elif hit=='defs close' or hit=='filter open':
        if stack: stack.pop()
PY

printf '%s\n' "== React namespace behavior approximation =="
node - <<'JS'
const samples = [
  '<div><svg>{pts.map(p => p.label === "this" && <circle filter="url(`#f`)" />)}</svg>{pts.map(p => p.label === "this" && <defs><filter id="f"/></defs>)}</div>',
  '<div><svg>{pts.map(p => p.label === "this" && <circle filter="url(`#f`)" />)}</svg></div>',
];
for (const [i, src] of samples.entries()) console.log(i, src.includes('<svg'), src.includes('<defs'));
console.log('React uses namespace of closest ancestor SVG element for descendants under SVG namespace');
JS

Repository: Musereview/FE

Length of output: 186


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="/home/jailuser/git/src/components/history/WeeklyTrendChart.tsx"

printf '%s\n' "== relevant source =="
awk '{printf "%6d\t%s\n", NR, $0}' "$file" | sed -n '118,175p'

printf '%s\n' "== target matches =="
python3 - "$file" <<'PY'
import re, sys
text = open(sys.argv[1], encoding='utf-8').read()
targets = ['<svg', '</svg>', '<defs>', '</defs>', '<filter', 'filter=', 'url(`#filter0_d_331_16405`)']
for name in targets:
    matches = [m.start() for m in re.finditer(re.escape(name), text, re.I)]
    for pos in matches:
        print(f'{name}: line {text[:pos].count(chr(10)) + 1}, pos {pos}')
PY

printf '%s\n' "== svg/defs tree analysis =="
python3 - "$file" <<'PY'
import sys, re
text = open(sys.argv[1], encoding='utf-8').read()
stack=[]
patterns = [
    (re.compile(r'<svg\b(?![^>]*\bxmlns:svg\b)', re.I), 'svg_open'),
    (re.compile(r'</svg>', re.I), 'svg_close'),
    (re.compile(r'<defs\b', re.I), 'defs_open'),
    (re.compile(r'</defs>', re.I), 'defs_close'),
    (re.compile(r'<filter\b', re.I), 'filter_open'),
    (re.compile(r'filter=[^>]*url\(`#filter0_d_331_16405`\)', re.I), 'filter_ref'),
]
for m in re.finditer('|'.join(p[0].pattern for p in patterns), text):
    line = text[:m.start()].count('\n') + 1
    hit = next((n for pat, n in patterns if pat.search(m.group())), None)
    print(f'line {line}: {hit}')
    if hit in {'svg_open', 'defs_open', 'filter_open'}:
        stack.append(hit)
    elif hit == 'svg_close':
        while stack and stack[-1] != 'svg_open':
            print(f'  unclosed sibling before svg_close: {stack.pop()}')
        if stack:
            stack.pop()
    elif hit in {'defs_close', 'filter_open'}:
        if stack:
            stack.pop()
PY

Repository: Musereview/FE

Length of output: 3724


<defs>/<filter><svg> 내부로 이동하세요.

<defs><svg> 밖의 일반 <div> 자식으로 렌더링되어 filter="url(#filter0_d_331_16405)" 참조가 SVG 네임스페이스에서 인식되지 않을 수 있어 “이번주” 포인트의 glow 효과가 빠질 수 있습니다. 정적 필터는 <svg> 안에 두거나 map 바깥에서 한 번만 정의하는 편이 안전합니다.

🤖 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 `@src/components/history/WeeklyTrendChart.tsx` around lines 122 - 171, Move the
conditional <defs> containing filter0_d_331_16405 inside the corresponding <svg>
in the weekly trend point rendering. Keep the 이번주 filter reference and glow
behavior unchanged, and remove the <defs> rendered as a direct child of the
surrounding div.
src/components/main/DashboardNoti.tsx (2)

18-28: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

클릭 가능한 알림 div에 키보드 접근성이 없습니다.

onClick만으로 히스토리 이동/읽음 처리를 구현해 키보드 사용자가 접근할 수 없습니다. LearningBanner.tsx, RecommandLearn.tsx, RecentPractices.tsx와 동일한 패턴입니다.

🤖 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 `@src/components/main/DashboardNoti.tsx` around lines 18 - 28, Update the
clickable notification div in DashboardNoti to support keyboard activation using
the same accessibility pattern as LearningBanner, RecommandLearn, and
RecentPractices, while preserving the existing onReadItem and history navigation
behavior.

47-52: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

알림 유형과 무관하게 "코멘트가 작성되었습니다" 문구가 하드코딩되어 있습니다.

NotiItemtype(comment/analysis/achievement/complete)과 content 필드를 정의하지만, 렌더링 로직은 이를 전혀 사용하지 않고 모든 알림에 동일한 코멘트 문구를 표시합니다. analysis/achievement/complete 유형 알림에는 잘못된 내용이 노출됩니다.

🛡️ 제안 수정
-                <p className="w-full truncate text-left font-sans text-[15px] leading-[22px] font-medium tracking-[-0.3px] text-white">
-                  <span className="font-semibold">{item.title}</span>에 코멘트가 작성되었습니다.
-                </p>
+                <p className="w-full truncate text-left font-sans text-[15px] leading-[22px] font-medium tracking-[-0.3px] text-white">
+                  {item.content ?? (
+                    <>
+                      <span className="font-semibold">{item.title}</span>에 코멘트가 작성되었습니다.
+                    </>
+                  )}
+                </p>
📝 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.

              <div className="flex min-w-0 flex-col items-start gap-1">
                <p className="w-full truncate text-left font-sans text-[15px] leading-[22px] font-medium tracking-[-0.3px] text-white">
                  {item.content ?? (
                    <>
                      <span className="font-semibold">{item.title}</span>에 코멘트가 작성되었습니다.
                    </>
                  )}
                </p>
                <span className="font-sans text-xs text-gray-400">{item.timeLabel}</span>
              </div>
🤖 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 `@src/components/main/DashboardNoti.tsx` around lines 47 - 52, Update the
notification text rendering in DashboardNoti to use each NotiItem’s type and
content instead of always appending the hardcoded comment message. Preserve the
title and timeLabel display, and ensure comment, analysis, achievement, and
complete notifications each render their corresponding content.
src/components/main/LearningBanner.tsx (2)

1-1: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

4개 컴포넌트 모두 클릭 가능한 div에 키보드 접근성이 없습니다. 네비게이션/읽음 처리를 onClick만 걸린 비-시맨틱 div로 구현해 키보드 사용자가 접근할 수 없는 동일한 근본 원인이 반복됩니다. 공통 ClickableCard/role="button" 헬퍼로 추출하거나 각 위치에 role="button", tabIndex={0}, onKeyDown(Enter/Space) 처리를 추가하세요.

  • src/components/main/LearningBanner.tsx#L54-71: 학습 배너 카드 divrole="button", tabIndex={0}, onKeyDown 추가.
  • src/components/main/RecommandLearn.tsx#L25-40: 추천 학습 카드 div에 동일한 키보드 핸들러 추가.
  • src/components/main/RecentPractices.tsx#L41-57: 최근 연습 행 div에 동일한 키보드 핸들러 추가.
  • src/components/main/DashboardNoti.tsx#L18-28: 알림 카드 div에 동일한 키보드 핸들러 추가.
🤖 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 `@src/components/main/LearningBanner.tsx` at line 1, Update the clickable
card/row elements in LearningBanner, RecommandLearn, RecentPractices, and
DashboardNoti to be keyboard accessible by adding role="button", tabIndex={0},
and onKeyDown handling that triggers the existing click/navigation behavior for
Enter and Space while preserving mouse behavior.

54-71: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

클릭 가능한 div에 키보드 접근성이 없습니다.

카드 이동을 onClick이 걸린 div로만 구현해 키보드 사용자(Tab/Enter)는 상세 학습 페이지로 이동할 수 없습니다. role="button", tabIndex={0}, onKeyDown 처리(또는 <button>/<Link>로 대체)가 필요합니다. 동일한 패턴이 RecommandLearn.tsx, RecentPractices.tsx, DashboardNoti.tsx에도 있어 근본 원인이 같습니다(하단 통합 코멘트 참고).

♿ 제안 수정
   <div
     onClick={() => navigate(`/learn/curriculum/${learningId}`)}
+    role="button"
+    tabIndex={0}
+    onKeyDown={(e) => {
+      if (e.key === 'Enter' || e.key === ' ') navigate(`/learn/curriculum/${learningId}`);
+    }}
     className="cursor-pointer text-white transition-colors select-none hover:opacity-90"
📝 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.

  return (
    <div
      onClick={() => navigate(`/learn/curriculum/${learningId}`)}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') navigate(`/learn/curriculum/${learningId}`);
      }}
      className="cursor-pointer text-white transition-colors select-none hover:opacity-90"
      style={{
        display: 'flex',
        height: '198px',
        padding: '32px',
        justifyContent: 'space-between',
        alignItems: 'flex-end',
        alignSelf: 'stretch',
        borderRadius: '6px',
        backgroundImage: `linear-gradient(180deg, rgba(11, 15, 25, 0.00) 44.7%, rgba(11, 15, 25, 0.70) 100%), url(${bannerBg})`,
        backgroundRepeat: 'no-repeat',
        backgroundPosition: '0.132px 0px',
        backgroundSize: '100% 100%',
        backgroundColor: 'lightgray',
      }}>
🤖 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 `@src/components/main/LearningBanner.tsx` around lines 54 - 71, Update the
clickable card in LearningBanner’s returned div to provide keyboard
accessibility: add button semantics, make it focusable, and handle Enter and
Space key presses by triggering the same
navigate(`/learn/curriculum/${learningId}`) action as onClick. Apply the same
interaction pattern to the corresponding clickable elements in RecommandLearn,
RecentPractices, and DashboardNoti.
src/components/main/RecentPractices.tsx (1)

41-57: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

클릭 가능한 행 div에 키보드 접근성이 없습니다.

onClick으로 상세 페이지 이동을 처리하지만 키보드 포커스/실행 수단이 없습니다. LearningBanner.tsx, RecommandLearn.tsx, DashboardNoti.tsx와 동일한 패턴입니다.

🤖 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 `@src/components/main/RecentPractices.tsx` around lines 41 - 57, Update the
clickable row div in RecentPractices to support keyboard access like the
established pattern in LearningBanner, RecommandLearn, and DashboardNoti: make
it focusable and invoke the existing navigate action for Enter and Space key
events while preserving mouse-click behavior.
src/components/main/RecommandLearn.tsx (1)

25-40: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

클릭 가능한 카드 div에 키보드 접근성이 없습니다.

LearningBanner.tsx와 동일한 패턴으로, onClick만 걸린 div는 키보드로 접근/실행할 수 없습니다.

🤖 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 `@src/components/main/RecommandLearn.tsx` around lines 25 - 40, Update the
clickable card div in RecommandLearn to provide keyboard accessibility,
following the existing pattern used by LearningBanner. Add the appropriate
focusable and interactive keyboard behavior alongside its onClick handler, while
preserving the current navigation target and styling.

@andada-creator andada-creator changed the title Feat/#45 history dashboard [FEAT] 히스토리 페이지 UI구현 Jul 23, 2026
@andada-creator andada-creator self-assigned this Jul 23, 2026
@andada-creator andada-creator added ✨ Feat New feature or request 🎨 Style UI/UX and visual design tasks labels Jul 23, 2026

@kxxnayun kxxnayun left a comment

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.

다현님 지금 피그마 dev mode 코드 추출해서 사용하시고 계신 것 같은데 이렇게 하면 Tailwind CSS와 inline style의 속성들이 겹치게 돼요! 변환할 수 있는 것들은 다 Tailwind로 변환해주시고 변환할 수 없는 것들만 inline style 써주세요

Comment thread tsconfig.app.json

/* Paths */
"baseUrl": ".",
"ignoreDeprecations": "6.0",

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.

이거 빌드 오류 나서 얘는 빼는 게 좋을 것 같습니다~!

<div
key={item.practiceId}
onClick={() => navigate(`/history/${item.practiceId}`)}
className="cursor-pointer text-white transition-colors select-none hover:border-[#3A3F4A]"

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.

inline style은 CSS 클래스보다 항상 우선순위가 높습니다. 39라인에서 border: '1px solid #2E3142'를 inline으로 박아놨기 때문에 마우스를 올려도 hover:border-[#3A3F4A]가 적용되지 않습니다. transition-colors도 같이 죽은 코드가 됩니다.

지금 피그마 데브 모드에서 스타일 속성 그대로 가져오신 것 같은데 저번 PR에서도 말씀드렸다시피 Tailwind CSS로 변환이 가능한 것들은 변환해주셨으면 좋겠습니다~!

boxSizing: 'border-box',
height: '160px',
justifyContent: 'space-between',
}}>

@kxxnayun kxxnayun Jul 23, 2026

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.

1-3번 카드 모두 classNameinline style에서 중복 선언된 값들이 존재합니다. 위에 달았던 코멘트처럼 Tailwind CSS로 변환 가능한 것들은 변환해주시고 변환할 수 없는 것들만 inline style 사용 부탁드립니다~

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.

1-3번 카드가 같은 구조로 반복되는데 실제로 다른 건 아이콘, 타이틀, 값, 증감 문구 4가지뿐입니다. 지금 구조에선 카드 패딩이나 "전 주보다 … 했어요" 문구 포맷을 바꾸려면 세 군데를 다 고쳐야 해서 컴포넌트로 분리하는게 좋을 것 같습니다~!

const cards = [
    { icon: AccuracyIcon, title: '정확도', value: '91%', delta: '4% 증가' },
    ...
];
{cards.map((c) => <SummaryCard key={c.title} {...c} />)}

이런 식으로 SummaryCard 하나 추출해서 정리하면 좋을 것 같습니다!

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

Labels

✨ Feat New feature or request 🎨 Style UI/UX and visual design tasks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT]대시보드 히스토리 페이지 UI

2 participants