[CLAUDE] Phase1: foundation - BE Clean Arch + Identity + JWT + 2 FE React + login E2E

Backend (.NET 10):
- Domain: BaseEntity/AuditableEntity, ContractType/Phase/ApprovalDecision enums, User/Role (Identity<Guid>), AppRoles (12 const)
- Application: IApplicationDbContext/ICurrentUser/IDateTime/IJwtTokenService, custom exceptions, ValidationBehavior (MediatR pipeline), Auth CQRS (Login/Refresh/Me), DependencyInjection
- Infrastructure: ApplicationDbContext (IdentityDbContext), AuditingInterceptor (auto audit + soft delete), DbInitializer (seed 12 role + admin), DesignTimeDbContextFactory, JwtTokenService, DateTimeService, DI
- Api: CurrentUserService, GlobalExceptionMiddleware (ProblemDetails), AuthController, Program.cs rewrite (Serilog + JWT + CORS + Swagger), appsettings + launchSettings (port 5443)
- Migration Init applied to SolutionErp_Dev LocalDB

Frontend (React 19 + Vite 8 + Tailwind 4):
- fe-admin (:8082 blue) + fe-user (:8080 emerald) - shared structure, khac menu + brand color
- Tailwind 4 via @tailwindcss/vite plugin, theme brand colors
- AuthContext (localStorage token), ProtectedRoute, Layout (sidebar + header)
- UI kit: Button/Input/Label (CVA + Tailwind)
- LoginPage voi toast error, DashboardPage/InboxPage placeholder
- Axios interceptor: auto Bearer + 401 redirect
- TanStack Query client, React Router 7, Sonner toast

Package downgrades (do .NET 10 / TS 6 compat):
- MediatR 14 -> 12.4.1 (v14 breaking changes)
- Swashbuckle 10 -> 6.9.0 (v10 khong tuong thich OpenApi 2)
- Removed Microsoft.AspNetCore.OpenApi (conflict voi Swashbuckle)

E2E verified: POST /api/auth/login qua Vite proxy ca 2 FE -> JWT + user info

Credentials seed: admin@solutionerp.local / Admin@123456

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-04-21 10:59:44 +07:00
parent 25dad7f36f
commit 702411fcc8
85 changed files with 10326 additions and 964 deletions

View File

