自建线上教学平台,取代Teachable/Thinkific,支持视频课程、测验、证书和收费
* Preview is for reference only. Actual results may vary depending on the AI model, variable values, and tools used.
You are a full-stack development expert proficient in Next.js 16, Tailwind CSS 4, and Supabase. Help me build an 'Online Course Platform (Learning Management System)' from scratch, designed for Malaysian training providers and educators, replacing expensive SaaS platforms like Teachable and Thinkific.
## Project Overview
Platform Name: {platform_name}
Platform Type: {platform_type}
Initial Number of Courses: {num_courses}
Currency: {currency}
Brand Primary Color: {primary_color}
## Tech Stack
- **Frontend**: Next.js 16 (App Router) + Tailwind CSS 4
- **Backend/Database**: Supabase (Postgres + Auth + RLS + Storage + Realtime)
- **Video**: YouTube/Vimeo embed + Supabase Storage (local upload fallback)
- **PDF Generation**: @react-pdf/renderer (certificate generation)
- **Payments**: Simulated payment flow (with Stripe / Billplz / Revenue Monster interface stubs)
- **Deployment**: Vercel
- **Notifications**: Simulated (console.log + toast notifications, with Email / WhatsApp API stubs)
## Data Model (Supabase Postgres)
### profiles (User Profiles)
- id (uuid, PK, linked to auth.users)
- role (enum: 'student'|'instructor'|'admin')
- full_name_zh (text, Chinese name)
- full_name_en (text, English name)
- email (text, unique)
- phone (text, Malaysian format 60XXXXXXXXX)
- avatar_url (text, nullable)
- bio (text, nullable, personal bio)
- created_at (timestamptz)
- updated_at (timestamptz)
### categories (Course Categories)
- id (uuid, PK)
- slug (text, unique)
- name_zh (text, e.g. 'Business Management' in Chinese)
- name_en (text, e.g. 'Business Management')
- icon (text, emoji)
- sort_order (int)
- created_at (timestamptz)
### courses
- id (uuid, PK)
- instructor_id (uuid, FK -> profiles)
- category_id (uuid, FK -> categories)
- slug (text, unique)
- title_zh (text, Chinese course title)
- title_en (text, English course title)
- description_zh (text, Chinese description)
- description_en (text, English description)
- thumbnail_url (text, course cover image)
- intro_video_url (text, nullable, intro video)
- price (numeric, course price)
- original_price (numeric, nullable, for showing discounts)
- currency (text, default 'MYR')
- pricing_type (enum: 'free'|'one_time'|'subscription')
- subscription_interval (enum: 'monthly'|'yearly', nullable)
- level (enum: 'beginner'|'intermediate'|'advanced')
- language (enum: 'zh'|'en'|'bilingual')
- status (enum: 'draft'|'published'|'archived')
- total_duration_minutes (int)
- total_lessons (int)
- rating (numeric, default 0)
- ratings_count (int, default 0)
- enrollment_count (int, default 0)
- created_at (timestamptz)
- updated_at (timestamptz)
### modules (Course Sections/Chapters)
- id (uuid, PK)
- course_id (uuid, FK -> courses)
- title_zh (text)
- title_en (text)
- sort_order (int)
- created_at (timestamptz)
### lessons
- id (uuid, PK)
- module_id (uuid, FK -> modules)
- course_id (uuid, FK -> courses)
- title_zh (text)
- title_en (text)
- content_type (enum: 'video'|'text'|'quiz'|'assignment')
- video_url (text, nullable, YouTube/Vimeo link or Supabase Storage path)
- video_provider (enum: 'youtube'|'vimeo'|'upload', nullable)
- video_duration_seconds (int, nullable)
- text_content_zh (text, nullable, rich text content)
- text_content_en (text, nullable)
- is_free_preview (boolean, default false)
- sort_order (int)
- created_at (timestamptz)
### quizzes
- id (uuid, PK)
- lesson_id (uuid, FK -> lessons)
- title_zh (text)
- title_en (text)
- passing_score (int, passing percentage, default 60)
- max_attempts (int, default 3)
- time_limit_minutes (int, nullable)
- created_at (timestamptz)
### quiz_questions
- id (uuid, PK)
- quiz_id (uuid, FK -> quizzes)
- question_type (enum: 'mcq'|'true_false')
- question_zh (text, Chinese question text)
- question_en (text, English question text)
- options (jsonb, array like [{'label':'A','text_zh':'...','text_en':'...','is_correct':true}])
- explanation_zh (text, nullable, answer explanation)
- explanation_en (text, nullable)
- points (int, default 1)
- sort_order (int)
### enrollments
- id (uuid, PK)
- student_id (uuid, FK -> profiles)
- course_id (uuid, FK -> courses)
- status (enum: 'active'|'completed'|'expired'|'refunded')
- enrolled_at (timestamptz)
- completed_at (timestamptz, nullable)
- expires_at (timestamptz, nullable, subscription expiry)
- progress_percent (numeric, default 0)
- last_accessed_at (timestamptz, nullable)
- UNIQUE(student_id, course_id)
### lesson_progress
- id (uuid, PK)
- enrollment_id (uuid, FK -> enrollments)
- lesson_id (uuid, FK -> lessons)
- status (enum: 'not_started'|'in_progress'|'completed')
- video_position_seconds (int, default 0, video playback position)
- completed_at (timestamptz, nullable)
- UNIQUE(enrollment_id, lesson_id)
### quiz_attempts
- id (uuid, PK)
- enrollment_id (uuid, FK -> enrollments)
- quiz_id (uuid, FK -> quizzes)
- answers (jsonb, student answer records)
- score (numeric, percentage score)
- passed (boolean)
- started_at (timestamptz)
- completed_at (timestamptz, nullable)
- time_spent_seconds (int, nullable)
### certificates
- id (uuid, PK)
- enrollment_id (uuid, FK -> enrollments)
- certificate_number (text, unique, format 'CERT-YYYYMMDD-XXXX')
- student_name (text, name displayed on certificate)
- course_title (text, course name on certificate)
- issued_at (timestamptz)
- pdf_url (text, nullable, storage path)
### payments
- id (uuid, PK)
- student_id (uuid, FK -> profiles)
- course_id (uuid, FK -> courses)
- amount (numeric)
- currency (text, default 'MYR')
- payment_method (enum: 'fpx'|'card'|'ewallet'|'manual')
- payment_provider (text, nullable, e.g. 'billplz'|'revenue_monster'|'stripe')
- provider_ref (text, nullable, third-party payment reference)
- status (enum: 'pending'|'completed'|'failed'|'refunded')
- paid_at (timestamptz, nullable)
- created_at (timestamptz)
### reviews (Course Reviews)
- id (uuid, PK)
- enrollment_id (uuid, FK -> enrollments)
- student_id (uuid, FK -> profiles)
- course_id (uuid, FK -> courses)
- rating (int, 1-5 stars)
- comment_zh (text, nullable)
- comment_en (text, nullable)
- is_visible (boolean, default true)
- created_at (timestamptz)
- UNIQUE(enrollment_id)
### discussions (Lesson Discussions)
- id (uuid, PK)
- lesson_id (uuid, FK -> lessons)
- author_id (uuid, FK -> profiles)
- parent_id (uuid, nullable, FK -> discussions, for replies)
- content (text)
- is_pinned (boolean, default false)
- is_instructor_reply (boolean, default false)
- created_at (timestamptz)
- updated_at (timestamptz)
### Indexes (Important)
- CREATE INDEX idx_courses_category ON courses(category_id)
- CREATE INDEX idx_courses_instructor ON courses(instructor_id)
- CREATE INDEX idx_courses_status ON courses(status)
- CREATE INDEX idx_lessons_module ON lessons(module_id)
- CREATE INDEX idx_lessons_course ON lessons(course_id)
- CREATE INDEX idx_enrollments_student ON enrollments(student_id)
- CREATE INDEX idx_enrollments_course ON enrollments(course_id)
- CREATE INDEX idx_lesson_progress_enrollment ON lesson_progress(enrollment_id)
- CREATE INDEX idx_quiz_attempts_enrollment ON quiz_attempts(enrollment_id)
- CREATE INDEX idx_discussions_lesson ON discussions(lesson_id)
- CREATE INDEX idx_payments_student ON payments(student_id)
## RLS Security Policies
### Public Access (anon + authenticated)
- courses: SELECT WHERE status = 'published'
- categories: SELECT all
- reviews: SELECT WHERE is_visible = true
### Student Access (authenticated, role = 'student')
- enrollments: SELECT/INSERT WHERE student_id = (select auth.uid())
- lesson_progress: SELECT/UPDATE WHERE enrollment_id belongs to this student
- quiz_attempts: SELECT/INSERT WHERE enrollment_id belongs to this student
- certificates: SELECT WHERE enrollment_id belongs to this student
- discussions: SELECT (enrolled course lessons), INSERT (enrolled course lessons)
- reviews: INSERT WHERE enrollment_id belongs to this student AND status = 'completed'
- payments: SELECT WHERE student_id = (select auth.uid()), INSERT
### Instructor Access (authenticated, role = 'instructor')
- courses: ALL WHERE instructor_id = (select auth.uid())
- modules/lessons: ALL WHERE course_id belongs to this instructor
- quizzes/quiz_questions: ALL WHERE associated course belongs to this instructor
- enrollments: SELECT WHERE course_id belongs to instructor courses
- quiz_attempts: SELECT WHERE associated course belongs to this instructor
- discussions: SELECT/INSERT/UPDATE WHERE associated course belongs to this instructor
### Admin (authenticated, role = 'admin')
- All tables: ALL (verified via profiles.role = 'admin')
## Business Logic
### Progress Tracking
1. Student completes a lesson (video watched/article read/quiz passed) -> update lesson_progress.status = 'completed'
2. Auto-calculate enrollment.progress_percent = (completed lessons / total lessons) * 100
3. Video lessons: track video_position_seconds, mark complete when >90% watched
4. Text lessons: click 'Mark Complete' button
5. Quiz lessons: passing quiz (score >= passing_score) marks as complete
6. When progress_percent = 100%, auto-trigger course completion flow
### Quiz Scoring
1. MCQ: each question has point value, correct = points awarded, wrong = 0
2. True/False: same logic as MCQ
3. score = (points earned / total points) * 100
4. passed = score >= quiz.passing_score
5. Multiple attempts supported, each recorded, highest score used
6. Cannot retry after max_attempts exceeded
7. Timed quizzes auto-submit when time expires
### Certificate Generation
1. Triggered when enrollment.progress_percent = 100% AND all required quizzes passed
2. System auto-creates certificate record
3. Uses @react-pdf/renderer to generate PDF certificate
4. Certificate contents: platform name {platform_name}, student name, course title, completion date, certificate number, instructor signature (text)
5. PDF stored in Supabase Storage certificates/ bucket
6. Students can download and share certificates anytime
### Payment Flow
1. Free courses: direct enrollment, create enrollment record
2. Paid courses: create payment (status='pending') -> simulated payment page -> on success payment.status='completed' + create enrollment
3. Subscription: enrollment.expires_at set to period end, status='expired' on expiry
4. Refund: payment.status='refunded' + enrollment.status='refunded'
### Rating System
1. Can submit 1-5 star review + text comment after course completion
2. course.rating = weighted average of all reviews
3. course.ratings_count = total review count
## Frontend Pages (Public — Responsive)
### Homepage /
- Hero section: platform tagline, search bar, CTA 'Browse Courses' button ({primary_color})
- Category navigation bar (icons + names)
- Featured courses carousel (Card component showing thumbnail, title, instructor, price, rating, student count)
- Latest courses grid (3-col desktop / 1-col mobile)
- Featured instructors section
- Platform statistics (course count, student count, certificates issued)
### Course Catalog /courses
- Left filter sidebar: category, price range (Free/RM0-50/RM50-100/RM100+), level, language, rating
- Right course grid, each Card showing:
- Course thumbnail
- Title (bilingual CN/EN)
- Instructor name + avatar
- Price ({currency} format, original price strikethrough)
- Rating stars + review count
- Level badge (Beginner/Intermediate/Advanced)
- Total duration + total lessons
- 'Free Preview' badge (if free preview lessons available)
- Search box (full-text search on course title and description)
- Sort: Most Popular / Newest / Highest Rated / Price Low to High
- Pagination
### Course Detail /courses/[slug]
- Course cover + intro video player
- Title, subtitle, instructor info
- Price block + 'Enroll Now' button ({primary_color})
- Course content outline (modules -> lesson list, expandable/collapsible)
- Free preview lessons marked with 'Free Preview' tag
- Each lesson shows duration and type icon (video/text/quiz)
- Course description details (bilingual CN/EN tab toggle)
- Instructor bio card
- Student reviews list (stars + comment + avatar + date)
- Sticky sidebar: price, included content summary, enroll button
### Checkout /checkout/[courseSlug]
- Order summary: course info, price
- Payment method selection: FPX online banking, credit/debit card, e-wallets (TNG/GrabPay/Boost)
- Simulated payment button
- Redirect to learning page after successful payment
## Frontend Pages (Student Learning Interface — Desktop-First)
### Student Dashboard /dashboard
- Overview cards: enrolled courses, in progress, completed, certificates count
- 'Continue Learning' section: most recent course + progress bar + 'Continue' button
- Enrolled courses list (Card grid):
- Course thumbnail
- Circular progress chart
- Last accessed time
- Quick actions: Continue Learning / View Certificate
- Recently earned certificates display
### Learning Page /learn/[courseSlug]
- Left side: course outline navigation (modules -> lessons, completed items checkmarked)
- Center main area:
- Video lessons: embedded player (YouTube/Vimeo iframe or HTML5 video), auto-track progress
- Text lessons: rich text content display + 'Mark Complete' button at bottom
- Quiz lessons: quiz interface (see below)
- Bottom: lesson discussion area (comment list + post comment form + instructor reply indicator)
- Top progress bar: overall course completion percentage
- Previous/Next lesson navigation buttons
### Quiz Interface /learn/[courseSlug]/quiz/[quizId]
- Quiz info: title, total questions, passing score, remaining attempts
- Countdown timer (if timed)
- Question display one at a time:
- MCQ: question + 4 options (radio selection)
- True/False: question + true/false buttons
- Question navigation bar (shows answered/unanswered/flagged status)
- Submit confirmation dialog
- Results page: score, pass/fail status, answer comparison per question (correct answer + explanation)
- Retry button if failed (showing remaining attempts)
### Certificates /dashboard/certificates
- Certificate list (Card grid)
- Each certificate shows: course name, earned date, certificate number
- 'Download PDF' button
- 'Share' button (copy certificate verification link)
### Certificate Verification /certificate/[certificateNumber]
- Public page, accessible by anyone
- Shows certificate details: student name, course title, issue date, certificate number
- Verification status indicator (valid/invalid)
## Frontend Pages (Instructor Dashboard — Desktop-First)
### Instructor Overview /instructor
- Summary cards: total students, total revenue, active courses, average rating
- Revenue trend chart (last 6 months line chart)
- Recent enrollment list
- Action items: new discussion replies, new reviews
### Course Management /instructor/courses
- Course list Table: title, status badge, student count, rating, revenue, actions
- Create new course button
- Course editor /instructor/courses/[id]/edit:
- Basic Info tab: title (CN/EN), description (CN/EN), category, level, language, thumbnail upload, intro video
- Pricing tab: free/one-time/{currency} price input, original price
- Content tab:
- Drag-and-drop module reordering
- Drag-and-drop lesson reordering within modules
- Add lesson: video (paste YouTube/Vimeo link or upload), text (rich text editor), quiz
- Quiz editor: add MCQ/True-False questions, set options, mark correct answer, set points, passing score
- Publish tab: preview + publish/unpublish
### Student Analytics /instructor/courses/[id]/analytics
- Enrollment trend chart
- Lesson completion rate heatmap
- Quiz score distribution chart
- Student progress Table: student name, progress %, last active, quiz scores
- Discussion activity
### Revenue Reports /instructor/revenue
- Total revenue, this month revenue, pending settlement
- Revenue detail Table: date, course, student, amount, status
- Filter by course / by month
## Frontend Pages (Admin — Desktop-First)
### Admin Dashboard /admin
- Platform overview: total users, total courses, total revenue, total certificates
- User management: list + role modification + disable
- Course moderation: pending courses list + approve/reject
- Category management: CRUD operations
- Payment records: all payment details + refund operations
## Malaysian Localization
### Currency and Payments
- Default MYR (RM), price format RM XX.XX
- Support Malaysian payment methods: FPX online banking, TNG eWallet, GrabPay, Boost
- SST tax consideration (digital services 8%, configurable)
### Language Support
- Bilingual CN/EN interface toggle
- Course content supports Chinese, English, and bilingual modes
- Date format: DD/MM/YYYY (Malaysian convention)
- Phone format: +60 prefix
### Education Context
- Course categories relevant to local needs: Business Management, Digital Marketing, E-Commerce, Programming, Accounting & Tax, Human Resources
- Certificates display platform name and date in formal format
## Sample Data
### Categories
1. Business Management
2. Digital Marketing
3. E-Commerce
4. Programming
5. Accounting & Tax
6. Human Resources
### Courses (3 examples)
1. **Shopee Store Setup for Beginners**
- Category: E-Commerce, Level: Beginner, Price: RM 149, Duration: 8 hours, 12 lessons
- Modules: Store Preparation (3 lessons), Product Listing Techniques (4 lessons), Marketing & Promotion (3 lessons), Data Analysis (2 lessons)
- Instructor: Chen Mei Ling
2. **Facebook & Instagram Ads Masterclass**
- Category: Digital Marketing, Level: Intermediate, Price: RM 299, Duration: 15 hours, 20 lessons
- Modules: Ad Fundamentals (4 lessons), Audience Targeting Strategy (5 lessons), Creative Asset Production (4 lessons), Data Optimization (4 lessons), Case Studies (3 lessons)
- Instructor: Lim Zhi Hao
3. **SSM Company Registration & Compliance Guide**
- Category: Business Management, Level: Beginner, Price: Free, Duration: 3 hours, 6 lessons
- Modules: Company Type Selection (2 lessons), Registration Process (2 lessons), Annual Compliance (2 lessons)
- Instructor: Wong Wei Jie
### Quiz Example (Shopee Course Module 1 Quiz)
1. What is the URL for Shopee Seller Centre? (MCQ) - 4 options, correct: seller.shopee.com.my
2. Do you need SSM registration to sell on Shopee Malaysia? (True/False) - Answer: Individual sellers no, company sellers yes (True)
3. What is Shopee standard commission rate? (MCQ) - Correct: 2.12% (including SST)
### Students
1. Zhang Wei Ming - Enrolled in Shopee course, 65% progress
2. Lee Shu Fen - Completed SSM course, certificate earned
3. Wong Tai Wah - Enrolled in Ads course, 30% progress
## Supabase Storage Configuration
### Buckets
1. course-thumbnails: course cover images, public read
2. course-videos: uploaded video files, authenticated access
3. certificates: generated PDF certificates, authenticated access
4. avatars: user profile photos, public read
## Key Requirements
1. **Server Components First**: public pages (homepage, course catalog, course detail) use RSC to query Supabase directly, no API round-trip
2. **Client Components for Interactivity**: video player, quiz interface, progress tracking, discussions, payment flow use 'use client'
3. **Server Actions**: course CRUD, quiz submission, review submission, payment processing use Server Actions
4. **Real-time Progress Sync**: use Supabase Realtime to listen for lesson_progress changes, update progress bars in real-time
5. **Certificate PDF Generation**: use @react-pdf/renderer on server-side, store to Supabase Storage
6. **Video Player**: support YouTube/Vimeo embed (iframe) and local upload (HTML5 video), track playback progress
7. **Full-text Search**: course title and description using Postgres tsvector + GIN index, ILIKE fallback for Chinese text
8. **Responsive Design**: public pages mobile-first, learning interface desktop-first (collapsible sidebar)
9. **SEO Optimization**: course detail pages use generateMetadata for dynamic title/description/og:image
10. **Performance**: course listing uses cursor-based pagination, images use next/image optimization, videos lazy-loaded
11. **Security**: all data access via RLS, quiz answers not exposed to frontend, payment verification server-side
12. **Accessibility**: video player aria labels, keyboard navigation support, color contrast compliant