From 309609fccb12bbcc7be85ed13b08fbd5f45849c8 Mon Sep 17 00:00:00 2001 From: renolation Date: Sat, 18 Apr 2026 23:16:52 +0700 Subject: [PATCH] update --- index.html | 1 + src/components/layout/AppHeader.tsx | 77 +- src/components/layout/MobileNav.tsx | 2 +- src/components/layout/Sidebar.tsx | 123 +++- .../dashboard/components/Dashboard.tsx | 596 ++++++++++++++-- .../dashboard/components/LeaderboardCard.tsx | 102 --- .../dashboard/components/StatsRow.tsx | 71 -- .../dashboard/components/WeeklySection.tsx | 90 --- .../dashboard/components/XpProgressCard.tsx | 56 -- .../dashboard/components/XuEconomyCard.tsx | 46 -- src/features/flash-card/api/flashcard-api.ts | 72 +- .../components/FlashCardLearnPage.tsx | 667 +++++++++++++----- .../components/FlashCardListPage.tsx | 156 ++-- .../components/FlashCardTermsPage.tsx | 206 ++++-- src/features/flash-card/lib/media-url.ts | 7 + src/features/flash-card/lib/srs-intervals.ts | 30 + src/features/home/components/Home.tsx | 404 +++++++---- .../toeic/components/ToeicPractice.tsx | 7 +- .../toeic/components/ToeicTestList.tsx | 74 +- .../writing/components/WritingChecker.tsx | 149 ++-- src/index.css | 332 ++++++++- src/routes/{dashboard.tsx => archivement.tsx} | 2 +- src/routes/flash-card.$listId.index.tsx | 11 + src/routes/flash-card.$listId.tsx | 10 +- .../dashboard/{design.png => dashboard.png} | Bin .../{design.png => preferences.png} | Bin .../{design.png => screen-01-home.png} | Bin .../{design.png => screen-02-part-select.png} | Bin .../{design.png => screen-03-exam.png} | Bin .../{design.png => screen-04-results.png} | Bin .../{design.png => screen-05-writing.png} | Bin .../{design.png => screen-06-flashcard.png} | Bin 32 files changed, 2261 insertions(+), 1030 deletions(-) delete mode 100644 src/features/dashboard/components/LeaderboardCard.tsx delete mode 100644 src/features/dashboard/components/StatsRow.tsx delete mode 100644 src/features/dashboard/components/WeeklySection.tsx delete mode 100644 src/features/dashboard/components/XpProgressCard.tsx delete mode 100644 src/features/dashboard/components/XuEconomyCard.tsx create mode 100644 src/features/flash-card/lib/media-url.ts create mode 100644 src/features/flash-card/lib/srs-intervals.ts rename src/routes/{dashboard.tsx => archivement.tsx} (73%) create mode 100644 src/routes/flash-card.$listId.index.tsx rename stitch-exports/dashboard/{design.png => dashboard.png} (100%) rename stitch-exports/preferences/{design.png => preferences.png} (100%) rename stitch-exports/screen-01-home/{design.png => screen-01-home.png} (100%) rename stitch-exports/screen-02-part-select/{design.png => screen-02-part-select.png} (100%) rename stitch-exports/screen-03-exam/{design.png => screen-03-exam.png} (100%) rename stitch-exports/screen-04-results/{design.png => screen-04-results.png} (100%) rename stitch-exports/screen-05-writing/{design.png => screen-05-writing.png} (100%) rename stitch-exports/screen-06-flashcard/{design.png => screen-06-flashcard.png} (100%) diff --git a/index.html b/index.html index a2aa25c..361a260 100644 --- a/index.html +++ b/index.html @@ -10,6 +10,7 @@ +
diff --git a/src/components/layout/AppHeader.tsx b/src/components/layout/AppHeader.tsx index 3833725..a45455a 100644 --- a/src/components/layout/AppHeader.tsx +++ b/src/components/layout/AppHeader.tsx @@ -2,30 +2,79 @@ import { useRouterState } from '@tanstack/react-router' import { useTestStore } from '@/store/test-store' import { UserMenu } from '@/components/UserMenu' -const ROUTE_TITLES: Record = { - '/': 'Trang chủ', - '/writing': 'AI Chấm Writing', - '/flash-card': 'Flash Card', - '/toeic': 'Luyện đề TOEIC', - '/toeic/session': '', // dynamic — filled below - '/toeic/result': 'Kết quả bài thi', +const ROUTE_TITLES: Record = { + '/': { eyebrow: 'Học TOEIC cùng AI', title: 'Trang chủ' }, + '/archivement': { eyebrow: 'Thành tích của bạn', title: 'Tôi học', accent: 'học' }, + '/toeic': { eyebrow: 'Luyện đề', title: 'TOEIC Mock Tests', accent: 'Mock' }, + '/writing': { eyebrow: 'AI Coach', title: 'Chấm Writing', accent: 'Writing' }, + '/flash-card': { eyebrow: 'Từ vựng TOEIC', title: 'Flash Card', accent: 'Card' }, + '/settings': { eyebrow: 'Tuỳ chỉnh', title: 'Cài đặt' }, +} + +function matchRouteLabel(pathname: string) { + if (ROUTE_TITLES[pathname]) return ROUTE_TITLES[pathname] + const keys = Object.keys(ROUTE_TITLES).sort((a, b) => b.length - a.length) + for (const k of keys) { + if (k !== '/' && pathname.startsWith(k)) return ROUTE_TITLES[k] + } + return { eyebrow: 'EnglishAI', title: 'EnglishAI' } } export function AppHeader() { const { location } = useRouterState() - const { partId, partName, answers, questions } = useTestStore() + const { testName, parts, answers } = useTestStore() const pathname = location.pathname - let title = ROUTE_TITLES[pathname] ?? 'EnglishAI' - + // In-session mode: show test progress instead of route title if (pathname === '/toeic/session') { - const answered = answers.filter((a) => a !== null).length - title = `Part ${partId} — ${partName} · ${answered}/${questions.length} câu` + const totalQuestions = parts.reduce((sum, p) => sum + p.questions.length, 0) + const answered = Object.values(answers).filter((a) => a !== null).length + return ( +
+
+
Phiên thi
+
+ {testName} · {answered}/{totalQuestions} câu +
+
+ +
+ ) + } + + const { eyebrow, title, accent } = matchRouteLabel(pathname) + const renderTitle = () => { + if (!accent || !title.includes(accent)) return title + const [before, after] = title.split(accent) + return ( + <> + {before} + {accent} + {after} + + ) } return ( -
- {title} +
+
+
{eyebrow}
+
+ {renderTitle()} +
+
) diff --git a/src/components/layout/MobileNav.tsx b/src/components/layout/MobileNav.tsx index 0896c05..30cf5a5 100644 --- a/src/components/layout/MobileNav.tsx +++ b/src/components/layout/MobileNav.tsx @@ -3,7 +3,7 @@ import { cn } from '@/lib/utils' const NAV_ITEMS = [ { to: '/', label: 'Home', icon: 'home', matchPrefix: '/', exact: true }, - { to: '/dashboard', label: 'Thành tích', icon: 'emoji_events', matchPrefix: '/dashboard', exact: false }, + { to: '/archivement', label: 'Thành tích', icon: 'emoji_events', matchPrefix: '/archivement', exact: false }, { to: '/toeic', label: 'Luyện đề', icon: 'assignment', matchPrefix: '/toeic', exact: false }, { to: '/writing', label: 'Writing', icon: 'edit_note', matchPrefix: '/writing', exact: false }, { to: '/settings', label: 'Cài đặt', icon: 'settings', matchPrefix: '/settings', exact: false }, diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index bb59b86..e813442 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -5,7 +5,7 @@ import { useAuthModalStore } from '@/store/auth-modal-store' const NAV_ITEMS = [ { to: '/', label: 'Trang chủ', icon: 'home', matchPrefix: '/', exact: true }, - { to: '/dashboard', label: 'Thành tích', icon: 'emoji_events', matchPrefix: '/dashboard', exact: false }, + { to: '/archivement', label: 'Thành tích', icon: 'emoji_events', matchPrefix: '/archivement', exact: false }, { to: '/toeic', label: 'Luyện đề TOEIC', icon: 'assignment', matchPrefix: '/toeic', exact: false }, { to: '/writing', label: 'AI Writing', icon: 'edit_note', matchPrefix: '/writing', exact: false }, { to: '/flash-card', label: 'Flash Card', icon: 'menu_book', matchPrefix: '/flash-card', exact: false }, @@ -23,60 +23,111 @@ export function Sidebar() { const openModal = useAuthModalStore((s) => s.open) return ( -