# Claude Project Context — English Learning App (TOEIC Focus) > File này cung cấp context đầy đủ cho Claude khi làm việc với dự án. > Cập nhật file này mỗi khi có quyết định kiến trúc mới. > **Last updated**: Phase 2 done — thêm Phase 3 Retention & Monetization, đẩy Speaking AI và Full TOEIC sang Phase 4 & 5 --- ## Tổng quan dự án **Loại sản phẩm**: Web app học tiếng Anh / luyện thi TOEIC & IELTS cho người dùng Việt Nam **Target users**: Sinh viên, người đi làm tại Việt Nam cần TOEIC, IELTS, hoặc học tiếng Anh tổng quát **Focus chính**: TOEIC (mở rộng market), sau đó IELTS **Giai đoạn hiện tại**: Phase 1 — MVP, validate market **Roadmap**: 5 phases — MVP → Auth & Progress → Retention & Monetization → Speaking AI → Full TOEIC --- ## Tech Stack ### Frontend | Layer | Tech | Ghi chú | |---|---|---| | Framework | **React** + **Vite** + **TypeScript** | | | Routing | **TanStack Router** | File-based, type-safe | | Server state | **TanStack Query** | Fetch, cache, sync API data | | Client state | **Zustand** | UI state + localStorage persist | | Styling | **Tailwind CSS** | Desktop-first | | UI Components | **shadcn/ui** | Dùng khi cần, không bắt buộc | ### Design System (từ Stitch export) | Token | Value | |---|---| | Font | Plus Jakarta Sans + Material Symbols Outlined | | Primary | #2563EB | | Success | #16A34A | | Danger | #DC2626 | | Background | #F8FAFC | | Card | #FFFFFF | | Border radius | 12–16px | | Shadow | soft, subtle | ### Responsive Layout | Breakpoint | Layout | |---|---| | Desktop (1280px) | Sidebar trái cố định (240px) + main content — **LAYOUT CHÍNH** | | Tablet (768px) | Sidebar thu gọn icon-only | | Mobile (375px) | Ẩn sidebar, hiện bottom navigation bar | ### Backend | Phase | Tech | Ghi chú | |---|---|---| | Phase 1 | **Supabase** (PostgreSQL + Edge Functions + JS SDK) | Tạm thời, migrate sau | | Phase 2+ | **NestJS** + **PostgreSQL** native | Khi có traction | > ⚠️ Supabase chỉ dùng Phase 1. Schema PostgreSQL thiết kế chuẩn ngay từ đầu để migrate không đau. ### AI | Layer | Tech | Ghi chú | |---|---|---| | Provider | **GLM (Z.ai API)** | Rẻ, OpenAI-compatible format | | Endpoint | `open.bigmodel.cn/api/paas/v4` | | | Model | GLM-4 / GLM-4.7 | | | Fallback | OpenAI / Claude API | Swap dễ vì API compatible | | Gọi từ | Supabase Edge Function (Phase 1) → NestJS service (Phase 2+) | Giấu API key | ### Deploy | Layer | Tech | |---|---| | Frontend | Self-hosted server (có sẵn) | | Backend | Self-hosted server (có sẵn) | | Database | Supabase Cloud (Phase 1) → self-hosted PostgreSQL (Phase 2+) | --- ## Database Schema (PostgreSQL) ```sql -- Câu hỏi TOEIC CREATE TABLE questions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), part INT NOT NULL, -- 1 đến 7 type TEXT, -- photo | q&a | incomplete_sentence | etc. content TEXT NOT NULL, -- nội dung câu hỏi / đoạn văn options JSONB, -- ["A. ...", "B. ...", "C. ...", "D. ..."] answer TEXT NOT NULL, -- "A" explanation TEXT, -- giải thích đáp án bằng tiếng Việt audio_url TEXT, -- Part 1–4 (listening) image_url TEXT, -- Part 1 (photos) created_at TIMESTAMPTZ DEFAULT now() ); -- Từ vựng TOEIC CREATE TABLE vocab ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), word TEXT NOT NULL, phonetic TEXT, -- /wɜːrd/ meaning_vi TEXT NOT NULL, -- nghĩa tiếng Việt topic TEXT NOT NULL, -- business | office | travel | finance | hr | marketing example TEXT, -- câu ví dụ tiếng Anh created_at TIMESTAMPTZ DEFAULT now() ); -- Phase 2+ CREATE TABLE users ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), email TEXT UNIQUE NOT NULL, name TEXT NOT NULL, password TEXT NOT NULL, -- hashed created_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE user_progress ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id), type TEXT, -- test | vocab | writing reference_id UUID, data JSONB, created_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE writing_submissions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id), content TEXT NOT NULL, feedback JSONB, created_at TIMESTAMPTZ DEFAULT now() ); ``` --- ## TypeScript Interfaces ```typescript interface Question { id: string part: number type: string content: string options: string[] answer: string explanation: string audioUrl?: string imageUrl?: string } interface VocabWord { id: string word: string phonetic: string meaningVi: string topic: 'business' | 'office' | 'travel' | 'finance' | 'hr' | 'marketing' example: string } interface TestResult { testId: string part: number score: number total: number duration: number answers: { questionId: string; selected: string; correct: boolean }[] completedAt: Date } interface WritingFeedback { score: string grammar: string[] vocabulary: string[] structure: string improvedVersion: string summary: string } // Phase 2+ interface User { id: string email: string name: string createdAt: Date } ``` --- ## Cấu trúc thư mục ``` src/ ├── routes/ │ ├── index.tsx ← Trang chủ (/) │ ├── toeic/ │ │ ├── index.tsx ← Chọn Part (/toeic) │ │ ├── part.$partId.tsx ← Config số câu (/toeic/part/$partId) │ │ ├── session.tsx ← Làm bài (/toeic/session) │ │ └── result.tsx ← Kết quả + đáp án (/toeic/result) │ ├── writing.tsx ← AI Writing Checker (/writing) │ ├── vocab.tsx ← Flashcard (/vocab) │ └── auth/ ← Phase 2 │ ├── login.tsx ← Đăng nhập (/auth/login) │ └── register.tsx ← Đăng ký (/auth/register) ├── components/ │ ├── layout/ │ │ ├── Sidebar.tsx ← Desktop sidebar │ │ └── BottomNav.tsx ← Mobile bottom nav │ ├── QuestionCard.tsx │ ├── FlashCard.tsx │ ├── WritingFeedback.tsx │ ├── ProgressRing.tsx │ └── Timer.tsx ├── store/ │ ├── testStore.ts ← Zustand: trạng thái bài thi │ ├── vocabStore.ts ← Zustand: flashcard progress (persist localStorage) │ └── authStore.ts ← Zustand: user session (Phase 2) ├── hooks/ │ ├── useQuestions.ts ← TanStack Query │ ├── useVocab.ts ← TanStack Query │ └── useWritingCheck.ts ← TanStack Mutation → Edge Function ├── lib/ │ └── supabase.ts ← Supabase client init └── utils/ └── rateLimiter.ts ← Rate limit 3 lần/ngày/IP (localStorage) ``` --- ## Supabase Edge Function — AI Writing Checker ```typescript // supabase/functions/writing-check/index.ts import { serve } from "https://deno.land/std/http/server.ts" serve(async (req) => { const { content } = await req.json() const response = await fetch("https://open.bigmodel.cn/api/paas/v4/chat/completions", { method: "POST", headers: { "Authorization": `Bearer ${Deno.env.get("GLM_API_KEY")}`, "Content-Type": "application/json" }, body: JSON.stringify({ model: "glm-4", messages: [ { role: "system", content: `You are an expert English writing evaluator for TOEIC/IELTS. Evaluate the writing and return ONLY valid JSON: { "score": "estimated band score", "grammar": ["error + fix"], "vocabulary": ["suggestion"], "structure": "feedback in Vietnamese", "improved_version": "rewritten version", "summary": "overall feedback in Vietnamese" }` }, { role: "user", content } ] }) }) const data = await response.json() const result = JSON.parse(data.choices[0].message.content) return new Response(JSON.stringify(result), { headers: { "Content-Type": "application/json" } }) }) ``` --- ## Roadmap — 4 Phases --- ### PHASE 1 — MVP (Hiện tại) 🚧 **Mục tiêu**: Ra sản phẩm web dùng thử không cần login, validate market **Stack**: React + Vite + TypeScript + TanStack + Zustand + Tailwind + Supabase + GLM **Tính năng**: - ✅ Luyện đề TOEIC mini test theo từng Part (Part 1–7) - ✅ Chọn số câu: 10 / 20 / full part, có đếm giờ - ✅ Submit → xem điểm + đáp án + giải thích tiếng Việt - ✅ Lịch sử kết quả + thống kê điểm yếu theo Part (localStorage) - ✅ AI Writing Checker (GLM, 3 lần/ngày/IP, không cần login) - ✅ Flashcard từ vựng TOEIC (6 chủ đề, localStorage progress) **Không có**: - ❌ Auth / login - ❌ Progress sync server - ❌ Thanh toán - ❌ Flutter / mobile app - ❌ Full mock test **Timeline**: 5 tuần **Done khi**: - ≥ 50 câu hỏi mỗi Part (Part 1–7) - AI Writing Checker phản hồi < 5 giây - Không lỗi hiển thị trên Chrome mobile - 20+ người dùng thật đã dùng thử - Không critical bug sau 1 tuần beta --- ### PHASE 2 — Auth & Progress **Mục tiêu**: Giữ chân user, sync progress server-side, hiểu behavior trước khi monetize **Trigger**: Phase 1 có traction — 200+ MAU hoặc feedback tích cực **Stack thay đổi**: - Migrate Supabase → **NestJS + PostgreSQL native** - Thêm **Redis** cho cache + session **Guest Access (chưa đăng ký)**: - Xem preview 1 bài test dạng read-only (thấy câu hỏi, không làm được) - Không cho submit đáp án, không xem kết quả - Hiện modal "Đăng ký để luyện thi" khi cố tương tác - Flashcard: xem vài card đầu, bị chặn sau đó - AI Writing: không dùng được, hiện CTA đăng ký **Auth — Đăng ký**: - Form: **Tên + Email + Password** (chỉ 3 field) - Không xác thực email, không OTP, không confirm - Đăng ký xong → **redirect home luôn** **Auth — Đăng nhập**: - Email + Password - Redirect về trang trước hoặc home **Sau khi đăng nhập**: - Làm bài không giới hạn - Progress sync server-side: lịch sử thi, từ vựng, writing submissions - Dashboard cá nhân: streak, biểu đồ điểm, điểm yếu theo Part - AI Writing: 10 lần/ngày **Không có ở Phase 2**: - ❌ Xác thực email - ❌ Quên mật khẩu - ❌ Google / Zalo OAuth - ❌ Flutter / mobile app - ❌ Thanh toán / freemium - ❌ Notification --- ### PHASE 3 — Xu System & Gamification ← Tiếp theo **Mục tiêu**: Tạo thói quen học hàng ngày, giữ chân user bằng Xu economy + gamification **Platform**: Web only (không Flutter ở phase này) **Trigger**: Phase 2 có user đăng ký, cần convert sang returning users --- #### Xu Economy — Trung tâm của Phase 3 ``` Học hàng ngày / xem ads → kiếm Xu ↓ Xu → dùng tính năng premium ↓ Hết Xu → xem ads thêm (nạp tiền thật → Phase 4) ``` **Kiếm Xu (miễn phí)**: | Hành động | Xu nhận | |---|---| | Đăng ký lần đầu (welcome bonus) | 50 Xu | | Hoàn thành daily goal | 10 Xu | | Streak milestone (7 / 30 / 100 ngày) | 20 / 50 / 100 Xu | | Xem rewarded ads trên web | 5 Xu / video (tối đa 5 video/ngày) | **Dùng Xu**: | Tính năng | Chi phí | |---|---| | Streak freeze (bảo vệ 1 ngày) | 20 Xu | | Thêm 5 lượt AI Writing (GLM-4.7) | 30 Xu | | 1 lượt AI Writing cao cấp (GPT-4o) | 15 Xu | | Mở thêm bài thi khi hết giới hạn free | 10 Xu | > ⚠️ Chưa có nạp Xu bằng tiền thật ở Phase 3 — chỉ kiếm qua học + ads. > Nạp tiền thật (VNPay/MoMo) → Phase 4. --- #### AI Model Tier | Tier | Model | Free limit | Dùng Xu | |---|---|---|---| | Free | GLM-4 base | 3 lần/ngày | — | | Standard | GLM-4.7 | — | 30 Xu / 5 lượt | | Premium | GPT-4o / Claude | — | 15 Xu / lượt | --- #### Gamification - **Streak hàng ngày**: học ít nhất 1 bài/ngày giữ chuỗi - **XP points**: mỗi bài thi / flashcard / writing check → XP - **Cấp độ**: Beginner → Bronze → Silver → Gold → Master (theo XP tích luỹ) - **Streak freeze**: dùng Xu bảo vệ streak khi bận — hook mạnh nhất - **Weekly goal**: đặt mục tiêu tuần, hoàn thành → badge + thưởng Xu #### Leaderboard - Bảng xếp hạng tuần theo XP (reset mỗi tuần, không tích luỹ) - Hiện top 10 + vị trí của bản thân - Chia sẻ kết quả lên Facebook/Zalo — viral loop tự nhiên #### Nhắc nhở - Browser push notification (web, không cần app) - Giờ nhắc do user tự chọn - Message cá nhân hoá: *"Streak 7 ngày của bạn sắp mất!"* #### Lộ trình AI cá nhân hoá - Phân tích kết quả thi → suggest *"Tuần này tập trung Part 5 và 6"* - Dashboard: *"Bạn yếu nhất ở Part 5 — luyện ngay"* - Đặt ngày thi TOEIC → đếm ngược + lịch ôn gợi ý #### Web Ads - Google AdSense: banner dưới trang + interstitial sau kết quả bài thi - Rewarded video ads: xem để nhận Xu - Không ads trong lúc đang làm bài - User có đủ Xu / premium → không hiện ads (Phase 4) --- #### DB Schema bổ sung ```sql CREATE TABLE user_gamification ( user_id UUID REFERENCES users(id) PRIMARY KEY, xp INT DEFAULT 0, level TEXT DEFAULT 'beginner', -- beginner | bronze | silver | gold | master streak INT DEFAULT 0, longest_streak INT DEFAULT 0, last_active DATE, xu INT DEFAULT 50, -- welcome bonus freeze_count INT DEFAULT 0 ); CREATE TABLE xu_transactions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id), type TEXT, -- earn_welcome | earn_daily | earn_streak | earn_ads | spend_freeze | spend_writing | spend_test amount INT, -- dương = nhận, âm = tiêu balance INT, -- số Xu sau giao dịch (để audit) description TEXT, created_at TIMESTAMPTZ DEFAULT now() ); CREATE TABLE weekly_leaderboard ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID REFERENCES users(id), week_start DATE, xp_earned INT DEFAULT 0, rank INT ); ``` **Tech mới**: - Google AdSense (banner + rewarded video) - Browser Push Notification API - Cron job: reset leaderboard mỗi tuần, check streak hàng ngày **Không có ở Phase 3**: - ❌ Nạp tiền thật (VNPay / MoMo) → Phase 4 - ❌ Flutter / mobile app → Phase 4 - ❌ Subscription / Premium plan → Phase 4 **Timeline**: 5–6 tuần --- ### PHASE 4 — Speaking AI **Mục tiêu**: Tăng differentiation, cover kỹ năng Speaking cho IELTS/TOEIC **Trigger**: Phase 3 ổn định, có doanh thu đều **Tính năng**: - AI Speaking Coach: record giọng → AI chấm phát âm + so sánh native speaker - Luyện IELTS Speaking Part 1/2/3 (mock interview với AI) - Shadow reading: nghe → lặp lại → AI so sánh độ chính xác - Pronunciation scoring có điểm số và progress chart **Tech mới**: - Speech-to-text: Whisper API hoặc Google Speech-to-Text - Text-to-speech: native audio - WebRTC / MediaRecorder API (web) + Flutter audio recording --- ### PHASE 5 — Full TOEIC Mock Test **Mục tiêu**: Platform luyện TOEIC toàn diện chuẩn ETS **Trigger**: Phase 4 xong, cần nội dung premium cao cấp hơn **Tính năng**: - Full TOEIC test chuẩn ETS: 200 câu, 120 phút - Audio Listening chuẩn cho Part 1–4 - Auto-score: tính điểm 10–990 theo bảng quy đổi ETS - Phân tích chi tiết: điểm mạnh/yếu từng Part, so sánh lần trước - Bộ đề theo năm: ETS 2023, 2024, Actual Test... - Đếm ngược đến ngày thi + lịch ôn tập gợi ý --- ## Quyết định kiến trúc quan trọng | Quyết định | Lý do | |---|---| | Không auth Phase 1 | Giảm scope, validate market trước | | Email only Phase 2, không OAuth | Đơn giản nhất để build, OAuth Phase 4+ | | Không xác thực email Phase 2 | MVP — giảm friction đăng ký tối đa | | Chỉ 3 field đăng ký (tên/email/pass) | Friction thấp nhất, đủ để identify user | | Guest chỉ xem preview, không làm được | Buộc đăng ký để dùng, giúp thu thập user data | | Không thanh toán Phase 2 | Hiểu behavior trước khi charge tiền | | Không Flutter Phase 2 | Web đã responsive, Flutter Phase 3 khi có traction | | Coins tên "Xu" | Gần gũi user VN hơn "Credits" hay "Points" | | Pay-as-you-go thay subscription | User VN ít cam kết dài hạn, mua theo nhu cầu dễ convert hơn | | AI model tier theo Xu | Tạo upsell tự nhiên — user thấy feedback tốt hơn khi dùng model cao hơn | | Rewarded ads mobile, banner ads web | Phù hợp từng platform — mobile chịu video, web chịu banner | | Supabase tạm Phase 1 | Ra nhanh hơn 2–3 tuần, schema chuẩn để migrate sau | | GLM thay OpenAI/Claude | Rẻ hơn, OpenAI-compatible, swap dễ | | Desktop-first | Target TOEIC learner hay dùng máy tính | | TanStack Query + Zustand | Server state tách biệt client state rõ ràng | | NestJS Phase 2 | Supabase đủ để validate, NestJS khi scale | | Speaking AI Phase 4 | Cần infra + monetization ổn định trước khi làm realtime audio | | Full mock test Phase 5 | Cần content team + audio, không phải tech problem | --- ## Conventions - **Ngôn ngữ**: Tiếng Việt cho UI người dùng, English cho code/comments/type names - **Giải thích đáp án**: Luôn bằng tiếng Việt - **AI feedback**: Nhận xét tổng thể tiếng Việt, ví dụ sửa tiếng Anh - **Desktop-first**: Design và test trên 1280px trước, mobile sau - **YAGNI / KISS**: Không build thứ chưa cần, từng Phase giải quyết từng vấn đề - **Schema chuẩn ngay từ đầu**: Dù dùng Supabase, PostgreSQL schema phải production-ready - **Coins = "Xu"**: Dùng nhất quán trong code lẫn UI --- ## Rủi ro đã nhận diện | Rủi ro | Mức độ | Xử lý | |---|---|---| | Content đề TOEIC chất lượng thấp (crawl) | 🔴 Cao | Crawl ít + clean kỹ, tự soạn dần thay thế | | GLM chấm writing không đủ tin cậy | 🟡 TB | Test prompt kỹ, fallback OpenAI nếu cần | | Latency GLM từ VN cao | 🟡 TB | Benchmark thực tế, cache response nếu cần | | User mất progress (localStorage Phase 1) | 🟡 TB | Chấp nhận Phase 1, auth Phase 2 giải quyết | | Bản quyền đề TOEIC crawl | 🟡 TB | Seed nhanh, thay bằng nội dung tự soạn dần | | Supabase free tier limit | 🟢 Thấp | 500MB đủ Phase 1, migrate trước khi hit limit | | AdSense bị block (adblocker) | 🟡 TB | Rewarded ads mobile bù lại, không phụ thuộc 1 nguồn | | VNPay/MoMo integration phức tạp | 🟡 TB | Dùng payment gateway trung gian (Stripe VN, PayOS) | | Audio quality Phase 5 | 🟡 TB | Budget cho studio recording hoặc TTS premium |