@ -15,75 +15,70 @@
- [x] Parse 8 form → `docs/forms-spec.md`
- [x] Parse quy trình → `docs/workflow-contract.md`
- [x] Viết `docs/{CLAUDE,STATUS,PROJECT-MAP}.md`
- [ ] Viết `.gitignore`, `README.md`, `global.json`, `docker-compose.yml`
- [ ] Tạo placeholder skill folders: `contract-workflow`, `form-engine`, `permission-matrix`
- [ ] `git init` + commit đầu
- [x] Viết `.gitignore`, `README.md`, `global.json`, `docker-compose.yml`
- [x] Tạo placeholder skill folders: `contract-workflow`, `form-engine`, `permission-matrix`
- [x] `git init` + commit đầu (`25dad7f`)
- [ ] Push Gitea remote (chờ URL từ user)
## Phase 1 — Alpha Core (T2-4)
### Backend foundation
### Foundation (đã xong Session 2)
- [ ] `Domain/BaseEntity.cs` (Id, CreatedAt, UpdatedAt, CreatedBy, UpdatedBy)
- [ ] `Domain/AuditableEntity : BaseEntity` (IsDeleted, DeletedAt, DeletedBy)
- [ ] `Domain/ValueObjects/ContractCode.cs` (wrap string theo format RG-001)
- [ ] `Domain/Enums/ContractType.cs`, `ContractPhase.cs`, `ApprovalDecision.cs`
- [ ] `Application/Common/IApplicationDbContext.cs` interface
- [ ] `Application/Common/IDateTime.cs`, `ICurrentUser.cs`
- [ ] `Application/DependencyInjection.cs` — register MediatR, FluentValidation, AutoMapper
- [ ] `Infrastructure/Persistence/ApplicationDbContext.cs` : `IdentityDbContext<User, Role, Guid>`, `IApplicationDbContext`
- [ ] Configurations per entity qua `IEntityTypeConfiguration<T>`
- [ ] `Infrastructure/DependencyInjection.cs`register DbContext, Identity, services
- [ ] `Api/Program.cs` setup: services, Serilog, auth, Swagger, CORS, middleware
- [ ] `Api/Middleware/GlobalExceptionMiddleware.cs`
- [x] `Domain/Common/BaseEntity.cs` (Id Guid, CreatedAt, UpdatedAt, CreatedBy, UpdatedBy)
- [x] `Domain/Common/AuditableEntity.cs` (IsDeleted, DeletedAt, DeletedBy)
- [x] `Domain/Contracts/` Enums: `ContractType`, `ContractPhase` (9 state), `ApprovalDecision`
- [x] `Domain/Identity/User.cs` (IdentityUser<Guid> + FullName + RefreshToken + IsActive)
- [x] `Domain/Identity/Role.cs` (IdentityRole<Guid> + Description)
- [x] `Domain/Identity/AppRoles.cs` — 12 role constants
- [x] `Application/Common/Interfaces/`: IApplicationDbContext, ICurrentUser, IDateTime, IJwtTokenService
- [x] `Application/Common/Exceptions/*`
- [x] `Application/Common/Behaviors/ValidationBehavior.cs`
- [x] `Application/DependencyInjection.cs`MediatR + FluentValidation
- [x] `Infrastructure/Persistence/ApplicationDbContext.cs : IdentityDbContext`
- [x] `Infrastructure/Persistence/Interceptors/AuditingInterceptor.cs`
- [x] `Infrastructure/Persistence/DbInitializer.cs` — seed 12 role + admin
- [x] `Infrastructure/Persistence/DesignTimeDbContextFactory.cs`
- [x] `Infrastructure/Identity/{JwtSettings, JwtTokenService}.cs`
- [x] `Infrastructure/Services/DateTimeService.cs`
- [x] `Infrastructure/DependencyInjection.cs`
- [x] `Api/Services/CurrentUserService.cs`
- [x] `Api/Middleware/GlobalExceptionMiddleware.cs`
- [x] `Api/Controllers/AuthController.cs` (login, refresh, me, logout)
- [x] `Api/Program.cs` (Serilog, JWT, CORS, Swagger, middleware)
- [x] `Api/appsettings.{json, Development.json}` + `launchSettings.json` (port 5443)
- [x] Migration 1 `Init` + apply to `SolutionErp_Dev` LocalDB
- [x] FE: Vite config (Tailwind 4 + proxy + alias)
- [x] FE: `src/{index.css, lib/api.ts, lib/cn.ts, types/auth.ts}` cho 2 app
- [x] FE: `src/contexts/AuthContext.tsx`, `components/{ProtectedRoute, Layout}.tsx`
- [x] FE: `components/ui/{Button, Input, Label}.tsx`
- [x] FE: `pages/LoginPage.tsx`, `pages/DashboardPage.tsx` (admin) + `pages/InboxPage.tsx` (user)
- [x] FE: `App.tsx` với Router + AuthProvider + Toaster
- [x] FE: `main.tsx` với QueryClient (TanStack Query)
- [x] E2E verified: login qua Vite proxy cả 2 app → JWT + user info
### Auth + Identity
### Phase 1 đợt 2 — CRUD master + Permission Matrix (sắp tới)
- [ ] `Domain/Entities/User : IdentityUser<Guid>`, `Role : IdentityRole<Guid>`
- [ ] Migration 1: `Init` (Identity tables)
- [ ] `Application/Auth/Commands/LoginCommand` + handler + validator
- [ ] `Application/Auth/Commands/RefreshTokenCommand`
- [ ] `Api/Controllers/AuthController` (login, refresh, logout, me)
- [ ] JWT config: issuer, audience, key, expiry 1h + refresh 7d
- [ ] Seed admin: `admin@solutionerp.local` / `Admin@123456`
- [ ] Test login → get token → call `/me` OK
### Permission Matrix
- [ ] `Domain/Entities/MenuItem` (Key, Label, ParentKey, Order, Icon)
- [ ] `Domain/Entities/Permission` (RoleId, MenuKey, CanRead, CanCreate, CanUpdate, CanDelete)
- [ ] Seed default menu tree (based on FE screens list)
- [ ] `Application/Permissions/Queries/GetMyMenuTree` — resolve per-user
- [ ] `Api/Controllers/MenusController` + `RolesController` + `PermissionsController`
- [ ] Admin UI: Permission Matrix grid (role × menu × CRUD checkbox)
### CRUD master data
- [ ] `Domain/Entities/Supplier` (Code, Name, TaxCode, Phone, Email, Address, Type: NCC/NTP/TĐ/ĐVDV)
- [ ] `Domain/Entities/Project` (Code, Name, StartDate, EndDate, Manager)
- [ ] `Domain/Entities/Department` (Code, Name, Manager)
- [ ] CQRS + Controller + Migration cho 3 entity
- [ ] FE admin 3 trang CRUD (list, create, edit, delete confirm)
- [ ] Pagination, search, sort server-side
### Contract draft (chưa workflow — chỉ CRUD)
- [ ] `Domain/Entities/Contract` (skeleton: Id, Type, SupplierId, ProjectId, Phase=DangChon, DraftData)
- [ ] API create/update/list/delete draft
- [ ] FE admin: list contracts + filter
- [ ] FE user: "HĐ của tôi" list
### FE setup
- [ ] Install Tailwind CSS cho 2 app + config content paths
- [ ] Install shadcn/ui CLI, init 2 app
- [ ] Install: `@tanstack/react-query`, `react-router-dom`, `axios`, `lucide-react`, `sonner`
- [ ] `src/lib/api.ts` — axios instance + interceptor JWT
- [ ] `src/contexts/AuthContext.tsx` — token từ localStorage
- [ ] `src/components/PermissionGuard.tsx` + `usePermission()` hook
- [ ] Layout shell: sidebar + header + content
- [ ] Route với protected route + role guard
- [ ] Toast notifications (sonner)
- [ ] `Domain/Entities/Supplier` (Code, Name, TaxCode, Phone, Email, Address, Type enum: NCC/NTP/TĐ/ĐVDV)
- [ ] `Domain/Entities/Project` (Code, Name, StartDate, EndDate, ManagerUserId)
- [ ] `Domain/Entities/Department` (Code, Name, ManagerUserId)
- [ ] EF `IEntityTypeConfiguration<T>` cho mỗi entity
- [ ] CQRS CRUD: Create/Update/Delete/GetById/List (với paging) cho 3 entity
- [ ] `Api/Controllers/{SuppliersController, ProjectsController, DepartmentsController}`
- [ ] Migration 2: `AddMasterData`
- [ ] `Domain/Entities/MenuItem` (Key PascalCase, Label, ParentKey, Order, Icon)
- [ ] `Domain/Entities/Permission` (RoleId, MenuKey, CanRead/Create/Update/Delete)
- [ ] Seed default menu tree + permission admin có full access
- [ ] `Application/Permissions/Queries/GetMyMenuTreeQuery` — resolve per-user, cache
- [ ] `Api/Controllers/{MenusController, RolesController, PermissionsController}`
- [ ] Migration 3: `AddPermissions`
- [ ] `Domain/Entities/Contract` skeleton (Id, Type, SupplierId, ProjectId, Phase=DangChon, DraftData JSON)
- [ ] Contract CRUD draft only (không workflow Phase 3)
- [ ] FE: `<PermissionGuard menuKey="Suppliers" action="Update">` + `usePermission()` hook
- [ ] FE Admin: 3 trang CRUD Supplier/Project/Department với table + modal + search/sort
- [ ] FE Admin: Permission Matrix grid page (role × menu × CRUD checkbox)
- [ ] FE User: trang "HĐ của tôi" list + filter
- [ ] Route guard theo role admin-only
- [ ] Update `SolutionErp.slnx` nếu thêm project mới
### Exit criteria Phase 1