[CLAUDE] PE Inbox: nhận filter approvalWorkflowId + show dropdown cả 2 view
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m3s

User báo:
- Filter "Tất cả quy trình duyệt" hiện chỉ ở Danh sách → muốn ở cả 2
- Filter chọn quy trình → không thấy phiếu V2 trong Duyệt (Inbox)

BE — wire filter vào Inbox:
- GetMyPurchaseEvaluationInboxQuery +ApprovalWorkflowId? param
- Handler thêm filter `q.Where(x => x.e.ApprovalWorkflowId == awId)`
- PurchaseEvaluationsController.Inbox +approvalWorkflowId query param

FE (cả 2 app mirror):
- PurchaseEvaluationsListPage: bỏ điều kiện `!pendingMe` ở Select dropdown
  → hiển thị filter quy trình duyệt CẢ Duyệt + Danh sách
- Inbox API call: pass approvalWorkflowId từ URL param

Verify: BE 0 error · 2 FE builds OK.

Test luồng eoffice:
1. Vào "Duyệt NCC > Duyệt" → 2 dropdown filter hiện đầy đủ
2. Chọn 1 quy trình V2 từ dropdown → list filter chỉ phiếu pin quy trình đó
3. Vào "Duyệt NCC > Danh sách" → 2 dropdown vẫn show, filter cũng work
This commit is contained in:
pqhuy1987
2026-05-08 15:37:03 +07:00
parent ac41d5e0d8
commit d250ae4e71
4 changed files with 37 additions and 28 deletions

View File

