Backend Report: - Application/Reports/Dtos/DashboardStatsDto: 5 KPI + PhaseCount + SupplierCount + ProjectCount + MonthlyValue - Application/Reports/Queries/GetDashboardStats handler: total/active/overdue/published this month/totalValueActive + byPhase + top 5 NCC/du an + 12 thang monthly (fill zero khi thang empty) - Application/Reports/Services/IContractExcelExporter interface - Infrastructure/Reports/ContractExcelExporter: ClosedXML workbook 10 cot, header style bold+blue, number format #,##0, formula SUM, auto-fit, freeze header - Application/Reports/Commands/ExportContractsToExcelCommand: filter phase/supplier/project/date range - Api/Controllers/ReportsController: GET /reports/dashboard, GET /reports/contracts/export - DI register IContractExcelExporter (Scoped) Frontend fe-admin: - types/reports.ts: DashboardStats type - components/BarChart.tsx: generic horizontal bar chart — chi Tailwind, khong thu vien ngoai - pages/DashboardPage.tsx REWRITE: 5 KPI card (FileText/TrendingUp/AlertTriangle/CheckCircle2/Coins) + by-phase bar + monthly 12-month chart + top 5 NCC + top 5 du an + skeleton loader - pages/ReportsPage.tsx MOI: filter phase/fromDate/toDate → export Excel button - Route /reports vao App.tsx E2E verified: - GET /api/reports/dashboard → 200 voi day du KPI + monthly fill 12 thang - GET /api/reports/contracts/export → 200 xlsx 7229 bytes (Microsoft Excel 2007+) Docs consolidation (theo yeu cau user): - docs/rules.md MOI: 9 section coding conventions (ngon ngu UI/code/DB/docs, BE Clean Arch, CQRS+MediatR, Validation FluentValidation, Error handling, Async, Entity rules, DI, Package pinning, FE React/TS erasableSyntaxOnly, path alias, TanStack Query, Permission guard, Toast+error, DB convention, Git commit format, Docs structure, Testing, Security) - docs/architecture.md MOI: layered overview ASCII art, request lifecycle (1 POST/api/contracts qua 10 step), workflow state machine 9 phase, permission model, data flow sequence diagram 4 actor (Drafter/Manager/CCM/BOD/HRA), deployment architecture Phase 5, skill library, non-functional table - docs/database/schema-diagram.md MOI: full ERD 19 table mermaid + data flow diagram + vong doi 1 HD (create → 7 transition → gen ma → publish) + index strategy table + relationship cardinality + soft delete behavior + SQL queries (inbox/dashboard/gen ma) + migration history - docs/gotchas.md UPDATE: 17 → 26 pitfalls, them section "Claude Code harness quirks" (Edit File not read, DI build pass nhung runtime fail) + "Contract workflow" (ma HD gen 2 lan, BE-FE NEXT_PHASES sync, race condition) + "Permission matrix" (cache real-time, MenuKey typo) - docs/STATUS.md: Phase 4 MVP done, docs entry points section liet ke het, next Phase 5 Production - docs/HANDOFF.md: phase table them Phase 4 row, file tree update voi Reports, test points day du, git state commit 7 - docs/changelog/migration-todos.md: tick Phase 4 MVP items + them iteration 2 list - docs/changelog/sessions/2026-04-21-1430-phase4-report.md: session log voi thong so cumulative (BE 3100 LOC, 30 docs) - CLAUDE.md root: update Tai lieu quan trong section them rules.md, architecture.md, schema-diagram.md, .claude/skills (13 links now) Bug fix: - TS unused import ContractPhaseLabel trong DashboardPage - DI thieu register IContractExcelExporter — build pass but runtime would fail (added) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.6 KiB
Rules — Coding Conventions SOLUTION_ERP
Nguồn duy nhất cho rules. Bất cứ commit nào vi phạm cần justify trong message.
1. Ngôn ngữ
| Thành phần | Ngôn ngữ |
|---|---|
| UI FE | 100% tiếng Việt |
| Code (.cs/.ts) | English (tên biến/hàm/class) |
| Comment code | Tiếng Anh hoặc Việt — miễn rõ |
| Tên DB table + column | English PascalCase (Contracts, SupplierId) |
| Business label | Tiếng Việt (vd "Đang soạn thảo" cho phase 2) |
| Commit message | English hoặc Việt — preferred English |
| Doc MD (docs/) | Tiếng Việt (audience là người Việt) |
2. Backend — .NET 10
2.1 Clean Architecture dependency rule
Api → Application ← Domain
↑
Infrastructure ──┘
- Domain: pure, chỉ dùng
Microsoft.Extensions.Identity.Stores(cần cho IdentityUser) — không depend ASP.NET Core framework - Application: depend Domain; CQRS handlers + interfaces + DTOs + validators
- Infrastructure: depend Application + Domain; impl interfaces (EF, JWT, rendering, services)
- Api: depend Application + Infrastructure; Controllers + middleware + composition root
2.2 CQRS + MediatR pattern
- 1 feature = 1 file
{Verb}{Entity}Command.cs/{Verb}{Entity}Query.cs - File chứa: Command record + Validator + Handler (cùng 1 file cho compact)
- Dài thì tách:
{Feature}/Commands/{Create}/...
Command naming:
Create{Entity}Command // tạo mới
Update{Entity}Command // update full
Patch{Entity}FieldCommand // update 1 field
Delete{Entity}Command // soft delete (default)
{Action}{Entity}Command // action domain-specific (vd TransitionContract)
Query naming:
Get{Entity}Query // single by Id
List{Entity}sQuery // list với paging
{Entity}{Purpose}Query // custom query (vd GetMyInboxQuery)
2.3 Validation
- FluentValidation (KHÔNG dùng DataAnnotation)
- Validator = class trong cùng file command:
{Command}Validator : AbstractValidator<{Command}> - Pipeline:
ValidationBehaviorauto-chạy trước Handler — throwValidationException→ 400 ProblemDetails
2.4 Error handling
- KHÔNG try-catch ở Controller
- Throw domain exception:
NotFoundException,ForbiddenException,UnauthorizedException,ConflictException,ValidationException GlobalExceptionMiddlewaremap exception → HTTP status + ProblemDetails JSON- Logging: Serilog structured, không
Console.WriteLine
2.5 Async/await
- Tất cả I/O (DB, HTTP, file) PHẢI async
- Truyền
CancellationTokenmọi async call - Tên method async: suffix
Async(trừ ASP.NET action signature)
2.6 Entity rules
- Mọi entity mới extend
BaseEntity(Id Guid + audit) hoặcAuditableEntity(+ soft delete) - PK:
IdtypeGuid, defaultGuid.NewGuid()trong constructor - FK:
{Entity}IdtypeGuid - Enum:
HasConversion<int>()trong EF config - String: luôn
HasMaxLength(n)trong EF config — không đểnvarchar(max)trừ khi là JSON/rich text - Money:
decimal+HasPrecision(18, 2) - DateTime: luôn UTC, lấy từ
IDateTime.UtcNow(khôngDateTime.Now) - Query filter soft delete:
HasQueryFilter(x => !x.IsDeleted)cho AuditableEntity
2.7 DI registration
- Singleton: stateless services (DateTimeService, FormRenderer)
- Scoped: per-request services (JwtTokenService, ContractWorkflowService, DbContext)
- Transient: lightweight utility (hiếm dùng)
2.8 Package version pinning
- KHÔNG dùng
*hoặclatest— luôn pin version cụ thể - Check compatibility với .NET 10 trước khi add. Gotchas đã biết:
- MediatR 14 → lỗi IMediator DI, pin 12.4.1
- Swashbuckle 10 → conflict OpenApi 2, pin 6.9.0
- Xem
gotchas.mdcho full list
3. Frontend — React 19 + Vite 8 + TS 6
3.1 Named export, không default export
// ✅
export function SuppliersPage() { }
export const MyConstant = 1
// ❌
export default function SuppliersPage() { }
// Exception: App.tsx dùng default export (Vite convention)
3.2 erasableSyntaxOnly (Vite 8)
KHÔNG dùng enum — dùng const-object pattern:
// ❌
export enum SupplierType { NhaCungCap = 1 }
// ✅
export const SupplierType = { NhaCungCap: 1, NhaThauPhu: 2 } as const
export type SupplierType = typeof SupplierType[keyof typeof SupplierType]
3.3 Path alias
- Dùng
@/cho absolute import:import { api } from '@/lib/api' - Config ở
vite.config.ts+tsconfig.app.json(chỉpaths, khôngbaseUrl)
3.4 Data fetching
- TanStack Query cho mọi API call (KHÔNG useState + useEffect thủ công)
useQuerycho GET,useMutationcho POST/PUT/DELETEinvalidateQueriessau mutation thành công
3.5 API calls
- Qua
apiinstance (src/lib/api.ts) — đã wire axios interceptor JWT + 401 redirect - Base URL:
/api(Vite proxy →:5443)
3.6 Permission guard
<PermissionGuard menuKey="Contracts" action="Update">
<Button>Sửa</Button>
</PermissionGuard>
// Hook
const { can } = usePermission()
if (!can('Contracts', 'Update')) return null
Nhớ: FE guard chỉ là UX. BE policy [Authorize(Policy = "Contracts.Update")] là source of truth.
3.7 Component naming
- PascalCase cho file:
SuppliersPage.tsx,DataTable.tsx - kebab-case cho utility:
api.ts,cn.ts - React component = PascalCase
- Hook: prefix
use, ví dụusePermission
3.8 Toast + error
- Dùng
sonner(đã wire<Toaster>ở App) toast.success('...'),toast.error(getErrorMessage(err))- Error helper:
src/lib/apiError.tsextract từ ProblemDetails
3.9 Duplicate giữa 2 FE
CÓ CHỦ ĐÍCH. Mỗi app (fe-admin + fe-user) là standalone — copy shared code giữa 2 app thay vì extract ra package chung. Lý do: 2 app có UX rất khác (admin mission-critical, user workflow-heavy), evolution độc lập.
Khi share code: cp file từ app này sang app kia. Đồng bộ tay khi có breaking change.
4. Database
4.1 Convention
| Item | Rule |
|---|---|
| Schema | 1 schema duy nhất: dbo |
| Table | PascalCase tiếng Anh, plural: Contracts, Suppliers |
| Column | PascalCase: FullName, CreatedAt |
| PK | Id — uniqueidentifier |
| FK | {Entity}Id — uniqueidentifier |
| Index | IX_{Table}_{Col} |
| Unique | UX_{Table}_{Col} |
Full: xem database/database-guide.md.
4.2 Migration
- Một commit = 1 migration (trừ khi nhiều change nhỏ trong cùng feature)
- Naming PascalCase:
AddMasterData,AddContractsWorkflow - Commit đủ 3 file:
{Name}.cs,{Name}.Designer.cs,ApplicationDbContextModelSnapshot.cs
5. Git & commits
5.1 Branch
main— deploy branch- Feature branch (nếu nhiều người):
feature/phase{N}-{feature}
5.2 Commit format
[CLAUDE] <scope>: <imperative message>
<body optional — chi tiết what + why>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Scope: Contract · Form · Workflow · Supplier · Auth · Admin · Api · App · Domain · Infra · FE-Admin · FE-User · Docs · CICD · Scripts · Report · Dashboard
5.3 Một commit nên
- Build pass (BE + FE)
- Không leave WIP nửa chừng (trừ khi commit message nói rõ)
- Test E2E với endpoint mới (curl trong session log)
5.4 Không commit
.env,appsettings.*.Local.jsonnode_modules/,bin/,obj/wwwroot/uploads/(user files)SolutionErp_Designdatabase (tạm cho EF CLI)
6. Docs
6.1 Khi nào viết session log
- Session có commit thay đổi code → PHẢI tạo
docs/changelog/sessions/YYYY-MM-DD-HHMM-{topic}.md - Session chỉ Q&A, không đụng file → không cần
6.2 Session log format
# Session YYYY-MM-DD HH:MM — <topic>
**Dev:** Claude / Copilot / <name>
**Duration:** ~Nh
**Base commit:** <sha>
## Làm được
### Chunk X — <name>
- bullet list deliverable
## E2E verified
<curl output hoặc screenshot>
## Bug gặp + fix
| Bug | Fix |
## Docs updates
## Handoff
## Thông số cumulative
6.3 Update khi thêm feature
- Thêm entity → update
database/database-guide.mdschema section - Thêm endpoint → update
flows/relevant flow - Thêm bug fix lặp lại → update
gotchas.md - Thêm pattern → update skill tương ứng ở
.claude/skills/ - Phase đổi → update
STATUS.md+HANDOFF.md+changelog/migration-todos.md
7. Testing (hiện chưa có test tự động)
Phase 1-4 — manual test:
- E2E qua curl/Postman trong mỗi session log
- Build + TS check mỗi commit
Phase 5 — tự động (chưa làm):
- Unit test: xUnit cho BE, Vitest cho FE
- Integration test: TestContainer SQL Server cho BE
- E2E test: Playwright cho FE
8. Security baseline
- HTTPS everywhere prod
- JWT Secret trong user-secrets / env var (không commit appsettings)
- Password hash: PBKDF2 (Identity default)
- CORS whitelist chỉ 2 FE origin
- SQL injection: EF Core parameterized (không raw SQL trừ khi phải)
- Audit log: mọi ghi phải log qua
ContractApprovalhoặcAuditLog(future) - Rate limit:
/api/auth/login5 req/min/IP (chưa implement — Phase 5)
9. Liên quan
CLAUDE.md— entry point cho AI agentarchitecture.md— kiến trúc chi tiết (layered, CQRS, state machine)gotchas.md— pitfalls đã gặpdatabase/database-guide.md— DB conventionsflows/— sequence diagram per feature