---
name: trg-erp-feature-standard
description: Kỹ năng tạo module/tính năng mới theo chuẩn Feature-based, dành riêng cho dự án TRG-ERP (Next.js, React, Mongoose).
---

# Kỹ Năng / AI Skill: TRG-ERP Feature Standard

Bạn được trang bị định hướng này để tuân thủ kiến trúc chuẩn của dự án **TRG-ERP** khi tạo hoặc refactor các tính năng mới trong `trg-crm`.

Mặc dù một số tính năng hiện tại (như `customer` hay `customer-care`) có thể vẫn đang dùng cấu trúc Client Component nguyên khối (bịch) với rất nhiều `useState`, **bạn BẮT BUỘC phải hướng việc code theo tiêu chuẩn mới dưới đây** (lấy cảm hứng từ chuẩn UI Zero-State/Server-First) nhắm mục tiêu tối ưu hiệu suất và module hóa mã nguồn.

## MỤC TIÊU CỐT LÕI

1. **Kiến trúc Feature-Based**: Mọi đoạn code của tính năng phải gom cụm trong `features/[tên-tính-năng]/`.
2. **Server Components First**: Ưu tiên sử dụng Server Component cho thư mục `app/`. UI nguyên khối như `[Feature]Overview.tsx` ôm tất cả logic cần được hạn chế tối đa.
3. **Quản lý State qua URL parameters**: Thay vì sử dụng `useState` cho Filter, Search, Pagination, View Mode... tất cả phải được ghi vào URL (qua `useSearchParams`). Điều này giúp dễ dàng chia sẻ link và giữ lịch sử trình duyệt.
4. **Phân quyền Kép (Dual-layer Authorization)**: Tính năng chặn kiểm tra ở UI thông qua `usePermissions()` và ở backend Mongoose qua `getPermissionQuery()`. Hoàn toàn KHÔNG SỬ DỤNG component `<PermissionGuard>`.
5. **Flow dữ liệu Database**: REST API / Server Action → `actions.ts` → `service.ts` (Nghiệp vụ Mongoose) → `repository.ts` (Truy vấn DB).

## CẤU TRÚC THƯ MỤC CHUẨN

```
src/
├── app/(dashboard)/[tên-tính-năng]/
│   └── page.tsx                 ← Entry point. Chỉ định Layout, ưu tiên Server component gọi dữ liệu.
│
├── features/[tên-tính-năng]/
│   ├── actions.ts               ← Server Actions (Dành cho Mutation: Tạo, Sửa, Xóa).
│   ├── service.ts               ← Business logic, auth checks, geocoding, notifications (Sử dụng Mongoose Models).
│   ├── repository.ts            ← Tương tác Database thuần túy (Mongoose find, aggregate, count).
│   ├── types.ts                 ← Khai báo TypeScript types, interfaces.
│   ├── constants.ts             ← Khai báo hằng số tĩnh (Options, Map trạng thái, Labels).
│   └── components/              ← Các UI Components của tính năng
│       ├── [Tên]Feature.tsx     ← Mảnh ghép chính. Nếu không thể chia tách hoàn toàn, đây là Client Component kết nối hooks.
│       ├── ui/
│       │   ├── [Tên]Toolbar.tsx ← Chứa Search, Filters, GroupBy, Export (Liên kết chặt chẽ với URL query/Params).
│       │   ├── [Tên]Table.tsx   ← Bảng dữ liệu / Danh sách Card.
│       │   └── [Tên]Stats.tsx   ← Các thẻ thống kê (Stat cards).
│       └── modals/              ← Chứa tất cả Modal/Dialog Add, Edit, View.
```

## BẢNG QUY TẮC SỬ DỤNG `useState`

| Use case | Quy tắc tại TRG-ERP | Hướng giải quyết đúng đắn |
|---|---|---|
| Mở / Đóng Modal Form | ❌ Hạn chế tại file root | Gom state đóng/mở trực tiếp vào Component của nút bấm, hoặc sử dụng `DialogTrigger` tích hợp. |
| Loading trạng thái Form | ❌ Không dùng `useState(false)` | Dùng hook `useTransition()` của React để theo dõi tiến trình submit mà không re-render toàn component. |
| Lưu Search Keyword, Filter... | ❌ Không dùng State nội bộ | Cập nhật URL thông qua `router.push('?search=...')` và đọc từ `useSearchParams()`. |
| Cập nhật dữ liệu Dropdown | ✅ Có thể dùng, hoặc ưu tiên | Tạo API lấy danh mục (Categories/Reference data) tại `useEffect` 1 lần duy nhất trên client, hoặc bọc `useQuery`. |
| Danh sách Data Table | ✅ Đang dùng Hook tuỳ chỉnh | Tính năng đang dùng Custom hook như `useCustomers` quản lý data fetch từ backend API. Hãy giữ cấu trúc này. |

## KIẾN TRÚC PHÂN QUYỀN KÉP (DUAL-LAYER PERMISSION)

