Files
solution-erp/docs/changelog/sessions/2026-04-21-1130-phase1-cruds-permission.md
pqhuy1987 54d6c9ba52 [CLAUDE] Phase1.2: CRUD Master + Permission Matrix + FE admin pages
Backend:
- Domain/Master: Supplier (+ SupplierType 5 loai), Project, Department (AuditableEntity)
- Domain/Identity: MenuItem, Permission, MenuKeys const (12 menu)
- EF Configurations voi unique Code + query filter IsDeleted
- DbSets + IApplicationDbContext interface update
- Application: PagedResult + PagedRequest generic
- Application/Master CQRS CRUD 3 entity (Create/Update/Delete/Get/List voi paging search sort)
- Application/Permissions: GetMyMenuTree (union OR role, filter tree), ListMenuItems, ListPermissionsByRole, UpsertPermission (guard admin khong tu giam quyen), ListRoles
- Api/Authorization: MenuPermissionRequirement + Handler (Admin bypass, query DB)
- Program.cs: register 48 policy {menu}.{action} tu MenuKeys x Actions
- Api/Controllers: Suppliers, Projects, Departments, Menus, Roles, Permissions
- DbInitializer: seed 12 menu + admin full CRUD permissions
- Migration AddMasterData + AddPermissions

Frontend (fe-admin):
- Types: menuKeys.ts const, menu.ts (MenuNode/Role/Permission), master.ts (Supplier/Project/Department + SupplierType const-object)
- AuthContext: load menu from /menus/me, cache localStorage, refreshMenu()
- usePermission hook + PermissionGuard component (wrap button)
- UI kit them: Dialog (modal overlay), Textarea, Select
- Generic: DataTable (column config, sortable, loading, empty) + Pagination
- PageHeader component
- apiError helper extract message tu ProblemDetails
- Layout rewrite: render menu dong tu AuthContext.menu (MenuGroup collapsible + NavLink + lucide icon map)
- Pages: master/Suppliers, master/Projects, master/Departments (CRUD + search + sort + paging + Dialog form)
- Page system/Permissions: ma tran Role x MenuKey x CRUD checkbox (tick tu dong PUT upsert)
- App.tsx them 4 route moi

Bug fix:
- MenuPermissionHandler: EF expression tree khong support switch expression -> tach switch ra ngoai AnyAsync
- TS erasableSyntaxOnly khong cho enum -> SupplierType const-object pattern (typeof[keyof])

E2E verified via Vite proxy:
- GET /menus/me -> 6 root + 6 child nodes (12 menus)
- GET /roles -> 12 roles
- POST/GET/PUT/DELETE /suppliers -> full CRUD, soft delete OK
- tsc -b fe-admin pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 11:30:14 +07:00

