[CLAUDE] FE-User: fix menu sang nham — click "Danh sach" lai highlight "Da duyet"
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m41s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m41s
Bug UAT anh bao 2026-07-27 (anh chup man hinh): bam muc "Danh sach" thi menu lai to sang muc "Da duyet". Goc: `phase` vua la BO LOC trang thai tren trang danh sach (nen nam trong TRANSIENT_QUERY_KEYS tu fix 2026-05-08 — de user loc/chon dong khong mat highlight), vua VUA-MOI tro thanh DANH TINH dieu huong cua muc "Da duyet" (`?type=N&phase=7`, dot 2 hom nay). `queryMatches` strip `phase` khoi CA HAI ve => "Danh sach" ([type]) va "Da duyet" ([type] sau khi mat phase) thanh KHONG PHAN BIET DUOC => URL `?type=1` khop ca hai, muc render sau thang phan hien thi. Cung mot tham so khong the vua la thu-bi-bo-qua vua la thu-dinh-danh. 2 sua, ca 2 app (Layout khong phai file mirror byte-identical nen sua rieng tung ben): - `queryMatches`: transient chi duoc bo qua khi MENU DICH KHONG tu ghim key do (dich ghim => so khop NGHIEM; khong ghim => giu hanh vi cu). - Route "Da duyet" them `&view=approved` = khoa DINH DANH rieng (trang bo qua no, chi `phase=7` loc that). Can thiet vi URL muc "Da duyet" TRUNG HET URL ma user tu loc trang thai tren Danh sach => khong the phan biet bang URL neu thieu khoa nay. Kiem 4 ca duong di (khong chi ca anh bao): ?type=1 -> Danh sach SANG, Da duyet TAT ?type=1&phase=7&view=approved -> Da duyet SANG, Danh sach TAT ?type=1&phase=7 (user tu loc) -> Danh sach VAN SANG (giu fix 2026-05-08) ?type=1&deleted=1 -> Da xoa SANG, con lai TAT tsc 0 loi x2 · npm build PASS x2. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@ -127,7 +127,11 @@ function resolvePath(key: string): string | null {
|
|||||||
// [S155 đợt 2] 2 mục MỚI — CẢ HAI tái dùng `PurchaseEvaluationsListPage`, 0 route mới,
|
// [S155 đợt 2] 2 mục MỚI — CẢ HAI tái dùng `PurchaseEvaluationsListPage`, 0 route mới,
|
||||||
// 0 page mới. "Đã duyệt" chỉ là filter phase sẵn có (`DaDuyet = 7`, đã thông 3 tầng);
|
// 0 page mới. "Đã duyệt" chỉ là filter phase sẵn có (`DaDuyet = 7`, đã thông 3 tầng);
|
||||||
// "Đã xóa" là view riêng trong cùng page, gọi endpoint `/purchase-evaluations/deleted`.
|
// "Đã xóa" là view riêng trong cùng page, gọi endpoint `/purchase-evaluations/deleted`.
|
||||||
if (action === 'Approved') return `/purchase-evaluations?type=${typeInt}&phase=7`
|
// 🔴 `view=approved` = KHOÁ ĐỊNH DANH (trang bỏ qua, chỉ `phase=7` lọc thật). URL mục
|
||||||
|
// "Đã duyệt" trùng hệt URL user tự lọc trạng thái trên Danh sách ⇒ menu sáng nhầm
|
||||||
|
// (bug UAT 2026-07-27). `phase` thuộc TRANSIENT nên không làm danh tính được.
|
||||||
|
// Mirror `fe-user/src/components/Layout.tsx` — cùng bản vá.
|
||||||
|
if (action === 'Approved') return `/purchase-evaluations?type=${typeInt}&phase=7&view=approved`
|
||||||
if (action === 'Deleted') return `/purchase-evaluations?type=${typeInt}&deleted=1`
|
if (action === 'Deleted') return `/purchase-evaluations?type=${typeInt}&deleted=1`
|
||||||
}
|
}
|
||||||
// PE workflow admin leaf: PeWf_<Code> → /system/pe-workflows/<code>
|
// PE workflow admin leaf: PeWf_<Code> → /system/pe-workflows/<code>
|
||||||
@ -225,8 +229,17 @@ const TRANSIENT_QUERY_KEYS = new Set(['id', 'q', 'editHeader', 'page', 'phase',
|
|||||||
function queryMatches(current: string, target: string): boolean {
|
function queryMatches(current: string, target: string): boolean {
|
||||||
const a = new URLSearchParams(current)
|
const a = new URLSearchParams(current)
|
||||||
const b = new URLSearchParams(target)
|
const b = new URLSearchParams(target)
|
||||||
const aKeys = [...a.keys()].filter(k => !TRANSIENT_QUERY_KEYS.has(k)).sort()
|
// 🔴 [S155 đợt 2] Transient chỉ được bỏ qua khi MENU ĐÍCH KHÔNG tự ghim key đó.
|
||||||
const bKeys = [...b.keys()].filter(k => !TRANSIENT_QUERY_KEYS.has(k)).sort()
|
// Bug UAT 2026-07-27: click "Danh sách" mà menu sáng "Đã duyệt". `phase` vừa là
|
||||||
|
// BỘ LỌC trạng thái (nên nằm trong TRANSIENT — fix 2026-05-08) vừa vừa-mới thành
|
||||||
|
// DANH TÍNH điều hướng của "Đã duyệt" (`?type=N&phase=7`). Strip cả 2 vế ⇒ 2 mục
|
||||||
|
// KHÔNG PHÂN BIỆT ĐƯỢC. Quyền quyết thuộc về ĐÍCH: ghim ⇒ so nghiêm, không ghim ⇒
|
||||||
|
// giữ hành vi cũ. (Mirror `fe-user/src/components/Layout.tsx` — cùng bản vá.)
|
||||||
|
const pinnedByTarget = new Set(b.keys())
|
||||||
|
const ignorable = (k: string) => TRANSIENT_QUERY_KEYS.has(k) && !pinnedByTarget.has(k)
|
||||||
|
|
||||||
|
const aKeys = [...a.keys()].filter(k => !ignorable(k)).sort()
|
||||||
|
const bKeys = [...b.keys()].filter(k => !ignorable(k)).sort()
|
||||||
if (aKeys.length !== bKeys.length) return false
|
if (aKeys.length !== bKeys.length) return false
|
||||||
return aKeys.every((k, i) => bKeys[i] === k && a.get(k) === b.get(k))
|
return aKeys.every((k, i) => bKeys[i] === k && a.get(k) === b.get(k))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -140,7 +140,12 @@ function resolvePath(key: string): string | null {
|
|||||||
// [S155 đợt 2] 2 mục MỚI — CẢ HAI tái dùng `PurchaseEvaluationsListPage`, 0 route mới,
|
// [S155 đợt 2] 2 mục MỚI — CẢ HAI tái dùng `PurchaseEvaluationsListPage`, 0 route mới,
|
||||||
// 0 page mới. "Đã duyệt" chỉ là filter phase sẵn có (`DaDuyet = 7`, đã thông 3 tầng);
|
// 0 page mới. "Đã duyệt" chỉ là filter phase sẵn có (`DaDuyet = 7`, đã thông 3 tầng);
|
||||||
// "Đã xóa" là view riêng trong cùng page, gọi endpoint `/purchase-evaluations/deleted`.
|
// "Đã xóa" là view riêng trong cùng page, gọi endpoint `/purchase-evaluations/deleted`.
|
||||||
if (action === 'Approved') return `/purchase-evaluations?type=${typeInt}&phase=7`
|
// 🔴 `view=approved` là KHOÁ ĐỊNH DANH, KHÔNG phải bộ lọc — trang bỏ qua nó, chỉ
|
||||||
|
// `phase=7` mới thật sự lọc. Vì sao phải có: URL của mục "Đã duyệt" TRÙNG HỆT URL
|
||||||
|
// mà user tự lọc trạng thái "Đã duyệt" trên trang Danh sách ⇒ không phân biệt được
|
||||||
|
// bằng URL ⇒ menu sáng nhầm (bug UAT anh báo 2026-07-27). `phase` nằm trong
|
||||||
|
// TRANSIENT (là bộ lọc) nên KHÔNG dùng làm danh tính được; `view` thì không.
|
||||||
|
if (action === 'Approved') return `/purchase-evaluations?type=${typeInt}&phase=7&view=approved`
|
||||||
if (action === 'Deleted') return `/purchase-evaluations?type=${typeInt}&deleted=1`
|
if (action === 'Deleted') return `/purchase-evaluations?type=${typeInt}&deleted=1`
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
@ -277,8 +282,22 @@ const TRANSIENT_QUERY_KEYS = new Set(['id', 'q', 'editHeader', 'page', 'phase',
|
|||||||
function queryMatches(current: string, target: string): boolean {
|
function queryMatches(current: string, target: string): boolean {
|
||||||
const a = new URLSearchParams(current)
|
const a = new URLSearchParams(current)
|
||||||
const b = new URLSearchParams(target)
|
const b = new URLSearchParams(target)
|
||||||
const aKeys = [...a.keys()].filter(k => !TRANSIENT_QUERY_KEYS.has(k)).sort()
|
|
||||||
const bKeys = [...b.keys()].filter(k => !TRANSIENT_QUERY_KEYS.has(k)).sort()
|
// 🔴 [S155 đợt 2] Transient chỉ được bỏ qua khi MENU ĐÍCH KHÔNG tự ghim key đó.
|
||||||
|
// Bug UAT 2026-07-27 (anh báo): click "Danh sách" mà menu sáng "Đã duyệt".
|
||||||
|
// Nguyên nhân: `phase` vừa là BỘ LỌC trạng thái trên trang danh sách (nên nằm
|
||||||
|
// trong TRANSIENT — fix 2026-05-08), vừa vừa-mới trở thành DANH TÍNH điều hướng
|
||||||
|
// của mục "Đã duyệt" (`?type=N&phase=7`, đợt 2). Strip cả 2 vế ⇒ "Danh sách"
|
||||||
|
// (`[type]`) và "Đã duyệt" (`[type]` sau khi mất `phase`) thành KHÔNG PHÂN BIỆT
|
||||||
|
// ĐƯỢC ⇒ URL `?type=1` khớp cả hai, mục render sau thắng.
|
||||||
|
// Cùng một tham số không thể vừa là thứ-bị-bỏ-qua vừa là thứ-định-danh; nên
|
||||||
|
// quyền quyết định thuộc về ĐÍCH: đích ghim `phase` ⇒ so khớp NGHIÊM; đích không
|
||||||
|
// ghim ⇒ giữ nguyên hành vi cũ (lọc/chọn dòng không làm mất highlight).
|
||||||
|
const pinnedByTarget = new Set(b.keys())
|
||||||
|
const ignorable = (k: string) => TRANSIENT_QUERY_KEYS.has(k) && !pinnedByTarget.has(k)
|
||||||
|
|
||||||
|
const aKeys = [...a.keys()].filter(k => !ignorable(k)).sort()
|
||||||
|
const bKeys = [...b.keys()].filter(k => !ignorable(k)).sort()
|
||||||
if (aKeys.length !== bKeys.length) return false
|
if (aKeys.length !== bKeys.length) return false
|
||||||
return aKeys.every((k, i) => bKeys[i] === k && a.get(k) === b.get(k))
|
return aKeys.every((k, i) => bKeys[i] === k && a.get(k) === b.get(k))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user