UX/UI và design system
Trang này dùng để trả lời nhanh khi thầy hỏi: giao diện LMS được thiết kế theo hướng nào, đổi màu/sửa layout ở đâu, và kiểm tra thế nào để không làm vỡ trải nghiệm người dùng.
Câu trả lời ngắn
LMS không thiết kế như landing page quảng cáo. Đây là hệ thống vận hành đào tạo, nên UI ưu tiên đọc nhanh, thao tác lặp lại, kiểm quyền rõ, trạng thái rõ và hạn chế nhầm lẫn ở các vùng nguy hiểm như publish, delete, grade, payment, submit quiz. Frontend dùng Angular 20, Signals, OnPush, component theo feature; giao diện dùng token xanh chủ đạo #0056D2, nền bg-slate-50, thẻ bg-white border border-gray-200 shadow-sm, tiếng Việt nhất quán, icon SVG qua app-icon/Lucide và thông báo qua Toast/ConfirmDialog thay vì alert()/confirm().
Nguyên tắc thiết kế hiện tại
| Nguyên tắc | Cách thể hiện trong codebase | Khi báo cáo nên nói |
|---|---|---|
| Tập trung nghiệp vụ | Teacher, student, admin, learning, payment nằm trong các feature riêng | Mỗi vai trò có màn hình riêng, không dùng một UI chung rồi ẩn/hiện tùy tiện |
| Dễ quét thông tin | Dashboard, bảng, sidebar, tabs, sticky footer/save bar | UI phục vụ tác vụ quản trị và học tập lặp lại, nên ưu tiên rõ ràng hơn trang trí |
| Trạng thái rõ | Loading, empty, error, toast, confirm dialog | Người dùng biết hành động đã lưu, lỗi gì, cần xác nhận gì |
| Ít rủi ro thao tác nhầm | Vùng nguy hiểm cần confirm, Wiii chỉ click safe target | FE giúp tránh nhầm, backend vẫn là lớp kiểm quyền cuối cùng |
| Nhất quán màu sắc | #0056D2, #004BB5, bg-slate-50, border-gray-200 |
Không tự thêm palette lạ nếu không có lý do |
| Responsive thực dụng | Grid responsive, overflow table/code, sidebar có trạng thái | Kiểm desktop và mobile trước khi nói UI ổn |
| Tiếng Việt chuẩn | User-facing text là tiếng Việt, tránh emoji trong UI nghiệp vụ | Dễ demo và phù hợp bối cảnh trường/học viên Việt Nam |
Design token cần nhớ
Design system hiện tại là hybrid, không phải 100% component library. Repo có token SCSS và shared primitive, nhưng nhiều màn hình feature vẫn dùng Tailwind utility trực tiếp trong template. Khi báo cáo, nên nói là hệ thống đã có quy ước thiết kế rõ và một số primitive dùng lại, nhưng vẫn cần giữ kỷ luật token khi sửa từng màn hình.
| Lớp thiết kế | File/evidence | Ý nghĩa |
|---|---|---|
| SCSS variables | fe/src/styles/_variables.scss |
Token màu, spacing 8px, radius, shadow, breakpoint |
| Shared button/card | fe/src/app/shared/components/ui/button/button.component.ts, fe/src/app/shared/components/ui/card/card.component.ts |
Primitive dùng lại cho UI chung |
| Template utility | fe/src/app/features/**/**/*.html |
Nhiều màn hình dùng class Tailwind trực tiếp, nên sửa scoped theo component |
| Feedback primitive | toast-container, confirm-dialog, dialog |
Thay alert()/confirm(), hỗ trợ UX an toàn hơn |
| Token | Dùng cho | Ghi chú |
|---|---|---|
#0056D2 |
Primary action, active state, focus, link quan trọng | Token xanh chính của LMS |
#004BB5 |
Hover primary action | Giữ cùng hue với primary |
bg-[#0056D2]/5, /10, /20, /30 |
Nền nhấn nhẹ, badge, trạng thái active nhẹ | Tránh phủ xanh quá dày |
bg-slate-50 |
Nền trang/panel nhẹ | Giúp dashboard và form bớt nặng |
bg-white rounded-xl border border-gray-200 shadow-sm |
Card/form/panel trong LMS hiện tại | Đây là pattern thực tế của repo; khi sửa nên giữ nhất quán |
focus:ring-[#0056D2], focus:border-[#0056D2] |
Input, select, checkbox, button focus | Cần cho keyboard/a11y |
| Red/amber/green semantic | Error, destructive, warning, success | Không dùng đỏ chỉ để trang trí |
Bản đồ màn hình chính
| Khu vực | File/màn hình nên mở | Vai trò UX |
|---|---|---|
| Teacher dashboard | fe/src/app/features/teacher/dashboard/teacher-dashboard.component.* |
Course-management focused: greeting, create CTA, pending grading, tabs, status badge, skeleton/empty state |
| Student dashboard | fe/src/app/features/student/dashboard/student-dashboard.component.* |
Continue-learning hero, progress tabs, expandable syllabus, offline download |
| Student my courses | fe/src/app/features/student/student-my-courses.component.* |
Desktop two-column layout, sticky filter sidebar, mobile hides filter |
| Course editor layout | fe/src/app/features/teacher/course-editor/layouts/course-editor-layout/course-editor-layout.component.* |
Full-screen authoring workspace với header, tab router, route-aware sidebar, skeleton, chat widget |
| Course editor sidebar | fe/src/app/features/teacher/course-editor/components/sidebar/sidebar.component.ts |
Cây curriculum 3 cấp, reorder, chọn bài/chương |
| Course info | fe/src/app/features/teacher/course-editor/pages/course-info/course-info.component.* |
Multi-section form, sticky section nav, error summary role="alert", sticky save bar |
| Curriculum editor | fe/src/app/features/teacher/course-editor/pages/course-curriculum/course-curriculum.component.* |
Chapter/lesson/section editors, empty-state 3 bước, AI generation CTA, quiz package, batch video upload |
| Section editor | fe/src/app/features/teacher/course-editor/pages/course-curriculum/components/section-editor/section-editor.component.ts |
Modal/panel sửa section, preview PDF/video, footer lưu |
| Interactive video authoring | fe/src/app/features/teacher/course-editor/pages/course-curriculum/components/section-editor/interactive-video-authoring-*.ts |
Tạo interaction, drag-drop, hotspot, quiz trong video |
| Learning player | fe/src/app/features/learning/pages/course-learning.component.* |
Split reader, collapsible outline, mobile dialog sidebar, fixed bottom nav, safe-area/reduced-motion |
| Lesson content | fe/src/app/features/learning/components/lesson-content/lesson-content.component.* |
Single-column learning flow, inline video/file, prose styling, reading progress, competency drawer |
| Adaptive video player | fe/src/app/features/learning/components/adaptive-video-player/adaptive-video-player.component.ts |
Quality selector, network hint, interactive overlay |
| Admin layout/dashboard | fe/src/app/features/admin/presentation/components/admin-layout-simple.component.ts, dashboard/*.component.* |
Operational console: skip link, shared sidebar, mobile drawer, KPI card, approval queue, health card |
| Admin CRUD/table | features/admin/presentation/components/user-management, shared/components/admin/bulk-action-bar |
Stats/search/table/modal, horizontal overflow, pagination, kebab actions, bulk action bar |
| Shared sidebar | fe/src/app/shared/components/navigation/sidebar.component.* |
Sidebar theo role, collapse, tooltip, responsive |
| Toast/dialog/confirm | fe/src/app/shared/components/toast-container/toast-container.component.ts, fe/src/app/shared/components/dialog/dialog.component.ts, fe/src/app/shared/components/confirm-dialog/confirm-dialog.component.ts |
Thông báo, scroll lock, focus trap, ESC topmost, xác nhận hành động |
| Icon system | fe/src/app/shared/components/ui/icon/ICON_GUIDE.md, fe/src/app/shared/components/ui/icon/icon.component.ts |
Giữ icon nhất quán thay vì dùng emoji/text thô |
Khi thầy bảo đổi màu
- Xác định màn hình đang mở thuộc feature nào, ví dụ teacher dashboard hay course editor.
- Mở component
.htmltrước để tìm utility class nhưbg-[#0056D2],text-slate-700,border-gray-200. - Nếu style nằm trong SCSS thì mở file
.scsscùng component. - Chỉ đổi token liên quan, tránh đổi hàng loạt bằng search-replace toàn repo.
- Kiểm lại hover, focus, disabled, loading, error, mobile.
Ví dụ trả lời:
Nếu đổi màu nút chính, em mở template component của màn hình đó,
tìm token #0056D2/#004BB5 hoặc class focus:ring-[#0056D2],
sửa theo design token rồi build frontend và smoke lại desktop/mobile.
Khi thầy bảo sửa layout
| Việc cần sửa | Nơi bắt đầu | Cần kiểm |
|---|---|---|
| Đổi dashboard giảng viên | teacher-dashboard.component.html/.scss |
Create CTA, pending grading, tabbed course list, status badge, skeleton/empty, mobile column |
| Đổi course editor | course-editor-layout.component.*, sidebar.component.ts |
Full-screen workspace, sidebar collapse/backdrop, route tab, chọn lesson/chapter, scroll |
| Đổi form thông tin khóa học | course-info.component.*, sections/course-info-*.component.ts |
Validation, sticky save, dirty state, discard |
| Đổi curriculum tree | sidebar.component.ts, curriculum-editor.service.ts |
Reorder, keyboard, drag preview, save order |
| Đổi student dashboard/my courses | features/student/dashboard, student-my-courses.component.* |
Continue-learning, offline download, sticky filter, mobile filter |
| Đổi learning player | course-learning.component.*, lesson-content.component.* |
Split reader, mobile outline dialog, video/text/quiz/file, progress, next lesson, offline edge case |
| Đổi admin table/dashboard | features/admin/presentation/..., shared/components/admin |
Operational console, skip link, table overflow, pagination/filter, bulk action, confirm |
| Đổi payment UI | features/payment, api/client/payment.api.ts |
Không tin giá từ FE, callback/webhook do backend xác nhận |
Khi thầy bảo sửa logic
Không sửa logic bằng cách chỉ đổi template. Trả lời theo chuỗi:
Em tìm route/component đang hiển thị, xem service/API client nào gọi backend,
rồi mở controller -> use case -> repository/adapter -> migration/table nếu có đổi dữ liệu.
Ví dụ course editor:
| Lớp | File nên mở |
|---|---|
| UI state | fe/src/app/features/teacher/course-editor/store/course-editor.store.ts |
| Curriculum commands | fe/src/app/features/teacher/course-editor/services/curriculum-editor.service.ts |
| API client/types | fe/src/app/api |
| Backend controller/use case | backend/src/main/java/com/example/lms/course_authoring |
| DB schema | backend/src/main/resources/db/migration |
Những điều không nên làm khi sửa UI
| Không nên | Vì sao | Nên làm |
|---|---|---|
| Nói “toàn bộ UI nằm trong một component library hoàn chỉnh” | Thực tế repo dùng hybrid: SCSS token, shared primitive và Tailwind utility trực tiếp | Nói “có quy ước token/primitive, sửa scoped theo component” |
Dùng alert()/confirm() |
Repo đã chuẩn hóa Toast/ConfirmDialog | Dùng service/component confirm hiện có |
| Thêm emoji vào nút nghiệp vụ | UI cần nghiêm túc, repo đã dùng icon SVG | Dùng app-icon/Lucide/icon component |
| Dùng đỏ cho nút thường | Đỏ dành cho lỗi/xóa/nguy hiểm | Dùng primary blue hoặc slate |
| Sửa trực tiếp logic nguy hiểm ở FE rồi tin FE | User có thể gọi API ngoài UI | Backend phải kiểm role, ownership, state |
| Thêm layout chỉ đẹp desktop | Dễ vỡ khi demo máy nhỏ | Kiểm mobile/table overflow |
| Đổi design token toàn repo không kiểm | Dễ gây regression lớn | Sửa scoped theo màn hình và test |
UX của Wiii trong LMS
Wiii/Pointy không được coi như người dùng toàn quyền. UX đúng là:
| Thành phần | Vai trò |
|---|---|
data-wiii-id |
Target ổn định để Wiii quan sát DOM |
data-wiii-click-safe="true" |
Chỉ gắn vào hành động điều hướng/preview an toàn |
| Preview/diff | Nội dung AI tạo phải được teacher xem trước |
| Apply có chủ đích | Teacher xác nhận trước khi mutate dữ liệu |
| Danh sách cấm | Wiii không tự submit quiz, delete, publish, enroll, grade, payment |
Khi bị hỏi “AI có tự sửa bài không?”, trả lời: AI chỉ đề xuất/preview, hệ thống phải có ranh giới safe click và apply có xác nhận; backend vẫn kiểm quyền và trạng thái.
UX của video/offline
| Mảng | UX cần nói |
|---|---|
| Video ABR | Shaka/HLS/DASH chọn chất lượng theo mạng, không bắt người học tự chọn thủ công mọi lúc |
| Upload video | Tách upload/ingest để backend web không bị nghẽn vì tác vụ nặng |
| Offline/PWA | Người học có cache/download, trạng thái sync, retry, storage health |
| Interactive video | Giảng viên tạo interaction có validation trước khi lưu |
Database count nói sao cho đúng
Theo catalog sinh từ Flyway hiện tại:
| Cách nói | Số nên dùng | Ghi chú |
|---|---|---|
| Bảng nghiệp vụ/application tables | 78 |
Từ docs-site/study/sql-schema-catalog.md |
| Materialized views | 2 |
Cũng nằm trong catalog |
| File migration SQL | 107 |
Version cuối có thể là V135 nhưng không có nghĩa là 135 file |
| “88 bảng” | Không dùng nếu chưa nêu cách đếm | Có thể là DBeaver cộng thêm object khác, bảng history/system hoặc schema khác |
| “108 SQL” | Không dùng nếu generator đang ra 107 | Có thể do tính nhầm file ngoài folder migration hoặc file chưa được track |
Khi báo cáo, nên nói: “Theo Flyway hiện tại, catalog sinh tự động ghi 78 application tables, 2 materialized views và 107 migration SQL files. Nếu DBeaver hiển thị số khác, em sẽ nói rõ bộ lọc đang tính thêm object nào.”
Checklist smoke UX/UI
| Mục kiểm | Cách kiểm nhanh |
|---|---|
| Desktop | Mở màn hình chính, kiểm header/sidebar/content không đè nhau |
| Mobile | Viewport hẹp, kiểm không tràn ngang ngoài table/code |
| Text dài tiếng Việt | Dòng dài phải wrap, không làm nút vỡ |
| Loading/empty/error | Có trạng thái rõ, không trắng màn |
| Dangerous action | Delete/publish/grade/payment phải có confirm hoặc guard phù hợp |
| Keyboard/focus | Input/button có focus ring, sidebar/tab không mất định hướng |
| Search docs | Gõ UX/UI, design token, đổi màu, course editor phải ra trang này |
Cách mở code khi cần sửa ngay
cd E:\Sach\Sua\LMS_hohulili
code fe/src/app/features/teacher/course-editor
code fe/src/app/features/teacher/dashboard
code fe/src/app/features/learning/pages/course-learning.component.html
code fe/src/app/shared/components/confirm-dialog
Chạy frontend khi cần xem UI:
cd E:\Sach\Sua\LMS_hohulili\fe
npm install
npm start
Build kiểm nhanh:
cd E:\Sach\Sua\LMS_hohulili\fe
npm run build
Docs hiện tại nói theo source code và catalog sinh tự động. Nếu trước ngày báo cáo có migration mới hoặc merge PR lớn, hãy chạy lại node scripts\generate-schema-catalog.mjs, build docs và kiểm search để cập nhật số liệu.