From de1c378279c27d96b68d631736c9cdb4b4b00a9a Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Thu, 28 May 2026 15:51:14 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Domain+App+Infra+Api+FE-Admin+FE-Use?= =?UTF-8?q?r:=20S37=20Mig=2037=20enum=20+=20Plan=20G-O3=20=C4=90=E1=BB=81?= =?UTF-8?q?=20xu=E1=BA=A5t=20full-stack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 10.3 G-O3 Đề xuất (Proposal) — Mig 37 enum extend +5 values + Mig 38 Proposal schema + BE CQRS 8 endpoint + FE 2 app SHA256 IDENTICAL. Mig 37 (em main solo): extend ApprovalWorkflowApplicableType enum +5 values ProposalGeneral=4 / LeaveRequest=5 / OtRequest=6 / VehicleBooking=7 / ItTicket=8 cookie-cutter Mig 22 pattern (Up/Down empty — enum mức Domain). Mig 38 (em main solo): 4 entity Proposal (Code DX/YYYY/NNN) + ProposalAttachment + ProposalLevelOpinion (UNIQUE composite PEId+LevelId mirror PE Mig 26) + ProposalCodeSequence (Prefix PK atomic seq). 4 EF Config + 2 DbContext mod. BE CQRS (em main solo ~700 LOC ProposalFeatures.cs sau Implementer truncate phase exploration gotcha #53 5th + 529 Overload): - 4 Header handler (List paged + GetById detail + Create + UpdateDraft owner-OR-admin) - 4 Workflow handler (Submit gen MaDeXuat atomic + Approve UPSERT LevelOpinion advance + Reject + Return) - SERIALIZABLE transaction CodeGen - DTOs nested LevelOpinion với Step+Level metadata JOIN ProposalsController 8 endpoint /api/proposals (List/GetById/Create/Update/Submit/Approve/Reject/Return) class-level [Authorize] + handler-level owner-OR-admin guard. DbInitializer: SeedSampleProposalWorkflowV2Async ~40 LOC seed QT-DX-V2-001 IsUserSelectable=true NOT gated DemoSeed per gotcha #51. SeedMenuTreeAsync +4 row (Off_DeXuat sub-group + 3 leaf). FE 2 app (em main solo + Implementer 529 fail fallback): - types/proposal.ts × 2 SHA256 IDENTICAL 95607052ff1138f2 - ProposalsListPage.tsx × 2 IDENTICAL 603f0d9cf74cd09a — table 6 cột + Status badge + filter - ProposalCreatePage.tsx × 2 IDENTICAL 6aed3a76563dd576 — Form Header card - ProposalDetailPage.tsx × 2 IDENTICAL 3dc229ea8dcc9bc0 — 3 Section + WorkflowActions - Pattern 16-bis 8× cumulative (App.tsx + menuKeys + Layout staticMap 3 entry) Verify: - dotnet build PASS 0 error 2 warning pre-existing DocxRenderer - dotnet test 130/130 PASS baseline preserve - npm build × 2 PASS (fe-admin 14.72s + fe-user 6.40s) - SHA256 verify 4 file × 2 app all IDENTICAL Pattern reinforced cumulative S37: - Pattern 12-bis cross-module mirror 11× (PE V2 → Proposal V2 ApproveV2) - Pattern 16-bis 4-place mirror cross-app 8× - gotcha #53 5th occurrence Implementer mid-exploration truncation + 529 Overload 1× — em main solo fallback proven Co-Authored-By: Claude Opus 4.7 (1M context) --- fe-admin/src/App.tsx | 6 + fe-admin/src/components/Layout.tsx | 3 + fe-admin/src/lib/menuKeys.ts | 5 + .../src/pages/office/ProposalCreatePage.tsx | 191 + .../src/pages/office/ProposalDetailPage.tsx | 310 + .../src/pages/office/ProposalsListPage.tsx | 209 + fe-admin/src/types/proposal.ts | 95 + fe-user/src/App.tsx | 6 + fe-user/src/components/Layout.tsx | 3 + fe-user/src/lib/menuKeys.ts | 5 + .../src/pages/office/ProposalCreatePage.tsx | 191 + .../src/pages/office/ProposalDetailPage.tsx | 310 + .../src/pages/office/ProposalsListPage.tsx | 209 + fe-user/src/types/proposal.ts | 95 + .../Controllers/ProposalsController.cs | 86 + .../Interfaces/IApplicationDbContext.cs | 8 + .../Office/ProposalFeatures.cs | 556 ++ .../ApprovalWorkflowsV2/ApprovalWorkflow.cs | 8 + .../SolutionErp.Domain/Identity/MenuKeys.cs | 7 + .../SolutionErp.Domain/Office/Enums.cs | 12 + .../SolutionErp.Domain/Office/Proposal.cs | 38 + .../Office/ProposalAttachment.cs | 20 + .../Office/ProposalCodeSequence.cs | 13 + .../Office/ProposalLevelOpinion.cs | 28 + .../Persistence/ApplicationDbContext.cs | 6 + .../ProposalAttachmentConfiguration.cs | 27 + .../ProposalCodeSequenceConfiguration.cs | 19 + .../Configurations/ProposalConfiguration.cs | 31 + .../ProposalLevelOpinionConfiguration.cs | 31 + .../Persistence/DbInitializer.cs | 60 + ...dApplicableTypeForWorkflowApps.Designer.cs | 5170 ++++++++++++++++ ...332_ExtendApplicableTypeForWorkflowApps.cs | 22 + .../20260528082726_AddProposals.Designer.cs | 5428 +++++++++++++++++ .../Migrations/20260528082726_AddProposals.cs | 184 + .../ApplicationDbContextModelSnapshot.cs | 258 + 35 files changed, 13650 insertions(+) create mode 100644 fe-admin/src/pages/office/ProposalCreatePage.tsx create mode 100644 fe-admin/src/pages/office/ProposalDetailPage.tsx create mode 100644 fe-admin/src/pages/office/ProposalsListPage.tsx create mode 100644 fe-admin/src/types/proposal.ts create mode 100644 fe-user/src/pages/office/ProposalCreatePage.tsx create mode 100644 fe-user/src/pages/office/ProposalDetailPage.tsx create mode 100644 fe-user/src/pages/office/ProposalsListPage.tsx create mode 100644 fe-user/src/types/proposal.ts create mode 100644 src/Backend/SolutionErp.Api/Controllers/ProposalsController.cs create mode 100644 src/Backend/SolutionErp.Application/Office/ProposalFeatures.cs create mode 100644 src/Backend/SolutionErp.Domain/Office/Proposal.cs create mode 100644 src/Backend/SolutionErp.Domain/Office/ProposalAttachment.cs create mode 100644 src/Backend/SolutionErp.Domain/Office/ProposalCodeSequence.cs create mode 100644 src/Backend/SolutionErp.Domain/Office/ProposalLevelOpinion.cs create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/ProposalAttachmentConfiguration.cs create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/ProposalCodeSequenceConfiguration.cs create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/ProposalConfiguration.cs create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/ProposalLevelOpinionConfiguration.cs create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260528082332_ExtendApplicableTypeForWorkflowApps.Designer.cs create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260528082332_ExtendApplicableTypeForWorkflowApps.cs create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260528082726_AddProposals.Designer.cs create mode 100644 src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/20260528082726_AddProposals.cs diff --git a/fe-admin/src/App.tsx b/fe-admin/src/App.tsx index 8e32666..0c6b345 100644 --- a/fe-admin/src/App.tsx +++ b/fe-admin/src/App.tsx @@ -32,6 +32,9 @@ import { HrmConfigsPage } from '@/pages/hrm/HrmConfigsPage' import { InternalDirectoryPage } from '@/pages/office/InternalDirectoryPage' import { MeetingCalendarPage } from '@/pages/office/MeetingCalendarPage' import { MeetingRoomsPage } from '@/pages/office/MeetingRoomsPage' +import { ProposalCreatePage } from '@/pages/office/ProposalCreatePage' +import { ProposalDetailPage } from '@/pages/office/ProposalDetailPage' +import { ProposalsListPage } from '@/pages/office/ProposalsListPage' function App() { return ( @@ -85,6 +88,9 @@ function App() { {/* Văn phòng số — Phòng họp Booking + Catalog (Phase 10.2 G-O2 — Mig 36 S36) */} } /> } /> + } /> + } /> + } /> } /> } /> 0 ? n.toLocaleString('vi-VN') : '' +} + +export function ProposalCreatePage() { + const navigate = useNavigate() + const [title, setTitle] = useState('') + const [description, setDescription] = useState('') + const [amountStr, setAmountStr] = useState('') + const [departmentId, setDepartmentId] = useState('') + const [approvalWorkflowId, setApprovalWorkflowId] = useState('') + + const departments = useQuery({ + queryKey: ['departments'], + queryFn: async () => (await api.get('/departments')).data, + }) + + const workflows = useQuery({ + queryKey: ['approval-workflows-v2', { applicableType: 4, isUserSelectable: true }], + queryFn: async () => + (await api.get('/approval-workflows-v2', { + params: { applicableType: 4, isUserSelectable: true }, + })).data, + }) + + const create = useMutation({ + mutationFn: async () => { + if (!title.trim()) throw new Error('Vui lòng nhập Tiêu đề') + const body: CreateProposalInput = { + title: title.trim(), + description: description.trim() || null, + amountEstimate: amountStr ? parseVnd(amountStr) : null, + departmentId: departmentId || null, + approvalWorkflowId: approvalWorkflowId || null, + } + const res = await api.post<{ id: string }>('/proposals', body) + return res.data.id + }, + onSuccess: (id) => { + toast.success('Tạo đề xuất thành công') + navigate(`/proposals/${id}`) + }, + onError: (e) => toast.error(getErrorMessage(e)), + }) + + const onSubmit = (e: FormEvent) => { + e.preventDefault() + create.mutate() + } + + return ( +
+ navigate('/proposals')}> + + Huỷ + + } + /> + +
+
+
+ + setTitle(e.target.value)} + maxLength={300} + placeholder="vd. Đề xuất mua sắm máy tính cho Phòng IT" + required + /> +
+ +
+ +