90 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Session 2026-04-21 11:30 — Phase 1 đợt 2 complete
**Dev:** Claude (Opus 4.7)
**Duration:** ~1h30m
**Base commit:** `49a5f57`
**Commits:** (sắp tạo — 1 commit lớn cho toàn bộ đợt 2)
## Làm được
### Chunk A — Backend Master data
- Domain: `Supplier` (+ `SupplierType` enum 5 loại: NCC/NTP/TĐ/ĐVDV/CĐT), `Project`, `Department` — extend `AuditableEntity`
- EF `IEntityTypeConfiguration` với unique index `Code`, query filter `IsDeleted`
- DbSets trong `ApplicationDbContext` + `IApplicationDbContext` (thêm package `Microsoft.EntityFrameworkCore` vào Application)
- `Common/Models/PagedResult<T>` + `PagedRequest` (page, pageSize 1-200, search, sortBy, sortDesc)
- CQRS cho Supplier: `CreateSupplierCommand` + Validator + Handler, `UpdateSupplierCommand`, `DeleteSupplierCommand` (soft-delete qua AuditingInterceptor), `GetSupplierQuery`, `ListSuppliersQuery` (với filter Type)
- CQRS cho Project + Department: gom vào 1 file mỗi entity (`ProjectFeatures.cs`, `DepartmentFeatures.cs`) — pattern giống Supplier nhưng compact
- 3 Controller: `SuppliersController`, `ProjectsController`, `DepartmentsController` — full REST (list, get, create, update, delete)
- Migration `AddMasterData`
### Chunk B — Backend Permission system
- Domain: `MenuKeys` const (12 menu key: Dashboard, Master, Suppliers, Projects, Departments, Contracts, Forms, Reports, System, Users, Roles, Permissions), `MenuItem` (Key PK, Label, ParentKey, Order, Icon), `Permission` (RoleId, MenuKey, CanRead/Create/Update/Delete)
- EF configs + unique index `(RoleId, MenuKey)`
- DTOs: `MenuNodeDto` (tree node có resolved CRUD), `PermissionDto`, `RoleDto`, `MenuItemDto`
- `GetMyMenuTreeQuery` — resolve menu tree theo roles của user, **union OR** CRUD flags qua roles, **filter** chỉ trả về node có CanRead hoặc có child CanRead
- `ListMenuItemsQuery` + `ListPermissionsByRoleQuery` + `UpsertPermissionCommand` (guard: admin không tự giảm quyền mình)
- `ListRolesQuery`
- **Authorization handler** `MenuPermissionHandler : AuthorizationHandler<MenuPermissionRequirement>` — check Admin bypass, query DB qua roleIds
- Register 48 policy (`{menu}.{action}` cho 12 menu × 4 action) trong Program.cs
- Controllers: `MenusController` (GET /me, GET /), `RolesController`, `PermissionsController` (Authorize policy `Permissions.Read/Update`)
- Update `DbInitializer`: seed 12 menu items + default **admin có full CRUD mọi menu**
- Migration `AddPermissions`
### Chunk C — Frontend (fe-admin)
- Types: `menuKeys.ts` const + `menu.ts` (MenuNode/MenuItem/Role/Permission) + `master.ts` (Supplier/Project/Department + SupplierType const-object với erasableSyntaxOnly)
- `contexts/AuthContext.tsx`: thêm `menu` state + `loadMenu()` khi login + localStorage cache + `refreshMenu()`
- `hooks/usePermission.ts`: `can(menuKey, action)` — search tree đệ quy
- `components/PermissionGuard.tsx`: wrapper component
- `components/ui/`: Dialog (modal overlay + Escape close), Textarea, Select
- `components/DataTable.tsx`: generic table với columns config, sort (sortBy/sortDesc callback), loading, empty state, click row; `Pagination` component kèm theo
- `components/PageHeader.tsx`: title + description + actions
- `components/Layout.tsx` rewrite: render menu **động** từ AuthContext.menu (không hardcode nữa), MenuGroup collapsible cho parent có children, NavLink active highlight, icon map từ lucide-react theo field `icon`
- `lib/apiError.ts`: extract user-friendly message từ ProblemDetails
- 3 trang CRUD admin (`master/SuppliersPage.tsx`, `ProjectsPage.tsx`, `DepartmentsPage.tsx`):
- Full CRUD với Dialog form, search, sort, paging
- `<PermissionGuard>` wrap Create/Update/Delete button
- `useQuery` + `useMutation` + `invalidateQueries` TanStack
- Toast success/error
- `system/PermissionsPage.tsx`: grid ma trận role × menu × CRUD, select role dropdown → tick checkbox tự động PUT `/permissions` upsert
- `App.tsx`: thêm 4 route mới
## Bug gặp + fix
| Bug | Root cause | Fix |
|---|---|---|
| Build CS8514 in MenuPermissionHandler | EF expression tree không support switch expression | Tách `switch` ra ngoài `AnyAsync()` — switch trên Action, mỗi case gọi AnyAsync với predicate riêng |
| POST /api/suppliers lỗi với Unicode tiếng Việt qua curl command line | Windows bash command line encoding không đúng UTF-8 | Dùng `--data-binary @file.json` — API handle UTF-8 OK |
| TS1294 erasableSyntaxOnly + `enum SupplierType` | TS 6 erasableSyntaxOnly không cho enum | Dùng `const + as const + type = typeof[keyof]` pattern |
| fe-admin npm run dev báo port 8082 in use | Background task trước chưa stop hẳn | Tình trạng tạm — E2E vẫn pass qua port đã bind |
## E2E verified
- `GET /api/menus/me` với admin token → **6 root + 6 child nodes** (12 menu items) ✅
- `GET /api/roles`**12 roles**
- `POST /api/suppliers` → 201 + id ✅
- `GET /api/suppliers` → PagedResult ✅
- `PUT /api/suppliers/{id}` → 204 ✅
- `DELETE /api/suppliers/{id}` → 204, list GET sau đó không còn ✅ (soft delete OK)
- `GET /api/permissions/by-role/{adminRoleId}` → 12 permissions all true ✅
- `tsc -b` fe-admin → pass ✅
## Handoff cho session tiếp theo
**Phase 2 — Form Engine:**
- Convert 3 `.doc` files → `.docx` via PowerShell COM automation
- Parse chi tiết field specs cho 5 contract templates
- `IFormRenderer` service với OpenXml + ClosedXML
- Form builder UI cho admin upload template
- Preview + export flow
Xem [`docs/flows/form-render-flow.md`](../../flows/form-render-flow.md) + [`docs/changelog/migration-todos.md`](../migration-todos.md) section Phase 2.
**Còn thiếu trong Phase 1 (có thể làm parallel với Phase 2 nếu muốn):**
- FE: thêm trang Users management (tạo user mới, gán role)
- FE: trang Roles CRUD (create/rename/delete custom role)
- Contract entity + CRUD draft (skeleton cho Phase 3 chuẩn bị)
- Test 1 role khác Admin → verify permission guard hoạt động đúng
**Blocker:**
- ⏳ Gitea remote URL