[CLAUDE] PE-Duyệt: ẩn dropdown trạng thái + filter cứng "Đã gửi duyệt"
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m12s

Leaf "Duyệt" (pendingMe=1) chỉ load phiếu trạng thái "Đã gửi duyệt".
Nháp / Trả lại / Đã duyệt / Từ chối lọc bỏ client-side.

- Replace <Select> trạng thái bằng hint amber "Lọc cố định: Đã gửi duyệt"
- allRows.filter qua getPeDisplayStatus === DaGuiDuyet
- Header count dùng rows.length khi pendingMe (inbox không paged)
- Mirror fe-admin + fe-user

Workaround BE /inbox loose UAT (trả phiếu Nháp). Phân quyền strict V2
pending Session 18+.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-08 17:07:23 +07:00
parent 8680f4c849
commit aaa1c6cba6
2 changed files with 66 additions and 38 deletions

View File

@ -107,7 +107,13 @@ export function PurchaseEvaluationsListPage() {
}
}
const rows = list.data?.items ?? []
const allRows = list.data?.items ?? []
// Duyệt (pendingMe) → filter cứng client-side chỉ "Đã gửi duyệt" (Nháp/Trả lại/
// Đã duyệt/Từ chối loại bỏ). BE /inbox hiện loose UAT có thể trả phiếu Nháp →
// FE filter để UX đúng kỳ vọng. Phân quyền strict V2 BE pending Session 18+.
const rows = pendingMe
? allRows.filter(p => getPeDisplayStatus(p.phase) === PeDisplayStatus.DaGuiDuyet)
: allRows
const headerTitle = typeFilter
? (pendingMe ? `${PurchaseEvaluationTypeLabel[typeFilter]} — Chờ duyệt` : PurchaseEvaluationTypeLabel[typeFilter])
@ -120,7 +126,7 @@ export function PurchaseEvaluationsListPage() {
<ClipboardCheck className="h-5 w-5 text-slate-500" />
<h1 className="text-base font-semibold tracking-tight text-slate-900">{headerTitle}</h1>
<span className="ml-2 rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-medium text-slate-600">
{list.data?.total ?? 0}
{pendingMe ? rows.length : (list.data?.total ?? 0)}
</span>
</div>
</header>
@ -151,23 +157,31 @@ export function PurchaseEvaluationsListPage() {
))}
</Select>
)}
<Select value={phase} onChange={e => setParam('phase', e.target.value)}>
<option value="">Tất cả trạng thái</option>
{Object.values(PeDisplayStatus).map(s => {
const phaseValue = s === PeDisplayStatus.Nhap
? String(PurchaseEvaluationPhase.DangSoanThao)
: s === PeDisplayStatus.DaDuyet
? String(PurchaseEvaluationPhase.DaDuyet)
: s === PeDisplayStatus.TraLai
? String(PurchaseEvaluationPhase.TraLai)
: s === PeDisplayStatus.TuChoi
? String(PurchaseEvaluationPhase.TuChoi)
: '' // DaGuiDuyet — multi-phase, không filter exact (TODO BE)
return phaseValue ? (
<option key={s} value={phaseValue}>{PeDisplayStatusLabel[s]}</option>
) : null
})}
</Select>
{/* Duyệt (pendingMe) → filter cứng "Đã gửi duyệt", ẩn dropdown trạng thái.
Danh sách (pendingMe=false) → giữ dropdown cho user filter mọi trạng thái. */}
{pendingMe ? (
<div className="rounded border border-amber-200 bg-amber-50 px-2 py-1.5 text-[11px] text-amber-700">
Lọc cố đnh: <strong>Đã gửi duyệt</strong> (phiếu đang chờ duyệt)
</div>
) : (
<Select value={phase} onChange={e => setParam('phase', e.target.value)}>
<option value="">Tất cả trạng thái</option>
{Object.values(PeDisplayStatus).map(s => {
const phaseValue = s === PeDisplayStatus.Nhap
? String(PurchaseEvaluationPhase.DangSoanThao)
: s === PeDisplayStatus.DaDuyet
? String(PurchaseEvaluationPhase.DaDuyet)
: s === PeDisplayStatus.TraLai
? String(PurchaseEvaluationPhase.TraLai)
: s === PeDisplayStatus.TuChoi
? String(PurchaseEvaluationPhase.TuChoi)
: '' // DaGuiDuyet — multi-phase, không filter exact (TODO BE)
return phaseValue ? (
<option key={s} value={phaseValue}>{PeDisplayStatusLabel[s]}</option>
) : null
})}
</Select>
)}
</div>
<div className="flex-1 overflow-y-auto">