@ -55,7 +55,10 @@ export function PurchaseEvaluationsListPage() {
queryFn: async () => { queryFn: async () => {
if (pendingMe) { if (pendingMe) {
const res = await api.get<PeListItem[]>('/purchase-evaluations/inbox', { const res = await api.get<PeListItem[]>('/purchase-evaluations/inbox', {
params: { type: typeFilter ?? undefined }, params: {
type: typeFilter ?? undefined,
approvalWorkflowId: approvalWorkflowId || undefined,
},
}) })
return { items: res.data, total: res.data.length, page: 1, pageSize: res.data.length } return { items: res.data, total: res.data.length, page: 1, pageSize: res.data.length }
} }
@ -135,17 +138,16 @@ export function PurchaseEvaluationsListPage() {
className="pl-8" className="pl-8"
/> />
</div> </div>
{/* Mig 23 — 2 dropdown tách: Quy trình duyệt + Trạng thái */} {/* Mig 23 — 2 dropdown tách: Quy trình duyệt + Trạng thái.
{!pendingMe && ( Hiển thị cả 2 view (Duyệt + Danh sách) — user feedback. */}
<Select value={approvalWorkflowId} onChange={e => setParam('awId', e.target.value)}> <Select value={approvalWorkflowId} onChange={e => setParam('awId', e.target.value)}>
<option value="">Tất cả quy trình duyệt</option> <option value="">Tất cả quy trình duyệt</option>
{approvalWorkflows.data?.map(w => ( {approvalWorkflows.data?.map(w => (
<option key={w.id} value={w.id}> <option key={w.id} value={w.id}>
{w.code} v{String(w.version).padStart(2, '0')} {w.name} {w.code} v{String(w.version).padStart(2, '0')} {w.name}
</option> </option>
))} ))}
</Select> </Select>
)}
<Select value={phase} onChange={e => setParam('phase', e.target.value)}> <Select value={phase} onChange={e => setParam('phase', e.target.value)}>
<option value="">Tất cả trạng thái</option> <option value="">Tất cả trạng thái</option>
{Object.values(PeDisplayStatus).map(s => { {Object.values(PeDisplayStatus).map(s => {

View File

@ -55,7 +55,10 @@ export function PurchaseEvaluationsListPage() {
queryFn: async () => { queryFn: async () => {
if (pendingMe) { if (pendingMe) {
const res = await api.get<PeListItem[]>('/purchase-evaluations/inbox', { const res = await api.get<PeListItem[]>('/purchase-evaluations/inbox', {
params: { type: typeFilter ?? undefined }, params: {
type: typeFilter ?? undefined,
approvalWorkflowId: approvalWorkflowId || undefined,
},
}) })
return { items: res.data, total: res.data.length, page: 1, pageSize: res.data.length } return { items: res.data, total: res.data.length, page: 1, pageSize: res.data.length }
} }
@ -135,17 +138,16 @@ export function PurchaseEvaluationsListPage() {
className="pl-8" className="pl-8"
/> />
</div> </div>
{/* Mig 23 — 2 dropdown tách: Quy trình duyệt + Trạng thái */} {/* Mig 23 — 2 dropdown tách: Quy trình duyệt + Trạng thái.
{!pendingMe && ( Hiển thị cả 2 view (Duyệt + Danh sách) — user feedback. */}
<Select value={approvalWorkflowId} onChange={e => setParam('awId', e.target.value)}> <Select value={approvalWorkflowId} onChange={e => setParam('awId', e.target.value)}>
<option value="">Tất cả quy trình duyệt</option> <option value="">Tất cả quy trình duyệt</option>
{approvalWorkflows.data?.map(w => ( {approvalWorkflows.data?.map(w => (
<option key={w.id} value={w.id}> <option key={w.id} value={w.id}>
{w.code} v{String(w.version).padStart(2, '0')} {w.name} {w.code} v{String(w.version).padStart(2, '0')} {w.name}
</option> </option>
))} ))}
</Select> </Select>
)}
<Select value={phase} onChange={e => setParam('phase', e.target.value)}> <Select value={phase} onChange={e => setParam('phase', e.target.value)}>
<option value="">Tất cả trạng thái</option> <option value="">Tất cả trạng thái</option>
{Object.values(PeDisplayStatus).map(s => { {Object.values(PeDisplayStatus).map(s => {

View File

@ -28,8 +28,10 @@ public class PurchaseEvaluationsController(IMediator mediator) : ControllerBase
[HttpGet("inbox")] [HttpGet("inbox")]
public async Task<ActionResult<List<PurchaseEvaluationListItemDto>>> Inbox( public async Task<ActionResult<List<PurchaseEvaluationListItemDto>>> Inbox(
[FromQuery] PurchaseEvaluationType? type = null, CancellationToken ct = default) [FromQuery] PurchaseEvaluationType? type = null,
=> Ok(await mediator.Send(new GetMyPurchaseEvaluationInboxQuery(type), ct)); [FromQuery] Guid? approvalWorkflowId = null,
CancellationToken ct = default)
=> Ok(await mediator.Send(new GetMyPurchaseEvaluationInboxQuery(type, approvalWorkflowId), ct));
[HttpGet("{id:guid}")] [HttpGet("{id:guid}")]
public async Task<ActionResult<PurchaseEvaluationDetailBundleDto>> Get(Guid id, CancellationToken ct) public async Task<ActionResult<PurchaseEvaluationDetailBundleDto>> Get(Guid id, CancellationToken ct)

View File

@ -333,8 +333,9 @@ public class ListPurchaseEvaluationsQueryHandler(
// ========== INBOX ========== // ========== INBOX ==========
public record GetMyPurchaseEvaluationInboxQuery(PurchaseEvaluationType? Type = null) public record GetMyPurchaseEvaluationInboxQuery(
: IRequest<List<PurchaseEvaluationListItemDto>>; PurchaseEvaluationType? Type = null,
Guid? ApprovalWorkflowId = null) : IRequest<List<PurchaseEvaluationListItemDto>>;
public class GetMyPurchaseEvaluationInboxQueryHandler( public class GetMyPurchaseEvaluationInboxQueryHandler(
IApplicationDbContext db, IApplicationDbContext db,
@ -374,6 +375,8 @@ public class GetMyPurchaseEvaluationInboxQueryHandler(
select new { e, p, s }; select new { e, p, s };
if (request.Type is not null) q = q.Where(x => x.e.Type == request.Type); if (request.Type is not null) q = q.Where(x => x.e.Type == request.Type);
if (request.ApprovalWorkflowId is not null)
q = q.Where(x => x.e.ApprovalWorkflowId == request.ApprovalWorkflowId);
return await q return await q
.OrderBy(x => x.e.SlaDeadline ?? DateTime.MaxValue) .OrderBy(x => x.e.SlaDeadline ?? DateTime.MaxValue)