diff --git a/src/components/history/GrowthProgressSection.tsx b/src/components/history/GrowthProgressSection.tsx new file mode 100644 index 0000000..f7a4186 --- /dev/null +++ b/src/components/history/GrowthProgressSection.tsx @@ -0,0 +1,58 @@ +// src/components/history/GrowthProgressSection.tsx + +export default function GrowthProgressSection() { + return ( +
+ {/* 그래프 및 라벨 리스트 영역 (행마다 32px 간격) */} +
+ {/* 스케일 (+8) */} +
+
+ 스케일 + +8 +
+
+
+
+
+
+ + {/* 텐션 (+6) */} +
+
+ 텐션 + +6 +
+
+
+
+
+
+ + {/* 진행 (+4) */} +
+
+ 진행 + +4 +
+
+
+
+
+
+ + {/* 코드 연결 (-10) */} +
+
+ 코드 연결 + -10 +
+
+
+
+
+
+
+
+ ); +} diff --git a/src/components/history/HistoryRecentPractices.tsx b/src/components/history/HistoryRecentPractices.tsx new file mode 100644 index 0000000..cf56ba1 --- /dev/null +++ b/src/components/history/HistoryRecentPractices.tsx @@ -0,0 +1,190 @@ +// src/components/history/HistoryRecentPractices.tsx + +import { useNavigate } from 'react-router-dom'; + +interface HistoryPracticeItem { + practiceId: number; + title: string; + scoreChange: string; + scoreType: 'up' | 'down' | 'neutral'; + description: string; + timeLabel: string; + date: string; +} + +interface HistoryRecentPracticesProps { + data: HistoryPracticeItem[]; +} + +export default function HistoryRecentPractices({ data }: HistoryRecentPracticesProps) { + const navigate = useNavigate(); + + return ( +
+ {data?.map((item) => { + return ( +
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', + }}> + {/* 좌측 구역 */} +
+
+ + {item.title} + + +
+ {item.scoreType === 'down' ? ( + + + + ) : item.scoreType === 'up' ? ( + + + + ) : ( + + + + )} + + {item.scoreType !== 'neutral' && ( + + {item.scoreChange} + + )} +
+
+ +

+ {item.description} +

+
+ + {/* 우측 구역 */} +
+ {/* 소요 시간 */} +
+ + + + + {item.timeLabel} +
+ + {/* 날짜 */} +
+ {item.date} +
+ + {/* > 아이콘 */} + + + +
+
+ ); + })} +
+ ); +} diff --git a/src/components/history/HistorySummaryCards.tsx b/src/components/history/HistorySummaryCards.tsx new file mode 100644 index 0000000..81b66fc --- /dev/null +++ b/src/components/history/HistorySummaryCards.tsx @@ -0,0 +1,171 @@ +// src/components/history/HistorySummaryCards.tsx + +export default function HistorySummaryCards() { + return ( +
+ {/* 1번 카드: 정확도 */} +
+
+
+ {/* 정확도 아이콘 */} + + + + + 정확도 + +
+ + 91% + +
+
+ 전 주보다 4% 증가했어요 +
+
+ + {/* 2번 카드: 연습 시간 */} +
+
+
+ {/* 연습 시간 아이콘 */} + + + + + 연습 시간 + +
+ + 65분 + +
+
+ 전 주보다 18분 증가했어요 +
+
+ + {/* 3번 카드: 완료 세션 */} +
+
+
+ {/* 완료 세션 아이콘 */} + + + + + 완료 세션 + +
+ + 6개 + +
+
+ 전 주보다 2개 증가했어요 +
+
+
+ ); +} diff --git a/src/components/history/WeeklyTrendChart.tsx b/src/components/history/WeeklyTrendChart.tsx new file mode 100644 index 0000000..b37c16a --- /dev/null +++ b/src/components/history/WeeklyTrendChart.tsx @@ -0,0 +1,205 @@ +// src/components/history/WeeklyTrendChart.tsx + +export default function WeeklyTrendChart() { + const points = [ + { label: '3주 전', score: 60, x: 40, y: 110 }, + { label: '2주 전', score: 78, x: 330, y: 65 }, + { label: '지난주', score: 63, x: 620, y: 100 }, + { label: '이번주', score: 93, x: 910, y: 24 }, + ]; + + const pathString = points.reduce((acc, cur, idx) => { + return idx === 0 ? `M ${cur.x} ${cur.y}` : `${acc} L ${cur.x} ${cur.y}`; + }, ''); + + return ( +
+ {/* 전 주 대비 +9점 향상 텍스트 */} +
+ 전 주 대비 +9점 향상 +
+ + {/* 차트 메인 박스 */} +
+ {/* 전체 차트 영역 */} +
+ {/* 1. Y축 수치 레이블 */} +
+ 100 + 80 + 60 + 40 +
+ + {/* 2. 그래프 및 그리드 영역 */} +
+ {/* 점선 4개 */} +
+
+
+
+
+
+ + {/* X축 기준 실선 */} +
+ + {/* SVG 연결선 및 점 */} +
+ + + + + {points.map((pt, idx) => { + const leftPercent = (pt.x / 950) * 100; + return ( +
+ {/* 수치 텍스트 */} +
+ + {pt.score} + +
+ + {/* 민트색 점 */} +
+ + + {pt.label === '이번주' && ( + + )} + + {pt.label === '이번주' && ( + + + + + + + + + + + + + )} +
+
+ ); + })} +
+ + {/* 3. 하단 X축 레이블*/} +
+ {points.map((pt, idx) => { + const leftPercent = (pt.x / 950) * 100; + return ( + + {pt.label} + + ); + })} +
+
+
+
+
+ ); +} diff --git a/src/pages/main/components/AttendanceSection.tsx b/src/components/main/AttendanceSection.tsx similarity index 100% rename from src/pages/main/components/AttendanceSection.tsx rename to src/components/main/AttendanceSection.tsx diff --git a/src/pages/main/components/DashboardNoti.tsx b/src/components/main/DashboardNoti.tsx similarity index 100% rename from src/pages/main/components/DashboardNoti.tsx rename to src/components/main/DashboardNoti.tsx diff --git a/src/pages/main/components/LearningBanner.tsx b/src/components/main/LearningBanner.tsx similarity index 100% rename from src/pages/main/components/LearningBanner.tsx rename to src/components/main/LearningBanner.tsx diff --git a/src/pages/main/components/RecentPractices.tsx b/src/components/main/RecentPractices.tsx similarity index 100% rename from src/pages/main/components/RecentPractices.tsx rename to src/components/main/RecentPractices.tsx diff --git a/src/pages/main/components/RecommandLearn.tsx b/src/components/main/RecommandLearn.tsx similarity index 100% rename from src/pages/main/components/RecommandLearn.tsx rename to src/components/main/RecommandLearn.tsx diff --git a/src/pages/history/HistoryPage.tsx b/src/pages/history/HistoryPage.tsx index 0c1ace6..d9e259a 100644 --- a/src/pages/history/HistoryPage.tsx +++ b/src/pages/history/HistoryPage.tsx @@ -1,6 +1,173 @@ -// 히스토리 목록 페이지 -function HistoryPage() { - return