Khác với file mẫu `SKILL 1` (chèn `<PermissionGuard>` vào file tsx), dự án **TRG-ERP** sở hữu hệ thống phân quyền phức tạp và sâu (chi tiết mức độ từng dòng dữ liệu dựa trên role/assignedTo/careStaff). Mọi module mới BẮT BUỘC phải tuân thủ 2 chốt chặn sau:

**1. Chốt chặn UI (Client): Hook `usePermissions()`**
- Sử dụng hook tại `hooks/usePermissions.ts`. Nó trả về các hàm boolean: `hasPermission`, `isOnlyOwn`, `canEditItem`, `canDeleteItem`, `canCreateItem`.
- **Không dùng component `<PermissionGuard>`**. Hãy ẩn hiện logic trực tiếp bằng điều kiện: `{canEditItem(row, "customer") && <Button>Sửa</Button>}`.
- Quyền của dự án được kiểm tra chặt tới cấp độ **Owner của từng dòng Data** thông qua trường `assignedTo` (người phụ trách chính) và mảng `careStaff` (nhân viên chăm sóc). Đặc thù TRG-ERP là nhân viên thuộc `careStaff` nhưng không phải `assignedTo` thì thường chỉ xem (View Only).

**2. Chốt chặn Data (Backend Service): `getPermissionQuery`**
- Tại lớp truy vấn `service.ts` / API, phải gọi `getPermissionQuery(auth, "tên-trường-khóa", "tên-module")` được cung cấp từ `lib/permissions.ts`.
- Object query trả về phải được dán đè vào tham số của `Model.find(query)` để bảo vệ ở mức Database, tránh thất thoát dữ liệu ngay cả khi hacker gọi trực tiếp Endpoint.

**3. 🚀 ĐỊNH HƯỚNG BẢO TRÌ BẢN CLONE (V2)**
Hook phân quyền hiện tại đang kiểm tra quá sâu và Gắn cứng (Hard-code) với các property như `item?.assignedTo` và `item?.careStaff` (vì đây là dự án CRM). Sự gắn cứng này khiến cho việc tạo các Module phi-khách-hàng (VD: Phiếu Chi/Nhập Kho) trở nên vô cùng rác rưởi vì hàm phân quyền phải bọc cả tá mệnh đề `if (module === 'kho')`.
- **Nếu làm bản V2**, bắt buộc phải biến thiết kế phân quyền này về thành **Policy-Based (Generic)**: File phân quyền không tự đi tìm `assignedTo` nữa, mà nhận biến truyền từ UI xuống (Ví dụ: `canEditItem(row, module, row.nguoi_tao)`). Vận dụng tính Động để dễ bảo trì mở rộng lên 50-100 modules.

## QUY TRÌNH BA LỚP (REPOSITORY - SERVICE - ACTION)

Khác với Prisma CRUD đơn giản, dự án TRG-ERP sử dụng Mongoose và chia lớp cực kỳ rõ ràng:

1. **`repository.ts` (Data Access Layer)**:
   - Chỉ được chứa logic query database (find, updateOne, aggregate).
   - Không được gọi logic kiểm tra quyền Auth hay gửi Notifications ở đây.
   - Trả về Lean Object hoặc Mongoose Document.

2. **`service.ts` (Business Logic Layer)**:
   - Nhận đối số `auth: JWTPayload` từ lớp gọi để kiểm tra phân quyền.
   - Apply filter, gom nhóm, gọi geocoding, kết nối nhiều models (lookup aggregate).
   - Gọi logic phụ (Log activities, Send Notifications sau khi hoàn tất).

3. **`actions.ts` / API Routes (Controller / Mutation Layer)**:
   - Điểm chạm của giao diện (`"use server"`).
   - Gọi các hàm trong `service.ts` truyền vào user session hiện hành.
   - Thao tác revalidate cache hoặc trả về JSON structure: `{ success: true, data: ... }`.

## CẤU TRÚC GIAO DIỆN CHUẨN

Thay vì ôm tất cả vào file `<TênFeature />`, hãy tách nó ra:
1. **Header/Toolbar**: Thiết kế đồng bộ. Nút "Thêm mới", "Xuất Excel", "Cài đặt" nằm trên cùng. Thanh Search + Filter Options (Trạng thái, Ngày, Đối tượng...) nắm ở Row kế tiếp. Trên Mobile, nên có nút toggle để thu gọn Filter panel.
2. **Data Table / Data View**: 
   - Table hiển thị cột rõ ràng.
   - Tên / Code Entity => Click vào sẽ trỏ đến trang chi tiết hoặc Modal chi tiết.
   - Cột hành động (Actions): Nằm ở cuối bên phải. Nếu nhiều hành động, đưa vào Dropdown.
3. **Thống Kê (Stat Cards)**: Chứa các thẻ tóm tắt (Tổng số, Theo trạng thái). Click thẻ này giúp trigger URL Parameter để filter Table phía dưới.

---
**Tóm lại**: File hướng dẫn này là "Kim chỉ nam" cho Team/AI khi được giao nhiệm vụ code chức năng mới trong thư mục `features/` của TRG-ERP, đảm bảo sự tách biệt dữ liệu, đồng bộ UX, và clean code nhất.
