bab503189a0546f456996b8f3a226c312efa5de3
39 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| bab503189a |
[CLAUDE] FE-Admin: PE + HĐ toggle "Nhập tay" + 2 fields manual budget (Mig 17)
Chunk 3/5 — UI cho manual budget fallback Mig 17. Toggle checkbox "Nhập tay
(không link)" cạnh Label Ngân sách. Khi ON: hide Select Budget, show 2 input
field grid 2-col (Tên tham chiếu text + Số tiền number formatted VND).
Files sửa:
~ fe-admin/src/types/purchaseEvaluation.ts — PeDetailBundle +2 field
~ fe-admin/src/types/contracts.ts — ContractDetail +2 field
~ fe-admin/src/components/pe/PeHeaderForm.tsx — toggle + 2 input + payload
conditional (manual mode → clear budgetId, link mode → clear manual). Auto-
detect manual mode khi load existing PE có manual data + !budgetId.
~ fe-admin/src/components/pe/PeDetailTabs.tsx — Section 2 "b. Ngân sách"
fallback display khi !ev.budget + có manual data: render text "Tên · Số tiền"
+ badge "nhập tay" thay vì "(chưa link)".
~ fe-admin/src/pages/pe/PurchaseEvaluationCreatePage.tsx — refactor wrap
PeHeaderForm để DRY (auto-inherit toggle pattern, không drift). 222 LOC → 30 LOC.
~ fe-admin/src/pages/contracts/ContractCreatePage.tsx — apply same toggle
pattern cho cả NewContractForm + EditContractForm. EditForm thêm read-only
display branch khi !isDraft + có manual data.
Verify: npm run build fe-admin pass · 1922 modules · không TS error.
Next: Chunk 4 fe-user mirror (PeHeaderForm + PeDetailTabs + ContractCreatePage
+ types).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| ee0d3608e7 |
[CLAUDE] FE-Admin: PE Thao tác 2-panel workspace + Panel 1 read-only picker + Section 5 disabled
Chunk 1/3 — restructure leaf "Thao tác" (Pe_*_Create) từ page tạo header riêng
sang workspace 2-panel mirror pattern HĐ Thầu phụ ContractCreatePage:
Panel 1 (320px): list pure picker (KHÔNG inline edit/delete per Q1 user) +
sticky "+ Thêm mới" bottom button.
Panel 2 (1fr): empty state | mode=new <PeHeaderForm> | <PeDetailTabs
mode="workspace"> (5 section, Section 5 Ý kiến 4PB DISABLED
per Q5 user — nhập ở leaf "Duyệt").
Workflow Panel + Approvals + History KHÔNG render trong workspace (Q1) — chỉ
hiện ở leaf "Danh sách" + "Duyệt" giữ nguyên 3-panel hiện tại (Q3).
URL: /purchase-evaluations/workspace?type={1|2}[&id=...][&mode=new][&q=][&phase=]
Menu resolver Pe_*_Create: /purchase-evaluations/new?type=N → /workspace?type=N.
Route mới /workspace; route /new giữ tồn tại cho deep-link "Sửa header" button.
Files:
+ fe-admin/src/components/pe/PeListPanel.tsx (~180 LOC) — pure picker reuseable
+ fe-admin/src/components/pe/PeHeaderForm.tsx (~210 LOC) — extract header form
+ fe-admin/src/pages/pe/PurchaseEvaluationWorkspacePage.tsx (~120 LOC)
~ fe-admin/src/components/pe/PeDetailTabs.tsx — add mode prop + Section 5 hint
~ fe-admin/src/components/Layout.tsx — resolver Pe_*_Create map workspace
~ fe-admin/src/App.tsx — route /purchase-evaluations/workspace
Verify: npm run build pass · dotnet test 83 vẫn pass (54 Domain + 29 Infra).
fe-user mirror = Chunk 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 4380bdc075 |
[CLAUDE] App+FE-Admin: Chunk E3 — UserManager toggle CanBypassReview
Admin UI bật/tắt CanBypassReview per user (Migration 16):
- BE: UserDto thêm field CanBypassReview (List + Get queries)
- FE: User type thêm canBypassReview field
- UsersPage: column "Bypass" badge fuchsia khi true + button toggle ShieldCheck
(icon highlight fuchsia khi enabled, slate khi disabled)
- bypassMut PATCH /users/{id}/bypass-review { canBypassReview: !current }
Use case: phòng ban không có TPB hoặc TPB ủy quyền cho 1 NV cụ thể —
NV được Stage=Confirm trực tiếp (skip Stage Review), IsBypassed=true ghi audit.
Endpoint backend đã có sẵn ở Chunk E1 (commit
|
|||
| 5d94bb449a |
[CLAUDE] PE: Workflow designer admin UI + Ý kiến 4 phòng ban (P1 Session 5)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m51s
==== Task 1: PE Workflow Designer admin ====
BE (mirror Contract WorkflowAdminFeatures pattern):
- Application/PurchaseEvaluations/PeWorkflowAdminFeatures.cs ~250 LOC:
- GetPeWorkflowAdminOverviewQuery → list 2 EvaluationType (DuyetNcc / DuyetNccPhuongAn) với Active + History versions + count phiếu đang dùng
- CreatePeWorkflowDefinitionCommand + Validator: auto-increment Version per Code, deactivate Active cũ trong cùng EvaluationType (1 active per type invariant)
- DTOs: PeWorkflowStepApproverDto / PeWorkflowStepDto / PeWorkflowDefinitionDto / PeWorkflowTypeSummaryDto / PeWorkflowAdminOverviewDto
- Phase validation 1..7 (state thường, không bao gồm 99=TuChoi)
- Api/Controllers/PeWorkflowsController.cs: 2 endpoint GET /api/pe-workflows + POST. Reuse policy "Workflows.Read" + "Workflows.Create" (admin chung quyền cho cả 2 nhóm WF).
FE:
- pages/system/PeWorkflowsPage.tsx ~500 LOC mirror WorkflowsPage:
- Landing 2-card grid khi /system/pe-workflows (chưa pick type)
- TypePanel khi /system/pe-workflows/:typeCode (DuyetNcc / DuyetNccPhuongAn)
- DefinitionCard read-only view với active badge + version + steps + approvers (Role/User chip)
- PeWorkflowDesigner dialog: clone từ existing, edit Code/Name/Description, add/remove steps, +Role / +User approvers per step, save → version mới + deactivate cũ
- App.tsx route /system/pe-workflows + /system/pe-workflows/:typeCode
- Layout đã có resolver PeWf_<Code> → /system/pe-workflows/<code> từ session 3
==== Task 2: Ý kiến 4 phòng ban PE ====
Domain:
- PurchaseEvaluationDepartmentOpinion entity (AuditableEntity) — PEId + Kind + Opinion text + SignedAt + UserId + UserName denorm
- PeDepartmentKind enum (PheDuyet / Ccm / MuaHang / SmPm)
- PE entity + collection navigation DepartmentOpinions
Infrastructure:
- PurchaseEvaluationDepartmentOpinionConfiguration EF: UNIQUE(PEId, Kind) — max 1 row per phòng ban per phiếu (UPDATE in-place)
- ApplicationDbContext + IApplicationDbContext DbSet
- Migration 15 AddPurchaseEvaluationDepartmentOpinions (15 migration total / 52 DB tables)
Application:
- PeDepartmentOpinionFeatures.cs: UpsertPeDepartmentOpinionCommand (sign=true → set SignedAt+UserId, sign=false chỉ lưu text giữ chữ ký cũ) + DeletePeDepartmentOpinionCommand
- DTO bundle update: + DepartmentOpinions list trong PurchaseEvaluationDetailBundleDto
- GetPurchaseEvaluationQueryHandler load DepartmentOpinions + KindLabel resolution
API:
- POST /api/purchase-evaluations/{id}/opinions (upsert)
- DELETE /api/purchase-evaluations/{id}/opinions/{kind}
FE:
- types/purchaseEvaluation.ts: + PeDepartmentKind enum + PeDepartmentKindLabel + PeDepartmentOpinion type + departmentOpinions vào bundle
- PeDetailTabs Section "5. Ý kiến 4 phòng ban (sign-off)" — 2x2 grid OpinionBox per kind:
- Read mode (readOnly menu Duyệt): hiển thị text + chữ ký
- Edit mode: textarea + 2 button "Lưu text" / "Lưu & Ký"
- Badge "Đã ký" emerald + tên người ký + ngày khi signedAt != null
==== Task 3: User seed verify ====
Seed `SeedDemoUsersAsync` đã match đúng user list authoritative (5 PRO TPB+NV / 7 CCM TPB+NV / 1 ISO / 1 CEO) từ prior commit. DbInitializer reconcile sẽ tự sync khi API restart. Typo trong list user (soluttions / trương) đã fixed sensibly trong seed.
==== Build verify ====
- dotnet build clean (0 error)
- fe-admin TS build pass (1 module mới PeWorkflowsPage)
- fe-user TS build pass (PE detail mirror)
Total: 8 file mới (BE 4 + FE 1 + Migration 2 + 1 Domain) + 13 file modified.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 61e5d4d503 |
[CLAUDE] PE+Contract+Budget integration — link Budget vào PE/HĐ + cột So với ngân sách
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m51s
BE wire BudgetId nullable FK qua command + DTO bundle:
- Budgets.Dtos: + BudgetSummaryDto (compact header snapshot, không kèm Details — gọi /budgets/{id} riêng nếu cần đối chiếu chi tiết)
- PurchaseEvaluations.Dtos: + BudgetId? + Budget? BudgetSummaryDto vào PurchaseEvaluationDetailBundleDto
- Contracts.Dtos: + BudgetId? + Budget? BudgetSummaryDto vào ContractDetailDto
- CreatePE + UpdatePEDraft + handlers: + BudgetId? param + validate (cùng Project + Phase=DaDuyet) + persist
- CreateContract + UpdateContractDraft + handlers: + BudgetId? param + validate + persist + log diff
- GetPE + GetContract handlers: load BudgetSummary nếu có link
- CreateContractFromEvaluation: carry forward pe.BudgetId → contract.BudgetId (nếu phiếu PE đã link)
FE PE (cả 2 app):
- types/purchaseEvaluation.ts: + BudgetSummary type + budgetId/budget vào PeDetailBundle
- PurchaseEvaluationCreatePage: thêm Select 'Ngân sách' filter Phase=DaDuyet + Project match (BE-side filter qua /budgets?projectId=&phase=4). Disabled khi chưa pick Project. Edit mode preserve.
- PeDetailTabs InfoTab: hiển thị Budget link với mã + tên + tổng (clickable → /budgets?id=)
- PeDetailTabs ItemsTab: thêm cột 'NS link · Δ' chỉ hiện khi ev.budgetId. Match per-row qua key groupCode|itemCode → fetch /budgets/{id} riêng. Footer aggregate row 'Tổng' + delta indicator (xanh dưới / đỏ vượt / xám khớp). No-match cell hiện '—'.
FE Contract (cả 2 app):
- types/contracts.ts: + ContractBudgetSummary + budgetId/budget vào ContractDetail
- ContractCreatePage HeaderForm: thêm Budget Select sau FormFields, useEffect reset khi đổi project
- ContractCreatePage EditForm: Select khi isDraft / read-only link card khi !isDraft
TS build pass cả 2 app + dotnet build clean. No new migration (BudgetId? nullable FK đã có từ migration 14).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| df12fb19c8 |
[CLAUDE] FE-Admin+FE-User: Module Ngân sách (Budget) FE — 3-panel List + Create + Detail tabs + Workflow timeline
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m59s
Mirror pattern PE 3-panel cho 2 app (admin + user):
- types/budget.ts (BudgetPhase 5-state enum + label/color, BudgetListItem, BudgetDetailRow, BudgetApproval, BudgetWorkflowSummary, BudgetChangelog, BudgetDetailBundle, BudgetDetailBody)
- components/budgets/BudgetDetailTabs.tsx — flat render Section "Thông tin" Header + Section "Hạng mục" table CRUD inline (Add/Edit/Delete dialog với auto-compute ThanhTien = KL × DonGia). Export BudgetApprovalsSection + BudgetHistorySection cho Panel 3 reuse.
- components/budgets/BudgetWorkflowPanel.tsx — Panel 3 timeline activePhases + nextPhases buttons (Approve/Reject color coding) + Dialog xác nhận có comment + sub-section Approvals + Changelog.
- pages/budgets/BudgetsListPage.tsx — 3-panel [340px_1fr_360px] với search + filter Phase + filter NamNganSach. ?phase=Pending alias FE filter 2 phase ChoCCM/ChoCEO. SlaTimer per row + readOnly mode khi pendingMe.
- pages/budgets/BudgetCreatePage.tsx — form Header (TenNganSach/Năm/Dự án/Phòng ban/Mô tả). Edit mode khóa Project+Department.
- App.tsx routes /budgets, /budgets/new, /budgets/:id cả 2 app
- Layout.tsx menu resolver Bg_List → /budgets, Bg_Create → /budgets/new, Bg_Pending → /budgets?phase=Pending. NavLink active dùng queryMatches helper (gotcha #34 — không conflict Bg_List vs Bg_Pending cùng pathname).
TS build: cả fe-admin + fe-user pass clean (1918 + 1901 modules).
BE: dùng 11 endpoint Budgets từ migration 14 (Phase 7 BE đã deploy commit
|
|||
| eda9e84187 |
[CLAUDE] PE: readOnly mode cho menu 'Duyet' (pendingMe=1)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m50s
User request: 'Menu duyet cua NCC -> chi de duyet thoi nhe khong co cac action them sua j vao'. Them readOnly prop vao PeDetailTabs — propagate xuong 3 sub-component (InfoTab / SuppliersTab / ItemsTab) + SupplierAttachmentsCell. URL pendingMe=1 (menu 'Duyet') → set readOnly=true. Hide khi readOnly: - Header: [Sua header] [Xoa] button - SuppliersTab: [+ Them NCC] button + action column (Check winner/Pencil edit/Trash delete per row) - ItemsTab: [+ Them hang muc] button + action column (Pencil/Trash per row) + click cell bao gia popup - SupplierAttachmentsCell: [+ Them file] button + Trash delete icon (giu download tren file name) - InfoTab: [Tao HD tu phieu] button Giu: - Moi thong tin doc-only - Download file dinh kem (click ten file) - Panel 3: Quy trinh + transition button (de action duyet phase) - [Dong] button - Chip 'che do duyet' gan phase badge de user biet mode Mirror fe-admin + fe-user. |
|||
| a737196b21 |
[CLAUDE] FE-Admin+FE-User: PurchaseEvaluation pages (3-panel list + tabs detail)
Types + pages + components cho module Duyệt NCC ở cả 2 FE (copy-share). Pages: - PurchaseEvaluationsListPage: 3-panel lg:grid-cols-[340px_1fr_360px] * Panel 1: list filter theo type/phase/search + pendingMe inbox mode * Panel 2: PeDetailTabs (Thông tin/NCC/Hạng mục/Duyệt/Lịch sử) * Panel 3: PeWorkflowPanel với timeline + nextPhase buttons * Mobile fallback fullpage /purchase-evaluations/:id - PurchaseEvaluationCreatePage: form create/edit header (Type / Tên gói thầu / Dự án / Địa điểm / Mô tả / PaymentTerms JSON). Suppliers+Details+Quotes thêm sau khi save ở Detail tabs. Components: - PeDetailTabs: 5 tab + dialogs (AddSupplier/EditSupplier/DetailDialog/ QuoteDialog) + matrix N NCC × M hạng mục clickable cells + select winner - PeWorkflowPanel: policy timeline từ BE workflow.activePhases + transition confirmation dialog với comment Routes (cả 2 app): - /purchase-evaluations (+ ?type=1|2&pendingMe=1&id=...) - /purchase-evaluations/new (+ ?type / ?id để edit) - /purchase-evaluations/:id (mobile fullpage) Menu resolver: - Pe_<Code>_List → /purchase-evaluations?type=N - Pe_<Code>_Create → /purchase-evaluations/new?type=N - Pe_<Code>_Pending → /purchase-evaluations?type=N&pendingMe=1 - PeWf_<Code> (fe-admin only) → /system/pe-workflows/<code> Skip MVP: PE Workflow admin designer UI, PE Attachments. TS build pass cả 2 app. |
|||
| 072ad6d014 |
[CLAUDE] App+Api+FE-Admin: RolesPage CRUD (/system/roles)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m58s
User feedback: /system/roles trỏ tới placeholder "chưa được build" — build trang quản lý 12 role mặc định + custom role admin tự thêm. ## BE — PermissionFeatures.cs 3 command mới: - CreateRoleCommand — Name regex `^[A-Za-z][A-Za-z0-9_]*$` (chỉ chữ/số/ underscore, bắt đầu chữ), throw ConflictException nếu code đã tồn tại - UpdateRoleCommand — CHỈ update ShortName + Description. KHÔNG đổi Name (Identity FK trong UserRoles + WorkflowStepApprover.AssignmentValue + [Authorize(Roles="...")] attr — đổi = data corruption widespread) - DeleteRoleCommand — block 2 trường hợp: * Role thuộc AppRoles.All hardcoded (workflow guard reference) * Còn user assigned (UserManager.GetUsersInRoleAsync count > 0) ValidationException reference fully-qualified để tránh ambiguous với FluentValidation.ValidationException. ## BE — RolesController 3 endpoint mới (POST/PUT/DELETE) — Authorize Admin role. ## FE — RolesPage Table list 12 + custom roles với 5 column (Mã code / Mã viết tắt / Tên đầy đủ / Loại badge / Ngày tạo) + actions Edit/Delete: - Edit dialog: chỉ ShortName + Description editable, Name disabled với hint "Không đổi được sau khi tạo" - Delete: block với toast nếu role mặc định (HARDCODED_ROLES set check client-side trước khi gọi BE — UX faster, BE vẫn double-check) - Create dialog: 3 field Name (regex pattern HTML5) + ShortName + Description - Banner amber warning về Mã code FK constraint - Loại badge: Mặc định (slate) vs Tùy chỉnh (brand) ## FE — App.tsx + import RolesPage + route /system/roles → RolesPage. ## Build - BE: dotnet build pass (0 error) - fe-admin: tsc + vite pass (13.88s) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| ae59cfeb5d |
[CLAUDE] FE-Admin: UsersPage dept/position field + RoleShortName tiếng Việt
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m27s
## types/users.ts
- User type + departmentId/departmentName/position
- CreateUserInput + departmentId/position
- RoleShortName map (Mã viết tắt VN per role): QTV/NV.PB/TPB/PM/PRO/CCM/
FIN/ACT/EQU/BOD/NĐUQ/HRA
- RoleLabel map (Tên đầy đủ VN per role)
- roleDisplayName(role) → "BOD — Ban Giám đốc" combined helper
## types/menu.ts
Role type + shortName field (mirror BE RoleDto).
## UsersPage redesign
- Column "Phòng ban" (departmentName + position 2 dòng)
- Column "Vai trò" hiển thị badge ShortName ("BOD", "CCM", "PM"), tooltip
hover full label
- Column actions thêm "Sửa thông tin" (Pencil icon) — dialog edit dept/
position/active state
- Create dialog 2-col grid: Email | Họ tên / Phòng ban (dropdown) | Chức vụ /
Password (col-span-2). Roles checkboxes hiển thị "ShortName — full label"
- Edit dialog mới — sửa fullName + dept + position + isActive
- Roles dialog title kèm dept name (context cho user reviewer)
- toggleActive mutation include departmentId/position để không reset
## PermissionsPage
Panel 1 role list:
- 2-line per row: ShortName (semibold) + Description (truncate small)
- Tooltip = description đầy đủ
- Active row vẫn ring-brand-200
Panel 2 header badge: ShortName thay name code English.
## Build
fe-admin: tsc + vite pass (12.17s lần 1, 671ms lần 2)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 39031ca33c |
[CLAUDE] FE: ContractDetailsPreview cho create mode — table headers + disabled add
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m36s
User feedback: thay vì placeholder dashed nhỏ "Chi tiết sẽ hiện sau khi
tạo Header", show structure thật của Chi tiết section ngay từ đầu nhưng
disabled. User thấy trước layout columns + button add → trải nghiệm
liên tục, không bất ngờ khi switch sang edit mode.
## Component mới: ContractDetailsPreview
- Section title "Chi tiết ({TypeLabel})" + amber pill "🔒 Cần tạo Header trước"
- Table opacity-60 với:
- thead column headers per type (sync với HEADERS_BY_TYPE config)
- tbody empty state: Lock icon + "Tạo Header xong sẽ thêm được hạng mục"
- Disabled "+ Thêm dòng" button (cursor-not-allowed, slate-400 text)
## HEADERS_BY_TYPE config
7 type × column headers — duplicate nhỏ với ContractDetailsTab.tsx renderers
(acceptable: chỉ là labels visual, không logic).
## Reactive theo type
User đổi dropdown "Loại HĐ" → preview headers update tương ứng (state-driven).
## Build
- fe-user: tsc + vite pass (586ms)
- fe-admin: tsc + vite pass (709ms)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 16e24ed962 |
[CLAUDE] FE: Admin CatalogsPage CRUD + Details form datalist autocomplete
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m36s
## fe-admin
### CatalogsPage.tsx (mới)
1 page generic CRUD cho 4 master catalogs (units / materials / services /
work-items). Dispatch theo URL param :kind:
- /master/catalogs (redirect units)
- /master/catalogs/:kind (units|materials|services|work-items)
Sub-tabs ở top chuyển nhanh giữa 4 kind. Mỗi kind có FIELDS_BY_TYPE config
riêng (3-7 field). Form dialog nested với input/textarea/checkbox theo type.
### Layout.tsx + App.tsx
- resolvePath thêm 4 CatalogXxx → /master/catalogs/{kind}
- Route /master/catalogs/:kind → CatalogsPage
## fe-user + fe-admin (mirror)
### ContractDetailsTab.tsx
Datalist autocomplete cho Add row form:
- Fetch 4 catalogs via TanStack Query (cache shared key 'catalogs')
- Mỗi field config thêm optional `datalist` + `datalistField` ('code'|'name')
- HTML5 <datalist> render options theo type:
- ThauPhu: hangMuc → work-items, donViTinh → units
- GiaoKhoan: maCongViec/tenCongViec → work-items, donViTinh → units
- NhaCungCap/MuaBan: maSP/tenSP → materials, donViTinh → units
- DichVu: maDichVu/tenDichVu → services, donViTinh → units
- NguyenTacNcc: tenSP → materials, donViTinh → units
- NguyenTacDv: tenDichVu → services, donViTinh → units
Smart-fill (handleFieldChange):
- User pick value khớp catalog → autofill sibling fields cùng catalog:
- Field name 'maXxx' → fill code; 'tenXxx'/'hangMuc' → fill name
- donViTinh nếu chưa có giá trị → fill từ defaultUnit của catalog item
Vẫn cho user gõ tự do (free text) — datalist chỉ là suggestion.
## Build
- fe-user: tsc + vite pass (5.69s)
- fe-admin: tsc + vite pass (633ms + 15.84s lúc test CatalogsPage lần đầu)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 7f26ff9d66 |
[CLAUDE] FE-User+FE-Admin: 2 button luôn hiện, mờ + disabled khi != DangSoanThao
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m45s
User feedback: hiển thị Edit + Xóa cho mọi row (kể cả Phase khác), nhưng
mờ và disabled khi không thao tác được — để user biết button TỒN TẠI mà
không bị bất ngờ phải hover row đúng phase mới thấy.
## Thay đổi
- Bỏ conditional render `{phase === DangSoanThao && ...}`
- Thêm canMutate = c.phase === DangSoanThao biến + className conditional:
- canMutate=true: text-slate-500 + hover brand/red + clickable
- canMutate=false: text-slate-300 + cursor-not-allowed + disabled
- Default opacity-60 (luôn visible nhẹ), group-hover:opacity-100 (rõ
khi hover)
- title tooltip thay đổi theo state — hint user lý do disable
- onClick guard early return nếu !canMutate (defense in depth)
Build: tsc + vite pass cả 2 app
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 501b4de418 |
[CLAUDE] FE-User+FE-Admin: Edit + Xóa, chỉ hiện khi DangSoanThao
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m45s
User feedback: 2 button trên row Panel 1 phải là Edit + Xóa, và CHỈ hoạt động trong trạng thái nhập liệu/điều chỉnh (Phase = DangSoanThao). ## Thay đổi - ✏ Pencil icon thay ExternalLink — hành động Edit (select Panel 2 form) - 🗑 Trash2 — Xóa (giữ nguyên) - Cả 2 button bọc trong `c.phase === DangSoanThao` conditional → Phase khác (DangGopY, DangDamPhan, ...) → ẩn cả 2 - Lý do: BE chỉ cho update + delete khi Phase=DangSoanThao (UpdateContractDraftCommand + DeleteContractCommand throw Conflict- Exception nếu khác) ## UX - Phase=DangSoanThao: hover row → 2 button fade in - Phase khác: chỉ row click select Panel 2 (form sẽ render read-only + banner amber "HĐ đã chuyển khỏi Đang soạn thảo") Build: tsc + vite pass cả 2 app Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| ec0c983e8e |
[CLAUDE] FE-User+FE-Admin: action buttons (Mở chi tiết + Xóa) trên row Panel 1 Thao tác
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m35s
User feedback: thêm nút edit/action ở mỗi row trong list Panel 1 trang
Thao tác. Hiện absolute positioned ở góc phải-trên row, opacity-0 → 100
khi hover (group-hover). Sibling không nested để click không trigger
row select propagation.
## 2 button per row
- ⤴ ExternalLink → navigate /contracts/{id} (fullpage detail với
Workflow + History, khác Panel 2 chỉ có Edit form)
- 🗑 Trash2 → confirm() + DELETE /contracts/{id} (soft delete,
blocked sau DangInKy ở BE). Nếu xóa HĐ đang select → clear ?id=
## Implementation details
- pr-16 cho row button để chừa khoảng cho action group
- group-hover:opacity-100 transition (smooth fade in)
- Mutation invalidate ['my-contracts'] sau xóa thành công
- Toast success + getErrorMessage cho fail case (vd xóa HĐ đã qua DangInKy)
Build: tsc + vite pass cả 2 app (fe-user 515ms, fe-admin 937ms)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 8c4b4da951 |
[CLAUDE] FE-User+FE-Admin: 2-panel layout cho Thao tác (Ct_*_Create)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m36s
Trang /contracts/new?type=X (menu "Thao tác") redesign từ single form
→ 2-panel: Panel 1 list HĐ theo type | Panel 2 Header form + Chi tiết.
## Layout
Panel 1 (320px) — flex column 3 vùng:
- Top: Search box (filter mã/tên/NCC client-side)
- Middle: List HĐ theo type (scroll, click row chọn)
- Bottom: + Thêm mới button (sticky, ring-brand khi active mode=new)
Panel 2 (flex) — 3 trạng thái theo URL:
- Empty state — chưa chọn HĐ và chưa bấm + Thêm mới
- ContractHeaderForm (mode=new) — form trống, sau Tạo HĐ draft
→ URL update ?id=newId chuyển edit mode
- ContractEditForm (id=abc) — form populated từ /contracts/{id}, +
section Chi tiết bên dưới (ContractDetailsTab reuse)
## URL state
- ?type=X → empty
- ?type=X&mode=new → form trống
- ?type=X&id=abc → edit form + Chi tiết
- ?type=X&q=keyword → search filter Panel 1
## Edit constraints
ContractEditForm respect UpdateContractDraftCommand limits:
- Editable khi Phase=DangSoanThao: Tên HĐ, Giá trị, Template, Nội dung
- Read-only luôn: Loại HĐ, NCC, Dự án, Bypass CCM (không đổi sau create
qua BE command hiện tại)
- Khi Phase != DangSoanThao: warning amber + tất cả input disabled,
nhưng Chi tiết section vẫn render để user xem (ContractDetailsTab tự
disable add/delete khi không phải draft)
## Components
ContractCreatePage.tsx (rewrite) — page entry
ContractHeaderForm — create mode (full fields editable)
ContractEditForm — edit mode (limited fields + Chi tiết section)
FormFields helper — shared form layout cho create
## Build verify
- fe-user: tsc + vite pass (374ms)
- fe-admin: tsc + vite pass (987ms)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| b75448e711 |
[CLAUDE] FE-User+FE-Admin: 3-panel layout cho danh sách HĐ
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m50s
Redesign trang Danh sách HĐ (Ct_*_List menu fe-user + /contracts admin)
thành 3-panel: List | Detail content | Workflow + lịch sử duyệt. Selected
HĐ giữ qua URL ?id= (bookmarkable + back/forward navigation work).
## Components mới (reuse cho cả 3-panel embedded + fullpage detail)
### fe-user/src/components/contracts/
- ContractDetailContent.tsx — Panel 2 body: header sticky (title + phase
+ actions Yêu cầu sửa/Duyệt) + Info section + Comments thread + form
thêm góp ý + Attachments. Transition Dialog inline. Prop optional
onBack — render arrow back button (fullpage) hoặc skip (embedded).
- WorkflowHistoryPanel.tsx — Panel 3: WorkflowSummaryCard (timeline
policy current+next) + Lịch sử duyệt (approvals: phase from→to + actor
+ timestamp + comment).
### fe-admin/src/components/contracts/
- ContractDetailContent.tsx — variant admin có thêm Phòng ban + Bypass
CCM trong Info section. Invalidate ['contracts'] khi transition.
- WorkflowHistoryPanel.tsx — identical fe-user.
## Trang refactored
### fe-user
- MyContractsPage.tsx — bỏ DataTable, dùng 3-panel grid
lg:grid-cols-[320px_1fr_360px] h-[calc(100vh-4rem)]:
Panel 1: search box + list compact (mã/tên/NCC/phase/SLA/giá), click
update ?id= active highlight ring-brand
Panel 2: detail content embedded
Panel 3: workflow + history
Mobile (<lg): chỉ Panel 1 visible, click row navigate fullpage
/contracts/:id (UX khả dụng, không nhồi 3 panel màn hình hẹp).
URL state: ?type=X (filter loại) + ?id= (selected) + ?q= (search).
- ContractDetailPage.tsx — slim version dùng ContractDetailContent +
WorkflowHistoryPanel, giữ deep link /contracts/:id work.
### fe-admin
- ContractsListPage.tsx — 3-panel + filter phase + pagination compact
trong Panel 1 footer. URL state: ?type, ?pendingMe, ?id, ?q, ?phase,
?page (full bookmarkable). Title hiển thị loại HĐ + count badge.
- ContractDetailPage.tsx — slim version giống fe-user.
## Build verified
- fe-user: tsc -b + vite build pass (1888 modules, 1.08MB JS)
- fe-admin: tsc -b + vite build pass (1903 modules, 1.15MB JS)
Note: npm install resolved @microsoft/signalr 8.0.7 → 8.0.17 (within
^8.0.7 caret), reverted package.json + lock changes do bump không phải
scope task này. Dev tiếp theo run npm install sẽ tự re-resolve.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 91b2da147f |
[CLAUDE] FE-Admin: PermissionsPage 3-panel layout
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m33s
Redesign theo yêu cầu user: 3 panel vertical đồng thời trên cùng 1 màn hình (không modal/dialog popup). Layout grid lg:grid-cols-[280px_1fr_300px]: Panel 1 — Vai trò (trái, 280px): Danh sách roles click-to-select với active highlight (brand-50 bg + ring-brand-200 + check icon). Đếm số roles ở header. Panel 2 — Quyền theo menu (giữa, flex): Tìm menu inline header + sticky thead. Click vai trò → lọc menu instant. Column toggle header (tick toàn cột) + per-cell checkbox. Hover brand-tinted. Menu key hiện mono nhỏ dưới label. Panel 3 — Tổng quan (phải, 300px): Vai trò đang chọn + số quyền (progress bar brand) + chi tiết từng CRUD (Xem/Tạo/Sửa/Xóa) với badge color-coded (slate/emerald/amber/ red) + count "X / Y menus" + tip helper cuối. Bỏ dialog select + 3-col grid filter ở đầu (thay bằng 3 panel), giữ logic mutation/toggle/column nguyên. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| f216169039 |
[CLAUDE] FE-Admin+Domain+Infra+App: Workflows tab → sidebar menu items
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m37s
User request: 7 tab trong /system/workflows thành menu items riêng. Domain: - MenuKeys.WorkflowTypeLeaf(code) helper — `Wf_<TypeCode>` pattern Infrastructure (DbInitializer): - Seed 7 leaves dưới Workflows group (order 95..101), label matches ContractType (HĐ Thầu phụ / Giao khoán / NCC / Dịch vụ / Mua bán / Nguyên tắc NCC / Nguyên tắc Dịch vụ). Idempotent. Application (GetMyMenuTreeQuery): - Generalized inherit-perm logic: descendants of Contracts AND Workflows inherit parent CanRead flag. Single Workflows.Read grant → all 7 Wf_* leaves visible; no per-leaf permission rows needed. FE Layout (admin): - resolvePath: Wf_<Code> → /system/workflows/<code>. Ct_* still hidden on admin side. FE App.tsx: - New route /system/workflows/:typeCode? FE WorkflowsPage: - Removed horizontal tab bar; type selection now comes từ URL param. - Landing view (no param): 3-col grid card per type với active version badge — so admin có visual overview khi click top-level Workflows group without selecting a type. - TYPE_CODE_TO_INT map drives URL→int conversion. Result: click `Quy trình HĐ > HĐ Mua bán` trong sidebar → opens /system/workflows/MuaBan directly với designer scoped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 355bbe3a61 |
[CLAUDE] FE-Admin: Dialog size xl → lg (fix TS2322 CI build)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
|
|||
| e7e5f2d066 |
[CLAUDE] Domain+Infra+App+Api+FE-Admin: versioned workflow per ContractType
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Failing after 1m32s
User yêu cầu: mỗi loại HĐ có quy trình riêng với admin add roles + users vào từng bước. Khi tạo version mới → HĐ tương lai chạy theo, HĐ cũ giữ version cũ. Domain: - WorkflowDefinition (Code + Version + ContractType + IsActive + Steps) - WorkflowStep (Order + Phase + Name + SlaDays + Approvers) - WorkflowStepApprover (Kind: Role/User + AssignmentValue) - Contract.WorkflowDefinitionId — pinned at creation - WorkflowPolicyRegistry.FromDefinition() — build runtime policy từ DB Infrastructure: - EF config + migration AddVersionedWorkflows (3 table mới) - DbInitializer.SeedWorkflowDefinitionsAsync: v01 per 7 ContractType, steps sinh từ hardcoded WorkflowPolicies (Role approvers). - ContractWorkflowService.TransitionAsync: load pinned WorkflowDefinition → FromDefinition(), fallback cho HĐ cũ không có pin. Application: - CreateContractCommand pin WorkflowDefinitionId = active version cho type - ContractFeatures.Get(id): load pinned def cho workflow summary - WorkflowAdminFeatures: GetWorkflowAdminOverviewQuery (7 types + active + history + ContractsUsingCount), CreateWorkflowDefinitionCommand (validate payload, auto-increment version, deactivate old). Api: - GET /api/workflows trả overview - POST /api/workflows tạo version mới (deactivate old) FE /system/workflows: - Tabs per 7 ContractType, mỗi tab hiện active version + lịch sử - DefinitionCard: steps với badge role/user + SLA + archived indicator hiện "N HĐ còn chạy" cho version cũ - WorkflowDesigner modal: form code/name/desc + danh sách steps (phase/name/SLA) + approvers (+ Role hoặc + User). Drop step ok. Clone từ version hiện tại để tạo v02 có điểm start sensible. - Amber banner: HĐ cũ không bị ảnh hưởng khi tạo version mới Invariants được giữ: - Unique (Code, Version) index - Chỉ 1 version IsActive per ContractType tại 1 thời điểm - Set default sẽ auto xóa override → respect legacy override table - Role-kind approvers drive transition guards; User-kind fallback DeptManager role cho v1 (user-level targeting = iteration 2) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 5e0f3801a1 |
[CLAUDE] Move nested-type menu → fe-user; Admin workflow config page
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m41s
User clarified: menu loại HĐ 3-level (Danh sách/Thao tác/Duyệt) thuộc
fe-user. Admin có page riêng để config quy trình per loại HĐ.
fe-admin Layout:
- filterForAdmin() drops Ct_* entries (hide nested type menu).
- Admin sidebar giờ về lại đơn giản: Dashboard / Master / Hợp đồng
(leaf) / Forms / Reports / System.
fe-user Layout:
- Dynamic menu tree từ /menus/me (thay fixed USER_MENU hardcoded).
- Recursive MenuNodeRenderer (top-level expanded, nested collapsed).
- resolvePath user-specific: Ct_*_List → /my-contracts?type=X,
Ct_*_Create → /contracts/new?type=X, Ct_*_Pending → /inbox?type=X.
- filterForUser drops admin-only entries (Master/System/Forms/Reports).
- Static USER_FIXED_TOP prepends "Hộp thư" leaf → /inbox.
- MyContractsPage + InboxPage đọc ?type=X param, filter client-side.
Workflow config (Admin side):
- Domain: WorkflowTypeAssignment entity (ContractType → PolicyName
override). Registry.ForContractWithOverrides() prefer DB override
else default.
- Infrastructure: EF config + migration AddWorkflowTypeAssignments,
unique index trên ContractType. ContractWorkflowService load
overrides dict mỗi transition. ContractFeatures load overrides khi
build WorkflowSummaryDto.
- Application: GetWorkflowAdminOverviewQuery returns 7 types × current
policy + available policies. SetWorkflowAssignmentCommand validate
policy name tồn tại; nếu = default thì delete override (no stale row).
- Api: GET /api/workflows + PUT /api/workflows/{contractType}
với policy "Workflows.Read" + "Workflows.Update".
- Menu: new key `Workflows` dưới System, label "Quy trình HĐ".
- FE /system/workflows: 7 card per type, dropdown Standard/SkipCcm +
'Đã override' badge khi khác default, phase sequence timeline,
explanation banner ở top. Iteration 2 note: admin-authored custom
policies.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 48e91fe7ca |
[CLAUDE] Domain+Infra+App+FE-Admin: per-ContractType nested sidebar menu
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
User request: mỗi loại HĐ có menu riêng với 3 action Danh sách / Thao tác / Duyệt. Sidebar giờ 3-level under "Hợp đồng": Hợp đồng (group, expandable) ├── HĐ Thầu phụ (sub-group) │ ├── Danh sách → /contracts?type=1 │ ├── Thao tác → /contracts/new?type=1 │ └── Duyệt → /contracts?type=1&pendingMe=1 ├── HĐ Giao khoán (sub-group) ├── HĐ NCC / Dịch vụ / Mua bán / Nguyên tắc NCC / Nguyên tắc DV └── ... (7 types × 4 = 28 new menu items) BE: - MenuKeys.cs: ContractTypeCodes array + helpers ContractTypeGroup/ List/Create/Pending → key format Ct_<TypeCode>[_<Action>] - DbInitializer.SeedMenuTreeAsync: loop seeds 28 entries under Contracts - GetMyMenuTreeQuery.BuildChildren: descendants of `Contracts` inherit parent permission (avoid adding 28 rows to Permissions table per role) FE: - Layout.tsx recursive: MenuNodeRenderer dispatches group vs leaf by depth; nested groups collapsed by default (top-level expanded). Deeper levels get smaller padding/text + left border guide. - Pattern-based resolvePath: Ct_<Type>_<Action> → URL with query. - Contract type code → int map (matches Domain ContractType enum). - ContractsListPage reads ?type + ?pendingMe, filters client-side. Header title + description reflect active filter. "← Tất cả loại" quick-reset button. - ContractCreatePage new cho admin (copy từ fe-user), pre-select type từ ?type URL param. - App.tsx route /contracts/new → ContractCreatePage. Pure navigation UX; no new permissions needed. Admin + any role with Contracts.Read see full menu; leaves click-through to filtered views. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 6197c841bb |
[CLAUDE] App+Infra+FE-Admin: seed master data + MyDashboard widgets
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
Task 1 — Seed master data unblock UAT/demo: - DbInitializer.SeedDepartmentsAsync: 9 departments từ QT-TP-NCC.docx (PM/QS/CCM/PRO/FIN/ACT/EQU/HRA/BOD) — reference data không phải demo. - DbInitializer.SeedDemoMasterDataAsync: 5 demo suppliers (NCC VLXD, NTP Xây dựng, TĐ Hoàng Nam, DV Clean, CĐT Vingroup — covers cả 5 SupplierType) + 3 demo projects (FLOCK01, SkyGarden, Industrial). Chỉ seed nếu tables empty — respect admin's real data khi họ add. Task 2 — Roles CRUD đã có sẵn trong UsersPage (Shield icon button mở dialog gán 12 roles từ AppRoles.cs). Skip. Task 3 — MyDashboard role-specific widgets: - GetMyDashboardQuery (Reports): returns DraftsInProgress (tôi là Drafter + phase soạn thảo), PendingMyApproval (phase eligible role tôi + không phải tôi drafter), DueSoon 24h, Overdue, DraftsTotalValue. - Endpoint GET /api/reports/my-dashboard. - FE MyDashboardRow ở đầu DashboardPage: 4 card hover → navigate. Admin ẩn row nếu tất cả = 0 (ERP noise reduction). 'Đang soạn thảo' + 'Chờ tôi duyệt' clickable → /contracts?filter=... (filter param để wire lần sau; row hiện chưa implement). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| cae4d84830 |
[CLAUDE] Domain+Infra+App+FE: dynamic workflow policy per ContractType
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m42s
Đọc QT-TP-NCC.docx: quy trình 9 bước chỉ áp dụng cho Thầu phụ/NCC/Tổ đội.
Dịch vụ/Mua bán/Nguyên tắc bypass CCM. Thay hardcoded dict bằng policy
registry.
Domain — WorkflowPolicy.cs:
- Record WorkflowPolicy { Name, Description, Transitions, PhaseSla,
ActivePhases } — pure data, testable.
- WorkflowPolicies.Standard: 9-phase full (Thầu phụ/Giao khoán/NCC)
- WorkflowPolicies.SkipCcm: 7-phase (Dịch vụ/Mua bán/Nguyên tắc)
- WorkflowPolicyRegistry.For(type) map ContractType → policy
- WorkflowPolicyRegistry.ForContract(c) override nếu BypassProcurement
AndCCM=true (instance-level escape hatch)
Infrastructure — ContractWorkflowService:
- Xóa hardcoded Transitions/PhaseSla dicts → load từ policy.ForContract
- TransitionAsync: validate qua policy.Transitions thay vì dict local
- Error message include policy.Name để debug dễ hơn
- GetPhaseSla trả SLA từ Standard policy (fallback — SLA hiện tại giống
nhau giữa 2 policy)
Application — ContractDetailDto:
- Field mới `Workflow: WorkflowSummaryDto { PolicyName, Description,
ActivePhases, NextPhases }` — FE dùng để render nút chuyển phase
dynamic + timeline card.
- BuildWorkflowSummary helper trong ContractFeatures.
FE (both apps):
- Type WorkflowSummary + ContractDetail.workflow
- ContractDetailPage xóa hardcoded NEXT_PHASES — dùng
c.workflow.nextPhases từ BE (single source of truth)
- WorkflowSummaryCard: timeline của ActivePhases với check/current/
future states + policy name/description ở header
- Card hiển thị trong sidebar, phía trên "Lịch sử duyệt"
Docs:
- gotchas.md #21 marked RESOLVED (NEXT_PHASES sync không còn cần)
Foundation: sau này admin có thể edit policy qua UI khi chuyển sang DB-
backed policy — nhưng API contract (WorkflowSummaryDto) đã stable.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| e45909712b |
[CLAUDE] App+Infra+FE-Admin: DynamicForm + .doc/.xls auto-convert on upload
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Failing after 1m17s
Tier 3 iter2 — form builder UI dùng FieldSpec thay raw JSON textarea.
FE:
- DynamicForm component — parse FieldSpec JSON (record of FieldDef với
label/type/required/placeholder/hint/options) và render inputs
dynamic theo type: text/textarea/number/date/currency/select.
- FormsPage render dialog thêm toggle Form ↔ JSON (segmented control).
Mặc định Form mode khi template có FieldSpec, JSON mode khi không.
Khi mở dialog cho row khác, reset formValues + chọn đúng default mode.
- parseFieldSpec helper trả { spec, error } — UI báo lỗi nếu JSON
không parse được, fallback JSON textarea.
BE — generalize converter thành IDocumentConverter:
- IPdfConverter → IDocumentConverter (ConvertAsync(bytes, src, tgt, ct))
— đủ gánh cả pdf, docx, xlsx targets.
- LibreOfficeDocumentConverter — 1 shell-out pattern cho mọi conversion
(docx→pdf, doc→docx, xls→xlsx, xlsx→pdf), target arg truyền vào
--convert-to.
- ExportTemplatePdfCommand update dùng "pdf" target.
Auto-convert .doc/.xls trên upload:
- Validator accept thêm .doc/.xls (thêm note "sẽ tự convert").
- UploadContractTemplateCommandHandler: nếu ext là doc/xls → read stream
→ converter.ConvertAsync → lưu file .docx/.xlsx thay vì format gốc.
File rendering pipeline (DocxRenderer/XlsxRenderer) chỉ support docx/
xlsx — convert đảm bảo consistent.
- Display FileName preserve original name nhưng đổi extension.
Unblock 3 file .doc legacy template — admin giờ upload .doc bình thường,
system tự convert.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 6bbd894d96 |
[CLAUDE] App+Infra+Api+FE-Admin: PDF export (LibreOffice headless)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m33s
Pipeline: template.docx → FormRenderer fill placeholders → LibreOffice
soffice --headless --convert-to pdf → PDF byte[] → File() stream to
browser.
Clean-arch split:
- Application: IPdfConverter abstraction (swap to QuestPDF/Aspose later
without touching caller).
- Infrastructure: LibreOfficePdfConverter — shells out to soffice.exe
path from config (Pdf:SofficePath, default
`C:\Program Files\LibreOffice\program\soffice.exe` on Windows).
Per-request temp workDir để tránh filename collision + -env:
UserInstallation isolate mỗi conversion (chống "soffice already
running" khi concurrent). Timeout 60s (configurable). Best-effort
cleanup. Kill entire process tree nếu timeout.
- Application: ExportTemplatePdfCommand — reuses existing FormRenderer
+ pipes bytes through IPdfConverter. Same data dict signature as
Render để UI code share.
- Api: POST /api/forms/templates/{id}/export-pdf (same JSON body as
/render, returns PDF stream).
FE:
- useExport hook chung cho 2 endpoints (DRY render + export-pdf mutations)
- Render dialog thêm nút "Tải PDF" (outline variant) cạnh "Tải file gốc".
Disabled khi mutation khác đang chạy.
- Hướng dẫn dialog nâng cấp: "file gốc để edit Word/Excel, PDF để
in/gửi không chỉnh sửa được".
Ops: scripts/install-libreoffice.ps1 — silent MSI install 25.8.6 cho
VPS (đã chạy trên prod).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 166d26c1d8 |
[CLAUDE] App+Api+FE-Admin: Form template builder (upload + edit + delete)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m44s
Admin giờ có thể quản lý template HĐ hoàn toàn qua UI — không cần dev
đụng vào file system hay seed data.
BE (FormFeatures.cs + FormsController.cs):
- UploadContractTemplateCommand (multipart): validate FormCode
(regex [A-Za-z0-9._-]+, unique), file <= 10MB, ext .docx/.xlsx,
FieldSpec phải là JSON hợp lệ hoặc null. Ghi file vào
wwwroot/templates/{formCode}_{guid:N}.{ext} để tránh collision
+ path traversal.
- UpdateContractTemplateCommand: sửa metadata + FieldSpec + IsActive
(không đụng file — chỉ DB).
- DeleteContractTemplateCommand: soft delete qua IsActive=false
(historical contracts ref template này vẫn resolve).
- Endpoints: POST /api/forms/templates (multipart),
PUT /api/forms/templates/{id}, DELETE /api/forms/templates/{id}.
RequestSizeLimit 12MB (validator caps 10MB).
FE (FormsPage.tsx admin):
- PageHeader action button "Upload template" mở dialog mới
- Row actions: Download (render existing), Pencil (edit), Trash (xóa
confirm) thay vì chỉ có 1 nút Render — row hover reveals clearly
- Upload dialog: file picker với file: pseudo-element brand styled,
FormCode (required, font-mono), Tên, Loại HĐ select, Mô tả,
FieldSpec JSON textarea với placeholder example
- Edit dialog: same fields minus file (FormCode disabled, edit chỉ
cập nhật metadata), có checkbox Kích hoạt
- Shared form submit handler — same dialog cho upload (__new) + edit
Foundation sẵn cho form builder thật (render UI từ FieldSpec JSON
đang là text field — iteration sau sẽ parse + render form dynamic).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| c8d0070770 |
[CLAUDE] App+Infra+Api+FE: Attachment upload E2E
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Failing after 1m40s
Foundation file-storage:
- IFileStorage interface (Application) — SaveAsync/OpenReadAsync/
DeleteAsync/Exists. Future swap cho S3/Azure Blob không đổi caller.
- LocalFileStorage (Infrastructure) — resolve Uploads:RootPath từ
config, path-traversal guard (resolved full path phải stay in root),
tự tạo directory khi save.
- DI: singleton (stateless).
- Config: dev "uploads", prod "C:\inetpub\solution-erp\uploads".
CQRS:
- UploadContractAttachmentCommand: validate size <=20MB + MIME whitelist
(pdf, doc/docx, xls/xlsx, png/jpg/jpeg/webp). Sanitize filename
(strip path components + invalid FS chars + leading dots). Storage
path: contracts/{contractId}/{attId}_{safeFileName}.
- DownloadContractAttachmentQuery: trả Stream + FileName + ContentType.
- DeleteContractAttachmentCommand: best-effort file delete sau DB remove
(orphan cleanup job có thể sweep sau).
Api:
- POST /api/contracts/{id}/attachments — multipart/form-data, field
'file' + form fields 'purpose' + 'note'. RequestSizeLimit 25MB
(validator enforces 20MB).
- GET /api/contracts/{id}/attachments/{attId}/download — File() stream.
- DELETE /api/contracts/{id}/attachments/{attId}.
FE ContractAttachmentsSection (both apps, identical):
- Drag-drop zone với dragging highlight (brand-500 border + brand-50 bg)
- Purpose selector (DraftExport / ScannedSigned / SealedCopy / Other)
- List có icon per MIME (FileText/Image/File), filename, metadata
(purpose · size · createdAt), download button (fetch blob + trigger
browser save với auth header), delete button (confirm dialog)
- Empty state hint về use-case ("bản scan HĐ đã ký ở phase In ký…")
Integrated vào cả 2 ContractDetailPage — ngay dưới phần comments,
trước sidebar lịch sử duyệt.
Unblock E2E workflow: users giờ có thể upload bản scan ký (DangInKy),
scan đóng dấu (DangDongDau) — phase transitions có bằng chứng thật.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 4abb5596d5 |
[CLAUDE] FE-Admin+FE-User: brand identity từ Solutions logo
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m54s
Lấy logo gốc từ template docx (SOL-CCM-FO-002.05) và brand color exact pixel-sampled #1F7DC1 từ chữ "Solutions". Thay đổi: - logo.png (407x145, từ header docx) đặt vào /public cả 2 app - favicon.svg: "S" trắng trên nền vuông brand blue bo góc - index.css: palette brand-50..900 generate quanh #1F7DC1 + accent red-500/600 cho ® mark + font Be Vietnam Pro (Google Fonts, designed cho tiếng Việt, diacritics đẹp) với fallback Inter + JetBrains Mono cho font-mono + tùy chỉnh scrollbar - Layout sidebar: logo.png 32px + "Admin"/"ERP" subtitle (thay text "SOLUTION ERP" đơn điệu) - LoginPage: gradient background brand-50 + 2 decorative orbs blur, rounded-2xl card + backdrop-blur, big logo 56px + subtitle tracking-[0.2em] - index.html: lang="vi", title "Solutions ERP · Admin" / "Solutions ERP", theme-color #1F7DC1 cho mobile address bar, preconnect fonts.gstatic.com để load Google Fonts nhanh hơn Tất cả màu hardcoded trong component đã dùng `brand-600` → tự map sang palette mới, không cần đổi logic. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| c73e3f904b |
[CLAUDE] FE: login error message hướng dẫn diagnose khi Network Error
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m55s
|
|||
| 6c0e20649a |
[CLAUDE] FE-Admin: PermissionsPage improved (search + stats + column bulk toggle + empty state)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m47s
|
|||
| 290936a0ca |
[CLAUDE] CICD+FE-Admin+FE-User: deploy pool-state guard + SlaTimer component
Some checks failed
Deploy SOLUTION_ERP / build-deploy (push) Has been cancelled
CICD: check app pool state before Stop-WebAppPool (idempotent). FE: new SlaTimer component with color-coded countdown (emerald/amber/red) and progress bar. Two variants: - inline: used in list tables (Inbox, Contracts list x2, MyContracts) - full: used in ContractDetail card with progress bar + deadline timestamp Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
|||
| 11e61c9c39 |
[CLAUDE] Phase5.1: Security headers + account lockout + Users management
Security hardening:
- Api/Middleware/SecurityHeadersMiddleware MOI: remove server fingerprint (Server, X-Powered-By, ...), add X-Content-Type-Options:nosniff, X-Frame-Options:DENY, Referrer-Policy:strict-origin-when-cross-origin, Permissions-Policy (disable geolocation/mic/cam/payment), X-Permitted-Cross-Domain-Policies:none, CSP (default-src 'self' + img data: + style inline for Tailwind + frame-ancestors 'none'). Skip CSP tren /swagger (dung inline script).
- Program.cs wire UseMiddleware SecurityHeadersMiddleware first in pipeline
- Infrastructure/DependencyInjection Identity options:
- Password.RequiredLength config-driven (Identity:Password:RequiredLength, default 8 dev, override 12+ prod)
- Lockout: DefaultLockoutTimeSpan (15min), MaxFailedAccessAttempts (5), AllowedForNewUsers=true — all config-driven
- LoginCommandHandler: IsLockedOutAsync check truoc → throw voi deadline message, AccessFailedAsync khi sai password, ResetAccessFailedCountAsync khi login thanh cong
Users management:
- Application/Users/UserFeatures.cs: 8 CQRS (ListUsersQuery paging+search, GetUserQuery, CreateUserCommand + Validator, UpdateUserCommand voi self-disable protection, AssignRolesCommand voi self-demote protection (khong tu go Admin), ResetPasswordCommand (invalidate refresh token + unlock), UnlockUserCommand)
- UserDto: Id, Email, FullName, IsActive, IsLocked (computed tu LockoutEnd), CreatedAt, Roles
- Api/Controllers/UsersController: 7 endpoint (Users.Read/Create/Update policies):
- GET / (list paged), GET /{id}, POST /, PUT /{id}, PUT /{id}/roles, POST /{id}/reset-password, POST /{id}/unlock
- using alias ValidationException = Application.Common.Exceptions.ValidationException (fix ambiguity voi FluentValidation)
Frontend fe-admin:
- types/users.ts MOI: User type + AVAILABLE_ROLES 12 role (match BE AppRoles.cs) + RoleLabel Vietnamese
- pages/system/UsersPage.tsx MOI:
- DataTable columns: Email (mono), FullName, Roles (badge chips voi Vietnamese label), IsActive (CheckCircle/XCircle), IsLocked (KeyRound red), CreatedAt
- Actions per row (PermissionGuard Users.Update wrap): Gan role (Shield icon → Dialog grid 12 checkbox), Reset password (KeyRound → Dialog voi warning user se bi logout), Unlock (Unlock icon, chi hien khi isLocked), Toggle active (XCircle/CheckCircle)
- Create user dialog: email + fullName + password (min 8) + grid 12 role checkbox
- Route /system/users vao App.tsx
E2E verified:
- Security headers present tren moi response (check qua curl -I)
- POST /api/users voi roles: [Drafter] → 201 + id
- GET /api/users → paged voi 2 user (admin + new test.drafter)
- TS check fe-admin → pass
- dotnet build → 0 errors
Docs:
- docs/STATUS.md: Phase 5.1 xong, cumulative BE 3700 LOC, 42 endpoints, 17 FE pages
- docs/HANDOFF.md: phase table update row Phase 5.1, last updated timestamp
- docs/changelog/migration-todos.md: tick 6 items Phase 5.1 + 4 items remaining (IDOR, deps scan, admin warning, Roles CRUD)
- docs/changelog/sessions/2026-04-21-1630-phase5-1-security-users.md: session log
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
EOF
|
|||
| fe7ad8e4a3 |
[CLAUDE] Phase4: Report MVP + Docs Consolidation (rules, architecture, schema-diagram)
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> |
|||
| 7e957a7654 |
[CLAUDE] Phase3: Workflow MVP — 9-phase state machine + code gen + FE Inbox/Detail
Backend Contracts domain (5 entities):
- Contract aggregate: Phase (9 enum), SlaDeadline, MaHopDong, BypassProcurementAndCCM, DraftData, SlaWarningSent
- ContractApproval: FromPhase → ToPhase, ApproverUserId (null = system auto-approve), Decision, Comment
- ContractComment: thread theo Phase current
- ContractAttachment: FileName + StoragePath + Purpose (DraftExport/ScannedSigned/SealedCopy)
- ContractCodeSequence: Prefix PK + LastSeq — atomic gen
EF configs:
- Unique MaHopDong filtered [MaHopDong] IS NOT NULL
- Indexes: Phase+IsDeleted, SupplierId, ProjectId, SlaDeadline, ContractId+ApprovedAt, ContractId+CreatedAt
- Cascade delete Approvals/Comments/Attachments khi Contract xoa
- Query filter IsDeleted
- Migration AddContractsWorkflow (DB 19 tables)
Workflow service:
- IContractWorkflowService.TransitionAsync:
- Adjacency check qua Transitions Dict<(from,to), roles[]> (12 transitions)
- Role guard: user phai co role ∈ allowed
- Admin bypass (role Admin pass moi check)
- System bypass (userId=null + Decision=AutoApprove → cho SLA job sau nay)
- Bypass CCM: BypassProcurementAndCCM=true cho phep DangInKy → DangTrinhKy skip phase 6
- Gen ma HD khi chuyen DangDongDau (idempotent — khong gen lai neu da co)
- Reset SlaDeadline = UtcNow + PhaseSla
- Insert ContractApproval row
Code generator (RG-001):
- 7 format theo ContractType: HDTP / HDGK / NCC / HDDV / MB + 2 framework (year prefix)
- BeginTransactionAsync(Serializable) + ContractCodeSequences UPSERT → atomic
- Idempotent: neu MaHopDong da co thi skip
CQRS (8 feature, ContractFeatures.cs):
- CreateContractCommand + Validator + Handler (set SlaDeadline = +7d)
- UpdateContractDraftCommand (chi khi Phase=DangSoanThao)
- TransitionContractCommand (delegate → WorkflowService)
- AddCommentCommand (phase = hien tai)
- ListContractsQuery (PagedResult + filter phase/supplier/project/search)
- GetMyInboxQuery (map Phase → actor roles, filter theo role user)
- GetContractQuery (detail + approvals + comments + attachments + resolve user names)
- DeleteContractCommand (soft, block > DangInKy)
Controller:
- ContractsController 8 endpoint: GET list/inbox/detail, POST create/transition/comment, PUT update, DELETE
Frontend fe-admin (2 page moi):
- types/contracts.ts: ContractPhase const + Label + Color maps + types
- components/PhaseBadge.tsx
- pages/contracts/ContractsListPage.tsx: filter phase + search + click → detail
- pages/contracts/ContractDetailPage.tsx: 2-col layout (info+comments | timeline), action dialog select target phase + comment
Frontend fe-user (4 page moi + 14 file shared):
- cp 14 file shared tu fe-admin (menuKeys, types/*, DataTable, PhaseBadge, Dialog, Textarea, Select, apiError, usePermission, PermissionGuard)
- AuthContext update: load menu tu /menus/me + cache
- Layout: menu fixed 3 muc + user info + roles display
- InboxPage: list HD cho role user xu ly (sort theo SLA)
- ContractCreatePage: form chon loai + template + NCC + du an + gia tri + bypass CDT
- ContractDetailPage: duplicate fe-admin pattern (convention)
- MyContractsPage: list HD cua toi
- App.tsx: 4 route moi
E2E verified:
- Setup Supplier + Project
- POST /contracts → 201 + phase=2
- POST /contracts/{id}/transitions x7 → di het 9 phase
- Final: MaHopDong = "FLOCK 01/HĐGK/SOL&PVL2026/01" dung format RG-001
- Approvals: 7 rows audit day du
Docs:
- .claude/skills/contract-workflow/SKILL.md: placeholder → full spec voi state machine, SLA table, role matrix, 7 code format, code pointers, API, E2E workflow, pitfalls
- docs/changelog/sessions/2026-04-21-1330-phase3-workflow.md: session log
- docs/STATUS.md: Phase 3 MVP done, next Phase 4
- docs/HANDOFF.md: update phase status + file tree + commit log + testing points
- docs/changelog/migration-todos.md: tick Phase 3 MVP items + add iteration 2 list
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 5113e4c771 |
[CLAUDE] Phase2: Form Engine MVP + docs (gotchas, skill, handoff)
Backend Forms:
- Domain/Forms: ContractTemplate (FormCode, Name, ContractType, FileName, StoragePath, Format, FieldSpec JSON, IsActive) + ContractClause
- EF config voi unique FormCode + query filter IsDeleted
- DbSets + IApplicationDbContext update
- Migration AddForms (bang 14 total)
- Packages: DocumentFormat.OpenXml 3.x + ClosedXML 0.105+
- Application/Forms:
- IFormRenderer interface + RenderResult record
- FormFeatures.cs: List/Get/Render CQRS
- IWebHostEnvironmentLocator (abstract IWebHostEnvironment)
- Infrastructure/Forms:
- DocxRenderer: OpenXml-based placeholder {{field}} replace, handle split runs (gom text tat ca <w:t> trong paragraph, replace, gan lai text dau + clear rest)
- XlsxRenderer: ClosedXML cell value replace
- FormRenderer router theo format docx/xlsx
- Api:
- FormsController: GET /templates (filter type, onlyActive), GET /templates/{id}, POST /templates/{id}/render (return file)
- WebHostEnvironmentLocator impl
- DbInitializer SeedContractTemplatesAsync: seed 8 template metadata, IsActive=true chi khi file ton tai
Templates vat ly:
- Copy 5 .docx/.xlsx tu FORM/ sang wwwroot/templates/
- 3 .doc (FO-002.02/03/06) chua convert: IsActive=false (Word COM bi stuck luc test, can retry voi DisplayAlerts=0 hoac LibreOffice)
- scripts/convert-doc-to-docx.ps1 (Word COM automation)
Frontend fe-admin:
- types/forms.ts: ContractTemplate + ContractTypeLabel
- pages/forms/FormsPage.tsx: list templates + Render dialog (paste JSON data → download .docx/.xlsx)
- Route /forms them vao App.tsx
Bug fix:
- SpaceProcessingModeValues namespace: wrap EnumValue<> full path
- SaveAs2($path, 16) thay vi SaveAs([ref], [ref]) — PowerShell type issue
- Word COM stuck: kill process, skip .doc cho MVP
Docs (theo yeu cau user):
- docs/gotchas.md MOI: 17 pitfalls nhom theo tech stack / EF Core / OpenXml / JSON / dev workflow
- .claude/skills/form-engine/SKILL.md: placeholder → full spec (algorithm + code pointers + API + limitations)
- .claude/skills/permission-matrix/SKILL.md: placeholder → full spec (BE policy + FE guard + seed + pitfalls)
- docs/HANDOFF.md MOI: brief 5 phut cho session sau (run quickstart + where we are + next steps + file tree + gotchas ref)
- docs/STATUS.md: update cumulative stats + next up Phase 3
- docs/changelog/migration-todos.md: tick Phase 2 iteration 1 items + add iteration 2 list
- docs/changelog/sessions/2026-04-21-1200-phase2-form-engine.md: session log
- CLAUDE.md root: them reference den gotchas + HANDOFF
E2E verified:
- GET /api/forms/templates (onlyActive=false) → 8 templates
- POST /api/forms/templates/{FO-002.05}/render voi data dict → HTTP 200 + file .docx 482KB (Microsoft Word 2007+ OK)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|||
| 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>
|
|||
| 702411fcc8 |
[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> |