View File

@ -107,7 +107,13 @@ export function PurchaseEvaluationsListPage() {
}
}
const rows = list.data?.items ?? []
const allRows = list.data?.items ?? []
// Duyệt (pendingMe) → filter cứng client-side chỉ "Đã gửi duyệt" (Nháp/Trả lại/
// Đã duyệt/Từ chối loại bỏ). BE /inbox hiện loose UAT có thể trả phiếu Nháp →
// FE filter để UX đúng kỳ vọng. Phân quyền strict V2 BE pending Session 18+.
const rows = pendingMe
? allRows.filter(p => getPeDisplayStatus(p.phase) === PeDisplayStatus.DaGuiDuyet)
: allRows
const headerTitle = typeFilter
? (pendingMe ? `${PurchaseEvaluationTypeLabel[typeFilter]} — Chờ duyệt` : PurchaseEvaluationTypeLabel[typeFilter])
@ -120,7 +126,7 @@ export function PurchaseEvaluationsListPage() {
<ClipboardCheck className="h-5 w-5 text-slate-500" />
<h1 className="text-base font-semibold tracking-tight text-slate-900">{headerTitle}</h1>
<span className="ml-2 rounded-full bg-slate-100 px-2 py-0.5 text-[11px] font-medium text-slate-600">
{list.data?.total ?? 0}
{pendingMe ? rows.length : (list.data?.total ?? 0)}
</span>
</div>
</header>
@ -151,23 +157,31 @@ export function PurchaseEvaluationsListPage() {
))}
</Select>
)}
<Select value={phase} onChange={e => setParam('phase', e.target.value)}>
<option value="">Tất cả trạng thái</option>
{Object.values(PeDisplayStatus).map(s => {
const phaseValue = s === PeDisplayStatus.Nhap
? String(PurchaseEvaluationPhase.DangSoanThao)
: s === PeDisplayStatus.DaDuyet
? String(PurchaseEvaluationPhase.DaDuyet)
: s === PeDisplayStatus.TraLai
? String(PurchaseEvaluationPhase.TraLai)
: s === PeDisplayStatus.TuChoi
? String(PurchaseEvaluationPhase.TuChoi)
: '' // DaGuiDuyet — multi-phase, không filter exact (TODO BE)
return phaseValue ? (
<option key={s} value={phaseValue}>{PeDisplayStatusLabel[s]}</option>
) : null
})}
</Select>
{/* Duyệt (pendingMe) → filter cứng "Đã gửi duyệt", ẩn dropdown trạng thái.
Danh sách (pendingMe=false) → giữ dropdown cho user filter mọi trạng thái. */}
{pendingMe ? (
<div className="rounded border border-amber-200 bg-amber-50 px-2 py-1.5 text-[11px] text-amber-700">
Lọc cố đnh: <strong>Đã gửi duyệt</strong> (phiếu đang chờ duyệt)
</div>
) : (
<Select value={phase} onChange={e => setParam('phase', e.target.value)}>
<option value="">Tất cả trạng thái</option>
{Object.values(PeDisplayStatus).map(s => {
const phaseValue = s === PeDisplayStatus.Nhap
? String(PurchaseEvaluationPhase.DangSoanThao)
: s === PeDisplayStatus.DaDuyet
? String(PurchaseEvaluationPhase.DaDuyet)
: s === PeDisplayStatus.TraLai
? String(PurchaseEvaluationPhase.TraLai)
: s === PeDisplayStatus.TuChoi
? String(PurchaseEvaluationPhase.TuChoi)
: '' // DaGuiDuyet — multi-phase, không filter exact (TODO BE)
return phaseValue ? (
<option key={s} value={phaseValue}>{PeDisplayStatusLabel[s]}</option>
) : null
})}
</Select>
)}
</div>
<div className="flex-1 overflow-y-auto">