quy-tac-code
Quy tắc code TypeScript và React cho dự án JDX-Gamers Website (JGameApp). Dùng khi: viết TypeScript (tránh any, strict typing), tổ chức React hooks, useEffect dependencies, performance memo/callback,
SKILL.md.claude/skills/quy-tac-code/SKILL.md
GitHubQuy Tắc Code — JDX-Gamers Website
TypeScript
| Quy tắc | Chi tiết |
|---|---|
❌ Cấm any |
Dùng unknown rồi narrow type |
❌ Hạn chế as X |
Chỉ khi chắc chắn kiểu |
| ✅ Explicit return type | Cho functions public/exported |
✅ interface cho object shapes |
type cho union/alias |
✅ enum / as const |
Cho tập giá trị cố định |
✅ Optional chaining ?. |
Thay vì kiểm tra thủ công |
✅ Nullish coalescing ?? |
Thay ` |
Types tổ chức: Mỗi domain entity có interface riêng trong /types/. Tách UI types (FormData) ≠ API types (Request/Response).
React & Hooks
- Pages / Components / Dialogs: Chỉ render UI — không chứa business logic
- Hooks: Chứa toàn bộ state management và business logic
- File
.tsx> 500 dòng → tách thành sub-components - Không gọi API trực tiếp từ component — phải qua hook
- Custom hook phải có tiền tố
use - Mỗi hook có một trách nhiệm rõ ràng
- Các file giao diện
.tsxkhông viết trực tiếp code logic, chỉ chứa JSX và gọi hook để lấy data/logic
useEffect:
- Luôn khai báo đầy đủ dependencies
- Luôn có cleanup khi subscribe/timer/listener
- Không dùng
useEffectđể sync state — dùnguseMemo
Performance:
React.memo()khi component render nhiều với props ít thay đổiuseMemo/useCallbackchỉ khi đo được lợi ích- Debounce search input ≥ 300ms (chuẩn dự án: 800ms)
Import Order (BẮT BUỘC)
// 1. React & third-party libraries
// 2. Shared utilities/components (@/shared/...)
// 3. Feature types (../types)
// 4. Feature services (../services)
// 5. Feature hooks (../hooks)
// 6. Feature components (../components)
Barrel Exports
- Mỗi folder (
types/,hooks/,services/,components/) phải cóindex.ts - Import từ folder:
from '../types'— không import từ file cụ thể bên trong
Comments & Logging
- Tất cả comments phải bằng tiếng Việt
- JSDoc bắt buộc cho functions public trong services, hooks, shared utils
- ❌ TUYỆT ĐỐI KHÔNG dùng
console.log/console.error - ✅ Dùng
ApiLogger(info,warn,error,debug)
// ✅ Đúng
ApiLogger.info('Bắt đầu tải danh sách hóa đơn')
ApiLogger.error('Lỗi hệ thống:', error)
// ❌ Sai
console.log(...)
An Toàn Khi Sửa Code
- Code đang hoạt động ổn định → KHÔNG sửa trừ khi có yêu cầu
- Chỉ sửa trong phạm vi chức năng được yêu cầu
- KHÔNG tự ý refactor khi không có task rõ ràng
- File mới:
create_file| File đã tồn tại:replace_string_in_file - Không thêm thư viện mới khi chưa được đồng ý
⛔ KHÔNG Tự Ý Sửa File Khác Khi Được Yêu Cầu Sửa File Cụ Thể
- Khi user yêu cầu sửa file A, chỉ sửa đúng file A — không lan sang file B, C dù có liên quan
- Nếu phát hiện file khác cũng cần sửa → DỪNG LẠI, báo cáo và chờ user xác nhận trước khi sửa
- Không tự ý "fix" hay "cải thiện" những file không nằm trong yêu cầu
⛔ Tôn Trọng Yêu Cầu Trước Đó — Không Tự Ý Sửa Lại
- Khi user đã yêu cầu sửa một field/theo một cách cụ thể (VD: "chỉ hiển thị tài khoản X, không gọi API tài khoản"), giữ nguyên cách đó — không tự ý sửa lại thành cách mặc định (VD: gọi API tài khoản)
- Nguyên tắc: Yêu cầu sau ghi đè quy tắc mặc định. Không "sửa lại cho đúng chuẩn" nếu user đã yêu cầu khác đi
- Ví dụ: Số tài khoản — mặc định gọi API
TaiKhoanApiServiceđể hiển thị. Nhưng nếu user đã yêu cầu "chỉ hiển thị tài khoản ngân hàng" hoặc "không gọi API", thì giữ nguyên, không tự ý sửa thành gọi API
Error Handling
| Loại lỗi | Hiển thị | Log |
|---|---|---|
| Client validation | Inline + Toast tổng hợp | Không |
| Server (4xx) | ValidationErrorDialog |
Không |
| System (≥ 500) | Toast lỗi chung | ApiLogger.error() |
| Unexpected catch | Toast lỗi chung | ApiLogger.error() |
Route URL Convention (JGameApp)
- Toàn bộ route khai báo trong 1 file duy nhất
src/modules/JGameApp/routes/routeConfig.tsx(mảngrouteConfig, không rải rác nhiều nơi) — path không có prefix/jgame(được mount tại/jgame/*ởApp.tsxroot). - Mỗi route là 1 object
{ path, element, pageId, requireAuth?, guestOnly?, requireShopOwner?, requireAffiliate?, requireAdmin? },elementluôn là component đãlazy()khai báo ở đầu file. - Điều hướng: dùng
<Link to="/jgame/...">hoặcuseNavigate()củareact-router-dom— không có hook điều hướng riêng theo pageId như dự án cũ. - Thêm route mới: khai báo
lazy()import ở đầu file + thêm 1 object vào mảngrouteConfig, đặt đúng nhóm comment (Storefront/Account/Admin/...) đã có trong file.
Checklist Nhanh
- Không
anytrong code mới - Không
console.log/console.error - Không business logic trong
.tsx - Không API call trực tiếp từ component
- Đã dùng lại từ
/shared/nếu có - Comments bằng tiếng Việt
-
index.tsđược cập nhật khi thêm file mới - Không refactor ngoài phạm vi yêu cầu
- Route mới đã khai báo
lazy()+ thêm object vàorouteConfigtrongroutes/routeConfig.tsx