[CLAUDE] Workflow: wire ApproveV2 + LevelOpinions cho 4 WorkflowApps module (Phase 11 P11-A)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m6s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m6s
Wire full approval workflow V2 cho Leave/OT/Travel/Vehicle — cookie-cutter
mirror Proposal (Mig 38). Trước đây skeleton Phase 1 (Create+List), giờ
ApproveV2 advance-level + UPSERT LevelOpinion + atomic codegen.
Schema (Mig 41 WireWorkflowAppsApprovalV2 — 84→89 tables, pure additive):
- 4 bảng {Leave,Ot,Travel,Vehicle}LevelOpinions (UNIQUE composite + Cascade
parent + Restrict Level — mirror ProposalLevelOpinion)
- 1 bảng WorkflowAppCodeSequences (shared atomic MaDonTu, Prefix-keyed)
- 4 cột RejectedFromStatus (smart return tracking)
- enum ApprovalWorkflowApplicableType.TravelRequest = 9
Application (LeaveOt + TravelVehicle ApprovalFeatures.cs — 30 handler):
- GetById detail (Include LevelOpinions + JOIN Step/Level) · UpdateDraft
- Submit (gen MaDonTu + DaGuiDuyet + level=1, verify ApplicableType per module)
- Approve (verify actor==ApproverUserId OR Admin, UPSERT opinion latest-write-wins,
advance level OR terminal DaDuyet, empty comment → placeholder)
- Reject (→TuChoi) · Return (→TraLai + RejectedFromStatus)
Api: 4 controller +6 route mỗi cái (GET/{id}, PUT/{id}, submit/approve/reject/return)
Infra: DbInitializer seed 4 workflow V2 mẫu (QT-NP/OT/CT/XE-V2-001) → UAT test ngay
FE: WorkflowAppDetailPage.tsx declarative 4-kind (fe-admin+fe-user SHA256 identical)
— workflow status + opinion timeline + action buttons; gỡ banner skeleton + row nav
Tests: +11 WorkflowAppApproveV2Tests (130→141 PASS) — state machine + UPSERT
invariant + guards + codegen + forbidden + placeholder (Leave full + Ot smoke)
Verify: build 0 error · 141 test PASS · FE build ×2 · reviewer checklist
(ApplicableType per-module + cross-module DbSet + [Authorize] — no copy-paste bug)
Known-minor (unreachable): Reject/Return actor-check skip nếu CurrentApprovalLevelOrder
null — nhưng DaGuiDuyet luôn có set (defer hardening).
ItTicket KHÔNG đụng (kanban, no workflow V2).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -1,9 +1,9 @@
|
||||
// Generic Workflow Apps List Page — Phase 10.3 G-O4+G-O5+G-O6 (S38 2026-05-28).
|
||||
// SKELETON Phase 1: read-only list. Create form + workflow actions DEFER Phase 11.
|
||||
// Wave 3a (S42 2026-05-30): row click → Detail page (workflow actions + opinion timeline).
|
||||
// Handles 4 module via URL `:kind` param: leave / ot / travel / vehicle.
|
||||
// File MIRROR SHA256 identical fe-user counterpart.
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { CalendarOff, Clock, Plane, Car, FileSignature } from 'lucide-react'
|
||||
import { PageHeader } from '@/components/PageHeader'
|
||||
import { api } from '@/lib/api'
|
||||
@ -80,6 +80,7 @@ const ICON_MAP: Record<Kind, any> = {
|
||||
|
||||
export function WorkflowAppsListPage() {
|
||||
const { kind = 'leave' } = useParams<{ kind: Kind }>()
|
||||
const navigate = useNavigate()
|
||||
const config = KIND_CONFIG[kind as Kind]
|
||||
const Icon = ICON_MAP[kind as Kind] ?? FileSignature
|
||||
|
||||
@ -99,11 +100,6 @@ export function WorkflowAppsListPage() {
|
||||
<div className="space-y-4">
|
||||
<PageHeader title={config.title} description={config.description} />
|
||||
|
||||
<div className="rounded-lg border bg-amber-50/50 p-3 text-sm text-amber-900">
|
||||
⚠️ <strong>Skeleton Phase 1 (S38):</strong> Read-only list. Form tạo + workflow Approve/Reject defer Phase 11 polish.
|
||||
Em chủ trì kích hoạt full ApproveV2 wire khi anh main yêu cầu.
|
||||
</div>
|
||||
|
||||
<div className="rounded-lg border bg-card">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="border-b bg-muted/50">
|
||||
@ -131,7 +127,11 @@ export function WorkflowAppsListPage() {
|
||||
</tr>
|
||||
)}
|
||||
{items.map((item: any) => (
|
||||
<tr key={item.id} className="border-b">
|
||||
<tr
|
||||
key={item.id}
|
||||
className="border-b cursor-pointer hover:bg-muted/40"
|
||||
onClick={() => navigate(`/workflow-apps/${kind}/${item.id}`)}
|
||||
>
|
||||
{config.columns.map((c) => (
|
||||
<td key={c.key} className="px-4 py-2">{c.render(item)}</td>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user