[CLAUDE] Infra: go nhanh S92 khoi revoker (owner supersede: hien thi het) + skeleton menu GD2/GD4 + ComingSoonPage x2
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m56s

- RevokeTemporarilyHiddenModulesAsync: GO Contracts+Ct_*/Master-set khoi predicate (bai tra gia: grant SQL tay bi revoker lat o restart #423 - 447/494 row roi; regrant=447 da chay truoc commit nay). Hrm/Off/Personal GIU (y owner: vung dang-lam chi admin)
- reviewKeys += ContractMenuKeys + MasterMenuKeys + KhkkKeys + HdcKeys (CanRead-only all-role, fresh-env hoi tu)
- Seeder tree.Add: Khkk_G1 + 4 leaves (khuon Duyet NCC) + Hdc_x7 (tai dung nhan 01.-07.) - prod da INSERT truc tiep (perm=156)
- ComingSoonPage mirror x2 (md5-identical) + route /coming-soon x2 + staticMap 11 leaf x2 (gotcha #50)
- Test: AdminOnlyModulesRevokeTests tach StillHidden/Reopened + REGRESSION-GUARD S159 (re-add nhanh S92 => do); ProcurementMasterAccessSeedTests doc lai isolation bang CO CAO (spec-change S92->S159, update test + code chung commit)
- 562 test PASS - nghiem thu cuoi = restart THAT khi deploy run nay (khuon S91)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-07-29 12:34:15 +07:00
parent b5a7dd8a92
commit 2a7269581c
9 changed files with 234 additions and 27 deletions

View File

@ -5,6 +5,7 @@ import { ProtectedRoute } from '@/components/ProtectedRoute'
import { Layout } from '@/components/Layout'
import { LoginPage } from '@/pages/LoginPage'
import { UserDashboardPage } from '@/pages/UserDashboardPage'
import { ComingSoonPage } from '@/pages/ComingSoonPage'
import { InboxPage } from '@/pages/InboxPage'
import { SuppliersPage } from '@/pages/master/SuppliersPage'
import { ProjectsPage } from '@/pages/master/ProjectsPage'
@ -47,6 +48,8 @@ function App() {
}
>
<Route path="/dashboard" element={<UserDashboardPage />} />
{/* [S159] trang dich cho menu skeleton GD2/GD4 */}
<Route path="/coming-soon" element={<ComingSoonPage />} />
<Route path="/inbox" element={<InboxPage />} />
<Route path="/master/suppliers" element={<SuppliersPage />} />
<Route path="/master/projects" element={<ProjectsPage />} />

View File

@ -54,6 +54,19 @@ const INT_TO_PE_CODE: Record<number, string> = Object.fromEntries(
// /my-contracts (user's own drafts), Duyệt to /inbox (pending THEIR approval).
function resolvePath(key: string): string | null {
const staticMap: Record<string, string> = {
// [S159-đợt3] skeleton GĐ2/GĐ4 — leaf trỏ trang "Sắp triển khai" (query giữ
// active-state riêng từng mục; gotcha #50: thiếu route = sidebar drop SILENT).
Khkk_List: '/coming-soon?stage=2&m=list',
Khkk_Create: '/coming-soon?stage=2&m=create',
Khkk_Pending: '/coming-soon?stage=2&m=pending',
Khkk_Approved: '/coming-soon?stage=2&m=approved',
Hdc_ThauPhu: '/coming-soon?stage=4&t=01',
Hdc_GiaoKhoan: '/coming-soon?stage=4&t=02',
Hdc_NhaCungCap: '/coming-soon?stage=4&t=03',
Hdc_DichVu: '/coming-soon?stage=4&t=04',
Hdc_MuaBan: '/coming-soon?stage=4&t=05',
Hdc_NguyenTacNcc: '/coming-soon?stage=4&t=06',
Hdc_NguyenTacDv: '/coming-soon?stage=4&t=07',
Dashboard: '/dashboard',
Contracts: '/my-contracts',
PurchaseEvaluations: '/purchase-evaluations',

View File

@ -0,0 +1,54 @@
// ComingSoonPage — trang đích cho các mục menu skeleton GĐ2/GĐ4 (module chưa build).
// [S159 2026-07-29] Owner: menu hiển thị đủ 4 giai đoạn + phân list như Duyệt NCC
// NGAY TỪ BÂY GIỜ để mọi người góp ý — leaf chưa có module trỏ về đây thay vì
// link chết. MIRROR ×2 app BYTE-IDENTICAL (nút quay lại dùng navigate(-1) để
// không phụ thuộc route riêng từng app).
import { useNavigate, useSearchParams } from 'react-router-dom'
import { ArrowLeft, Hourglass } from 'lucide-react'
import { Button } from '@/components/ui/Button'
import { PePipelineStrip } from '@/components/pe/PePipelineStrip'
const STAGE_INFO: Record<string, { name: string; steps: string; desc: string }> = {
'2': {
name: 'Kế hoạch ký kết HĐ',
steps: 'Bước 7 → 12',
desc: 'Phiếu nối tiếp từ phiếu Duyệt NCC đã duyệt — duyệt mẫu/shopdrawing, so sánh giá và chốt giá trị ký kết hợp đồng.',
},
'4': {
name: 'Hợp đồng cứng',
steps: 'Bước 19 → 21',
desc: 'Lưu bản hợp đồng đã ký cứng (tải lên bản scan sau khi ký, đóng dấu và phát hành).',
},
}
export function ComingSoonPage() {
const navigate = useNavigate()
const [params] = useSearchParams()
const stage = params.get('stage') ?? '2'
const info = STAGE_INFO[stage] ?? STAGE_INFO['2']
return (
<div className="p-6">
<div className="mx-auto max-w-3xl rounded-lg border border-slate-200 bg-white shadow-sm">
<PePipelineStrip current={Number(stage)} />
<div className="flex flex-col items-center px-6 py-10 text-center">
<span className="icon-chip mb-3">
<Hourglass className="h-4 w-4" />
</span>
<h2 className="text-lg font-semibold text-slate-900">{info.name}</h2>
<div className="mt-1 text-xs font-medium text-slate-500">
Giai đoạn {stage} · {info.steps}
</div>
<p className="mt-3 max-w-md text-sm leading-relaxed text-slate-600">{info.desc}</p>
<p className="mt-2 rounded-md bg-amber-50 px-3 py-1.5 text-xs font-medium text-amber-700">
Phân hệ đang đưc triển khai cấu trúc menu hiển thị trước đ mọi người góp ý.
</p>
<Button variant="outline" size="sm" className="mt-5" onClick={() => navigate(-1)}>
<ArrowLeft className="h-3.5 w-3.5" />
Quay lại
</Button>
</div>
</div>
</div>
)
}