히스토리

; -} +// src/pages/history/HistoryPage.tsx + +import HistorySummaryCards from '@/components/history/HistorySummaryCards'; +import GrowthProgressSection from '@/components/history/GrowthProgressSection'; +import WeeklyTrendChart from '@/components/history/WeeklyTrendChart'; +import HistoryRecentPractices from '@/components/history/HistoryRecentPractices'; + +export default function HistoryPage() { + const historyPracticeData = [ + { + practiceId: 1, + title: 'Jazz Standard Practice', + scoreChange: '+8', + scoreType: 'up' as const, + description: + '리디안 스케일 활용이 우수하며, 텐션음 해결이 자연스러웠습니다.\n박자 안정성을 더 개선하면 좋겠습니다.', + timeLabel: '소요시간 10분', + date: '오늘', + }, + { + practiceId: 2, + title: 'Modal Interchange Practice', + scoreChange: '+6', + scoreType: 'up' as const, + description: '모달 인터체인지 개념을 잘 적용했으나, 전조 구간에서 약간의 불안정함이 있었습니다.', + timeLabel: '소요시간 10분', + date: '어제', + }, + { + practiceId: 3, + title: 'Voice Leading Exercise', + scoreChange: '-5', + scoreType: 'down' as const, + description: '보이스 리딩이 매끄럽지 못했고, 코드 톤 간 연결이 부자연스러웠습니다.\n더 많은 연습이 필요합니다.', + timeLabel: '소요시간 10분', + date: '4월 30일', + }, + { + practiceId: 4, + title: 'Blues Scale Improvisation', + scoreChange: '—', + scoreType: 'neutral' as const, + description: '블루스 스케일을 효과적으로 사용했고, 리듬감이 뛰어났습니다.', + timeLabel: '소요시간 10분', + date: '4월 29일', + }, + ]; + + return ( +
+ {/* 컨텐츠 묶음 */} +
+ {/* 1. 상단 타이틀 및 설명 섹션 */} +
+

+ 연주 히스토리 +

+

+ 최근 연주 기록과 흐름을 한눈에 확인해보세요. +

+
-export default HistoryPage; + {/* 2. 이번주 연주 요약 섹션 */} +
+

+ 이번주 연주 요약 +

+ +
+ + {/* 3. 영역별 성장 변화 섹션 */} +
+
+

+ 영역별 성장 변화 +

+

+ 지난주 대비 영역별 평균 점수 변화를 보여드려요. +

+
+ +
+ + {/* 4. 최근 4주 학습 추이 섹션 */} +
+
+

+ 최근 4주 학습 추이 +

+

+ 최근 4주간 평균 점수 변화를 보여드려요. +

+
+ +
+ + {/* 5. 최근 연주 리스트 */} +
+

+ 최근 연주 +

+ +
+
+
+ ); +} diff --git a/src/pages/main/MainPage.tsx b/src/pages/main/MainPage.tsx index 75de7f6..7abcf34 100644 --- a/src/pages/main/MainPage.tsx +++ b/src/pages/main/MainPage.tsx @@ -1,10 +1,10 @@ // src/pages/main/MainPage.tsx import { useOutletContext } from 'react-router-dom'; -import AttendanceSection from './components/AttendanceSection'; -import LearningBanner from './components/LearningBanner'; -import RecommendedLearnings from './components/RecommandLearn'; -import RecentPractices from './components/RecentPractices'; -import DashboardNoti from './components/DashboardNoti'; +import AttendanceSection from '@/components/main/AttendanceSection'; +import LearningBanner from '@/components/main/LearningBanner'; +import RecommendedLearnings from '@/components/main/RecommandLearn'; +import RecentPractices from '@/components/main/RecentPractices'; +import DashboardNoti from '@/components/main/DashboardNoti'; import type { NotiItem } from '@/types/notification'; interface LayoutContextType { diff --git a/tsconfig.app.json b/tsconfig.app.json index 41750a6..5cbd9a4 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -10,6 +10,7 @@ /* Paths */ "baseUrl": ".", + "ignoreDeprecations": "6.0", "paths": { "@/*": ["src/*"] },