[CLAUDE] FE-User: tab strip 4 giai doan tren phieu PE + menu 4-GD + danh so 01-07 nhom HD (UAT anh Kiet)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m44s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m44s
- PePipelineStrip moi (mirror x2 app md5-identical): Duyet NCC | Ke hoach ky ket HD | Duyet hop dong | Hop dong cung — chen dau PeDetailTabs (GD1 active) - Menu 4-GD: rename 2 root (PurchaseEvaluations->Duyet NCC, Contracts->Duyet hop dong) + 2 root placeholder KeHoachKyKet/HopDongCung (ngoai MenuKeys.All, staticMap -> /dashboard) — prod da SQL truc tiep cung ngay, seeder+labelBackfill giu hoi tu (gotcha #11) - Danh so 01.-07. nhom HD: prod prepend-only 0-VN-over-wire (renamed=7), seeder typeLabels + labelBackfill 7 entry - Dong bo nhan 4-GD tren dashboard (3 title + 3 group aria) - reviewKeys += 2 placeholder CanRead-only; test 562 PASS; build fe x2 PASS Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@ -18,6 +18,7 @@ import { getErrorMessage } from '@/lib/apiError'
|
|||||||
import { cn } from '@/lib/cn'
|
import { cn } from '@/lib/cn'
|
||||||
import { useAuth } from '@/contexts/AuthContext'
|
import { useAuth } from '@/contexts/AuthContext'
|
||||||
import { AttachmentPreviewDialog, isPreviewable } from './AttachmentPreviewDialog'
|
import { AttachmentPreviewDialog, isPreviewable } from './AttachmentPreviewDialog'
|
||||||
|
import { PePipelineStrip } from './PePipelineStrip'
|
||||||
import {
|
import {
|
||||||
PeAttachmentPurpose,
|
PeAttachmentPurpose,
|
||||||
PeAttachmentPurposeLabel,
|
PeAttachmentPurposeLabel,
|
||||||
@ -272,6 +273,8 @@ export function PeDetailTabs({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-slate-200 bg-white shadow-sm">
|
<div className="rounded-lg border border-slate-200 bg-white shadow-sm">
|
||||||
|
{/* [S159] dải 4 giai đoạn toàn trình — owner vẽ tab lên ảnh phiếu (phiếu PE ⇒ GĐ1 active) */}
|
||||||
|
<PePipelineStrip current={1} />
|
||||||
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-slate-200 px-5 py-3">
|
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-slate-200 px-5 py-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
65
fe-admin/src/components/pe/PePipelineStrip.tsx
Normal file
65
fe-admin/src/components/pe/PePipelineStrip.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// PePipelineStrip — dải 4 giai đoạn toàn trình, đặt trên đầu detail phiếu Duyệt NCC.
|
||||||
|
// [S159 2026-07-29] Owner vẽ tab strip trực tiếp lên ảnh phiếu + chốt tên qua chat:
|
||||||
|
// "Duyệt NCC | Kế hoạch ký kết HĐ | Duyệt hợp đồng | Hợp đồng cứng"
|
||||||
|
// (2 chỉnh verbatim: "Trình ký Hợp đồng đổi thành Duyệt hợp đồng, ký cứng -> Hợp đồng cứng").
|
||||||
|
// MIRROR ×2 app BYTE-IDENTICAL (cùng họ PeDetailTabs md5-parity) — sửa file này
|
||||||
|
// thì sửa CẢ HAI, verify md5 trước commit.
|
||||||
|
// v1: phiếu PE ⇒ giai đoạn 1 active. GĐ2/GĐ4 chưa có module (badge "Sắp");
|
||||||
|
// GĐ3 có module HĐ nhưng CHƯA nối per-phiếu (cầu PE→KHKK→HĐ ship ở wave GĐ2)
|
||||||
|
// ⇒ mọi stage không-active đều KHÔNG click được — hiển thị lộ trình, không giả link.
|
||||||
|
import { Check } from 'lucide-react'
|
||||||
|
import { cn } from '@/lib/cn'
|
||||||
|
|
||||||
|
const STAGES = [
|
||||||
|
{ n: 1, label: 'Duyệt NCC', soon: false },
|
||||||
|
{ n: 2, label: 'Kế hoạch ký kết HĐ', soon: true },
|
||||||
|
{ n: 3, label: 'Duyệt hợp đồng', soon: false },
|
||||||
|
{ n: 4, label: 'Hợp đồng cứng', soon: true },
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export function PePipelineStrip({ current = 1 }: { current?: number }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="list"
|
||||||
|
aria-label="Toàn trình 4 giai đoạn"
|
||||||
|
className="flex items-stretch gap-1 overflow-x-auto rounded-t-lg border-b border-slate-200 bg-slate-50/70 px-2 py-1.5"
|
||||||
|
>
|
||||||
|
{STAGES.map((s, i) => {
|
||||||
|
const active = s.n === current
|
||||||
|
return (
|
||||||
|
<div key={s.n} role="listitem" className="flex min-w-0 flex-1 items-center gap-1">
|
||||||
|
<div
|
||||||
|
aria-current={active ? 'step' : undefined}
|
||||||
|
title={
|
||||||
|
active
|
||||||
|
? 'Giai đoạn hiện tại'
|
||||||
|
: s.soon
|
||||||
|
? 'Sắp triển khai'
|
||||||
|
: 'Sẽ nối khi giai đoạn trước hoàn tất'
|
||||||
|
}
|
||||||
|
className={cn(
|
||||||
|
'flex w-full min-w-0 items-center justify-center gap-1.5 rounded-md px-2 py-1.5 text-center text-[11px] font-medium leading-tight',
|
||||||
|
active
|
||||||
|
? 'bg-brand-600 text-white shadow-sm'
|
||||||
|
: 'border border-dashed border-slate-300 bg-white text-slate-500',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{active && <Check aria-hidden className="h-3 w-3 shrink-0" />}
|
||||||
|
<span className="truncate">{s.label}</span>
|
||||||
|
{s.soon && !active && (
|
||||||
|
<span className="shrink-0 rounded bg-slate-100 px-1 text-[9px] font-semibold uppercase tracking-wide text-slate-400">
|
||||||
|
Sắp
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{i < STAGES.length - 1 && (
|
||||||
|
<span aria-hidden className="shrink-0 px-0.5 text-slate-300">
|
||||||
|
→
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -57,6 +57,11 @@ function resolvePath(key: string): string | null {
|
|||||||
Dashboard: '/dashboard',
|
Dashboard: '/dashboard',
|
||||||
Contracts: '/my-contracts',
|
Contracts: '/my-contracts',
|
||||||
PurchaseEvaluations: '/purchase-evaluations',
|
PurchaseEvaluations: '/purchase-evaluations',
|
||||||
|
// [S159 2026-07-29] 2 root placeholder giai đoạn 2/4 (owner: menu hiển thị đúng 4 GĐ
|
||||||
|
// như tab strip). Module chưa build → trỏ về Tổng quan pipeline (khung "Sắp triển khai").
|
||||||
|
// LESSON gotcha #50: key DB mới PHẢI có route ở đây, không thì sidebar drop SILENT.
|
||||||
|
KeHoachKyKet: '/dashboard',
|
||||||
|
HopDongCung: '/dashboard',
|
||||||
// [Plan CA Hotfix 1 S29 2026-05-22] 4 master + 4 catalog leaf moved từ
|
// [Plan CA Hotfix 1 S29 2026-05-22] 4 master + 4 catalog leaf moved từ
|
||||||
// fe-admin → fe-user. resolvePath PHẢI có route mapping nếu không
|
// fe-admin → fe-user. resolvePath PHẢI có route mapping nếu không
|
||||||
// MenuLeaf line 238 `if (!path) return null` → sidebar drop silent.
|
// MenuLeaf line 238 `if (!path) return null` → sidebar drop silent.
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import { getErrorMessage } from '@/lib/apiError'
|
|||||||
import { cn } from '@/lib/cn'
|
import { cn } from '@/lib/cn'
|
||||||
import { useAuth } from '@/contexts/AuthContext'
|
import { useAuth } from '@/contexts/AuthContext'
|
||||||
import { AttachmentPreviewDialog, isPreviewable } from './AttachmentPreviewDialog'
|
import { AttachmentPreviewDialog, isPreviewable } from './AttachmentPreviewDialog'
|
||||||
|
import { PePipelineStrip } from './PePipelineStrip'
|
||||||
import {
|
import {
|
||||||
PeAttachmentPurpose,
|
PeAttachmentPurpose,
|
||||||
PeAttachmentPurposeLabel,
|
PeAttachmentPurposeLabel,
|
||||||
@ -272,6 +273,8 @@ export function PeDetailTabs({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="rounded-lg border border-slate-200 bg-white shadow-sm">
|
<div className="rounded-lg border border-slate-200 bg-white shadow-sm">
|
||||||
|
{/* [S159] dải 4 giai đoạn toàn trình — owner vẽ tab lên ảnh phiếu (phiếu PE ⇒ GĐ1 active) */}
|
||||||
|
<PePipelineStrip current={1} />
|
||||||
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-slate-200 px-5 py-3">
|
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-slate-200 px-5 py-3">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
65
fe-user/src/components/pe/PePipelineStrip.tsx
Normal file
65
fe-user/src/components/pe/PePipelineStrip.tsx
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// PePipelineStrip — dải 4 giai đoạn toàn trình, đặt trên đầu detail phiếu Duyệt NCC.
|
||||||
|
// [S159 2026-07-29] Owner vẽ tab strip trực tiếp lên ảnh phiếu + chốt tên qua chat:
|
||||||
|
// "Duyệt NCC | Kế hoạch ký kết HĐ | Duyệt hợp đồng | Hợp đồng cứng"
|
||||||
|
// (2 chỉnh verbatim: "Trình ký Hợp đồng đổi thành Duyệt hợp đồng, ký cứng -> Hợp đồng cứng").
|
||||||
|
// MIRROR ×2 app BYTE-IDENTICAL (cùng họ PeDetailTabs md5-parity) — sửa file này
|
||||||
|
// thì sửa CẢ HAI, verify md5 trước commit.
|
||||||
|
// v1: phiếu PE ⇒ giai đoạn 1 active. GĐ2/GĐ4 chưa có module (badge "Sắp");
|
||||||
|
// GĐ3 có module HĐ nhưng CHƯA nối per-phiếu (cầu PE→KHKK→HĐ ship ở wave GĐ2)
|
||||||
|
// ⇒ mọi stage không-active đều KHÔNG click được — hiển thị lộ trình, không giả link.
|
||||||
|
import { Check } from 'lucide-react'
|
||||||
|
import { cn } from '@/lib/cn'
|
||||||
|
|
||||||
|
const STAGES = [
|
||||||
|
{ n: 1, label: 'Duyệt NCC', soon: false },
|
||||||
|
{ n: 2, label: 'Kế hoạch ký kết HĐ', soon: true },
|
||||||
|
{ n: 3, label: 'Duyệt hợp đồng', soon: false },
|
||||||
|
{ n: 4, label: 'Hợp đồng cứng', soon: true },
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export function PePipelineStrip({ current = 1 }: { current?: number }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
role="list"
|
||||||
|
aria-label="Toàn trình 4 giai đoạn"
|
||||||
|
className="flex items-stretch gap-1 overflow-x-auto rounded-t-lg border-b border-slate-200 bg-slate-50/70 px-2 py-1.5"
|
||||||
|
>
|
||||||
|
{STAGES.map((s, i) => {
|
||||||
|
const active = s.n === current
|
||||||
|
return (
|
||||||
|
<div key={s.n} role="listitem" className="flex min-w-0 flex-1 items-center gap-1">
|
||||||
|
<div
|
||||||
|
aria-current={active ? 'step' : undefined}
|
||||||
|
title={
|
||||||
|
active
|
||||||
|
? 'Giai đoạn hiện tại'
|
||||||
|
: s.soon
|
||||||
|
? 'Sắp triển khai'
|
||||||
|
: 'Sẽ nối khi giai đoạn trước hoàn tất'
|
||||||
|
}
|
||||||
|
className={cn(
|
||||||
|
'flex w-full min-w-0 items-center justify-center gap-1.5 rounded-md px-2 py-1.5 text-center text-[11px] font-medium leading-tight',
|
||||||
|
active
|
||||||
|
? 'bg-brand-600 text-white shadow-sm'
|
||||||
|
: 'border border-dashed border-slate-300 bg-white text-slate-500',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{active && <Check aria-hidden className="h-3 w-3 shrink-0" />}
|
||||||
|
<span className="truncate">{s.label}</span>
|
||||||
|
{s.soon && !active && (
|
||||||
|
<span className="shrink-0 rounded bg-slate-100 px-1 text-[9px] font-semibold uppercase tracking-wide text-slate-400">
|
||||||
|
Sắp
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{i < STAGES.length - 1 && (
|
||||||
|
<span aria-hidden className="shrink-0 px-0.5 text-slate-300">
|
||||||
|
→
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -2,9 +2,9 @@
|
|||||||
// [S159 2026-07-29 — owner chốt qua AskUser] Hero = dải pipeline 4 GIAI ĐOẠN xuyên
|
// [S159 2026-07-29 — owner chốt qua AskUser] Hero = dải pipeline 4 GIAI ĐOẠN xuyên
|
||||||
// suốt quy trình mua sắm → ký kết hợp đồng:
|
// suốt quy trình mua sắm → ký kết hợp đồng:
|
||||||
// GĐ1 Duyệt NCC (bước 1 → 6, CEO chốt) 🟢 LIVE — module PE
|
// GĐ1 Duyệt NCC (bước 1 → 6, CEO chốt) 🟢 LIVE — module PE
|
||||||
// GĐ2 Đề xuất ký kết hợp đồng (bước 7 → 12) ⏳ sắp triển khai
|
// GĐ2 Kế hoạch ký kết HĐ (bước 7 → 12) ⏳ sắp triển khai
|
||||||
// GĐ3 Duyệt Hợp đồng (bước 13 → 18) 🟢 LIVE — module Contract
|
// GĐ3 Duyệt hợp đồng (bước 13 → 18) 🟢 LIVE — module Contract
|
||||||
// GĐ4 Ký cứng & phát hành (bước 19 → 21) ⏳ sắp triển khai
|
// GĐ4 Hợp đồng cứng (bước 19 → 21) ⏳ sắp triển khai
|
||||||
// Dưới hero GIỮ nguyên KPI cá nhân + 2 danh sách gần đây (chỉ redesign cho hài hoà).
|
// Dưới hero GIỮ nguyên KPI cá nhân + 2 danh sách gần đây (chỉ redesign cho hài hoà).
|
||||||
// fe-user ONLY — KHÔNG mirror sang fe-admin (trang này là UX riêng của user).
|
// fe-user ONLY — KHÔNG mirror sang fe-admin (trang này là UX riêng của user).
|
||||||
//
|
//
|
||||||
@ -473,7 +473,7 @@ export function UserDashboardPage() {
|
|||||||
<StageArrow />
|
<StageArrow />
|
||||||
|
|
||||||
{/* GĐ2 — sắp triển khai */}
|
{/* GĐ2 — sắp triển khai */}
|
||||||
<StageCard index={2} title="Đề xuất ký kết hợp đồng" stepRange="Bước 7 → 12" icon={FileSignature}>
|
<StageCard index={2} title="Kế hoạch ký kết HĐ" stepRange="Bước 7 → 12" icon={FileSignature}>
|
||||||
<StageSoon
|
<StageSoon
|
||||||
description="Phiếu nối tiếp từ phiếu Duyệt NCC đã duyệt — chốt giá trị ký kết."
|
description="Phiếu nối tiếp từ phiếu Duyệt NCC đã duyệt — chốt giá trị ký kết."
|
||||||
hint={
|
hint={
|
||||||
@ -489,14 +489,14 @@ export function UserDashboardPage() {
|
|||||||
{/* GĐ3 — LIVE */}
|
{/* GĐ3 — LIVE */}
|
||||||
<StageCard
|
<StageCard
|
||||||
index={3}
|
index={3}
|
||||||
title="Duyệt Hợp đồng"
|
title="Duyệt hợp đồng"
|
||||||
stepRange="Bước 13 → 18"
|
stepRange="Bước 13 → 18"
|
||||||
icon={FileText}
|
icon={FileText}
|
||||||
tone="teal"
|
tone="teal"
|
||||||
>
|
>
|
||||||
<div className="space-y-0.5">
|
<div className="space-y-0.5">
|
||||||
<StageStatRow
|
<StageStatRow
|
||||||
group="Duyệt Hợp đồng"
|
group="Duyệt hợp đồng"
|
||||||
label="Tổng hợp đồng"
|
label="Tổng hợp đồng"
|
||||||
value={recent.data?.total}
|
value={recent.data?.total}
|
||||||
tone="teal"
|
tone="teal"
|
||||||
@ -505,7 +505,7 @@ export function UserDashboardPage() {
|
|||||||
onClick={() => navigate('/my-contracts')}
|
onClick={() => navigate('/my-contracts')}
|
||||||
/>
|
/>
|
||||||
<StageStatRow
|
<StageStatRow
|
||||||
group="Duyệt Hợp đồng"
|
group="Duyệt hợp đồng"
|
||||||
label="Chờ tôi duyệt"
|
label="Chờ tôi duyệt"
|
||||||
value={s?.pendingMyApproval}
|
value={s?.pendingMyApproval}
|
||||||
tone="teal"
|
tone="teal"
|
||||||
@ -515,7 +515,7 @@ export function UserDashboardPage() {
|
|||||||
onClick={() => navigate('/inbox')}
|
onClick={() => navigate('/inbox')}
|
||||||
/>
|
/>
|
||||||
<StageStatRow
|
<StageStatRow
|
||||||
group="Duyệt Hợp đồng"
|
group="Duyệt hợp đồng"
|
||||||
label="Đã quá hạn"
|
label="Đã quá hạn"
|
||||||
value={s?.overdue}
|
value={s?.overdue}
|
||||||
tone="teal"
|
tone="teal"
|
||||||
@ -531,7 +531,7 @@ export function UserDashboardPage() {
|
|||||||
<StageArrow />
|
<StageArrow />
|
||||||
|
|
||||||
{/* GĐ4 — sắp triển khai */}
|
{/* GĐ4 — sắp triển khai */}
|
||||||
<StageCard index={4} title="Ký cứng & phát hành" stepRange="Bước 19 → 21" icon={Stamp}>
|
<StageCard index={4} title="Hợp đồng cứng" stepRange="Bước 19 → 21" icon={Stamp}>
|
||||||
<StageSoon description="Tải lên bản hợp đồng đã ký cứng." />
|
<StageSoon description="Tải lên bản hợp đồng đã ký cứng." />
|
||||||
</StageCard>
|
</StageCard>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -34,6 +34,13 @@ public static class MenuKeys
|
|||||||
public static readonly string[] ContractTypeCodes =
|
public static readonly string[] ContractTypeCodes =
|
||||||
["ThauPhu", "GiaoKhoan", "NhaCungCap", "DichVu", "MuaBan", "NguyenTacNcc", "NguyenTacDv"];
|
["ThauPhu", "GiaoKhoan", "NhaCungCap", "DichVu", "MuaBan", "NguyenTacNcc", "NguyenTacDv"];
|
||||||
|
|
||||||
|
// [S159 2026-07-29] 2 root PLACEHOLDER giai đoạn 2/4 (owner: menu hiển thị đúng
|
||||||
|
// 4 giai đoạn như tab strip). CỐ Ý NGOÀI `All` — chưa có module ⇒ không sinh
|
||||||
|
// policy per-action (giữ canonical Menu keys/Policies không drift); FE trỏ về
|
||||||
|
// /dashboard. Wave-1 GĐ2 (spec KHKK) phải TÁI DÙNG/thay root `KeHoachKyKet` này.
|
||||||
|
public const string KeHoachKyKet = "KeHoachKyKet";
|
||||||
|
public const string HopDongCung = "HopDongCung";
|
||||||
|
|
||||||
public static string ContractTypeGroup(string typeCode) => $"Ct_{typeCode}";
|
public static string ContractTypeGroup(string typeCode) => $"Ct_{typeCode}";
|
||||||
public static string ContractTypeList(string typeCode) => $"Ct_{typeCode}_List";
|
public static string ContractTypeList(string typeCode) => $"Ct_{typeCode}_List";
|
||||||
public static string ContractTypeCreate(string typeCode) => $"Ct_{typeCode}_Create";
|
public static string ContractTypeCreate(string typeCode) => $"Ct_{typeCode}_Create";
|
||||||
|
|||||||
@ -1736,15 +1736,19 @@ public static class DbInitializer
|
|||||||
|
|
||||||
private static async Task SeedMenuTreeAsync(ApplicationDbContext db, ILogger logger)
|
private static async Task SeedMenuTreeAsync(ApplicationDbContext db, ILogger logger)
|
||||||
{
|
{
|
||||||
|
// [S159 2026-07-29] UAT anh Kiệt: "cho đánh số thứ tự phía trước giúp anh — 01. HĐ thầu phụ"
|
||||||
|
// ⇒ prefix 01.-07. theo THỨ TỰ hiển thị (= thứ tự ContractTypeCodes). Prod đã UPDATE
|
||||||
|
// trực tiếp cùng ngày (prepend-only, 0 ký tự VN qua wire); labelBackfill dưới giữ
|
||||||
|
// các env khác hội tụ (gotcha #11 — sửa tree.Add một mình là vô tác dụng trên DB đang chạy).
|
||||||
var typeLabels = new Dictionary<string, string>
|
var typeLabels = new Dictionary<string, string>
|
||||||
{
|
{
|
||||||
["ThauPhu"] = "HĐ Thầu phụ",
|
["ThauPhu"] = "01. HĐ Thầu phụ",
|
||||||
["GiaoKhoan"] = "HĐ Giao khoán",
|
["GiaoKhoan"] = "02. HĐ Giao khoán",
|
||||||
["NhaCungCap"] = "HĐ Nhà cung cấp",
|
["NhaCungCap"] = "03. HĐ Nhà cung cấp",
|
||||||
["DichVu"] = "HĐ Dịch vụ",
|
["DichVu"] = "04. HĐ Dịch vụ",
|
||||||
["MuaBan"] = "HĐ Mua bán",
|
["MuaBan"] = "05. HĐ Mua bán",
|
||||||
["NguyenTacNcc"] = "HĐ Nguyên tắc NCC",
|
["NguyenTacNcc"] = "06. HĐ Nguyên tắc NCC",
|
||||||
["NguyenTacDv"] = "HĐ Nguyên tắc Dịch vụ",
|
["NguyenTacDv"] = "07. HĐ Nguyên tắc Dịch vụ",
|
||||||
};
|
};
|
||||||
|
|
||||||
var tree = new List<(string Key, string Label, string? Parent, int Order, string Icon)>
|
var tree = new List<(string Key, string Label, string? Parent, int Order, string Icon)>
|
||||||
@ -1764,7 +1768,11 @@ public static class DbInitializer
|
|||||||
(MenuKeys.CatalogServices, "Dịch vụ", MenuKeys.Catalogs, 3, "Wrench"),
|
(MenuKeys.CatalogServices, "Dịch vụ", MenuKeys.Catalogs, 3, "Wrench"),
|
||||||
(MenuKeys.CatalogWorkItems,"Hạng mục công việc", MenuKeys.Catalogs, 4, "ListChecks"),
|
(MenuKeys.CatalogWorkItems,"Hạng mục công việc", MenuKeys.Catalogs, 4, "ListChecks"),
|
||||||
// [S57] Order 30→31: nhường slot 30 cho nhóm "Cá nhân" (đứng ngay sau Văn phòng số = 29, mirror Puro).
|
// [S57] Order 30→31: nhường slot 30 cho nhóm "Cá nhân" (đứng ngay sau Văn phòng số = 29, mirror Puro).
|
||||||
(MenuKeys.Contracts, "Hợp đồng", null, 31, "FileText"),
|
(MenuKeys.Contracts, "Duyệt hợp đồng", null, 31, "FileText"), // [S159] rename theo 4-GĐ (owner tab strip)
|
||||||
|
// [S159] 2 root PLACEHOLDER GĐ2/GĐ4 — menu = 4 giai đoạn (owner). Ngoài MenuKeys.All,
|
||||||
|
// FE staticMap trỏ /dashboard. Prod đã INSERT trực tiếp cùng ngày (idempotent ở đây).
|
||||||
|
(MenuKeys.KeHoachKyKet, "Kế hoạch ký kết HĐ", null, 26, "FileSignature"),
|
||||||
|
(MenuKeys.HopDongCung, "Hợp đồng cứng", null, 32, "Stamp"),
|
||||||
(MenuKeys.Forms, "Biểu mẫu", null, 40, "FileSpreadsheet"),
|
(MenuKeys.Forms, "Biểu mẫu", null, 40, "FileSpreadsheet"),
|
||||||
(MenuKeys.Reports, "Báo cáo", null, 50, "BarChart3"),
|
(MenuKeys.Reports, "Báo cáo", null, 50, "BarChart3"),
|
||||||
(MenuKeys.System, "Hệ thống", null, 90, "Settings"),
|
(MenuKeys.System, "Hệ thống", null, 90, "Settings"),
|
||||||
@ -1774,7 +1782,7 @@ public static class DbInitializer
|
|||||||
(MenuKeys.MenuVisibility, "Menu eOffice", MenuKeys.System, 94, "Eye"),
|
(MenuKeys.MenuVisibility, "Menu eOffice", MenuKeys.System, 94, "Eye"),
|
||||||
(MenuKeys.Workflows, "Quy trình HĐ", MenuKeys.System, 95, "GitBranch"),
|
(MenuKeys.Workflows, "Quy trình HĐ", MenuKeys.System, 95, "GitBranch"),
|
||||||
// Module Duyệt NCC (tiền-HĐ)
|
// Module Duyệt NCC (tiền-HĐ)
|
||||||
(MenuKeys.PurchaseEvaluations, "Quy trình chọn Thầu phụ - NCC", null, 25, "ClipboardCheck"),
|
(MenuKeys.PurchaseEvaluations, "Duyệt NCC", null, 25, "ClipboardCheck"), // [S159] rename theo 4-GĐ
|
||||||
(MenuKeys.PeWorkflows, "Quy trình Duyệt NCC", MenuKeys.System, 95, "GitCompareArrows"),
|
(MenuKeys.PeWorkflows, "Quy trình Duyệt NCC", MenuKeys.System, 95, "GitCompareArrows"),
|
||||||
// Quy trình duyệt MỚI (Mig 22 — Session 17, 2026-05-08): schema riêng
|
// Quy trình duyệt MỚI (Mig 22 — Session 17, 2026-05-08): schema riêng
|
||||||
// UAT trước khi migrate hoàn toàn. Cấu trúc: Quy trình > Bước (Phòng) > Cấp (NV cụ thể).
|
// UAT trước khi migrate hoàn toàn. Cấu trúc: Quy trình > Bước (Phòng) > Cấp (NV cụ thể).
|
||||||
@ -1932,6 +1940,19 @@ public static class DbInitializer
|
|||||||
// chạy vì upsert `:1893-1905` chỉ update `Order` cho key đã tồn tại.
|
// chạy vì upsert `:1893-1905` chỉ update `Order` cho key đã tồn tại.
|
||||||
[MenuKeys.PurchaseEvaluationPending("DuyetNcc")] = "Đang duyệt",
|
[MenuKeys.PurchaseEvaluationPending("DuyetNcc")] = "Đang duyệt",
|
||||||
[MenuKeys.PurchaseEvaluationPending("DuyetNccPhuongAn")] = "Đang duyệt",
|
[MenuKeys.PurchaseEvaluationPending("DuyetNccPhuongAn")] = "Đang duyệt",
|
||||||
|
// [S159] 🔴 UAT anh Kiệt: đánh số 01.-07. nhóm HĐ (verbatim "01 . HĐ thầu phụ").
|
||||||
|
// Prod đã prepend trực tiếp cùng ngày ⇒ trên prod các entry này NO-OP (equal);
|
||||||
|
// dev/fresh-DB hội tụ qua đây. Nguồn số = thứ tự ContractTypeCodes (= [Order]).
|
||||||
|
// [S159] 2 root rename theo 4-GĐ (prod đã SQL cùng ngày ⇒ NO-OP trên prod).
|
||||||
|
[MenuKeys.PurchaseEvaluations] = "Duyệt NCC",
|
||||||
|
[MenuKeys.Contracts] = "Duyệt hợp đồng",
|
||||||
|
[MenuKeys.ContractTypeGroup("ThauPhu")] = "01. HĐ Thầu phụ",
|
||||||
|
[MenuKeys.ContractTypeGroup("GiaoKhoan")] = "02. HĐ Giao khoán",
|
||||||
|
[MenuKeys.ContractTypeGroup("NhaCungCap")] = "03. HĐ Nhà cung cấp",
|
||||||
|
[MenuKeys.ContractTypeGroup("DichVu")] = "04. HĐ Dịch vụ",
|
||||||
|
[MenuKeys.ContractTypeGroup("MuaBan")] = "05. HĐ Mua bán",
|
||||||
|
[MenuKeys.ContractTypeGroup("NguyenTacNcc")] = "06. HĐ Nguyên tắc NCC",
|
||||||
|
[MenuKeys.ContractTypeGroup("NguyenTacDv")] = "07. HĐ Nguyên tắc Dịch vụ",
|
||||||
};
|
};
|
||||||
var updatedLabels = 0;
|
var updatedLabels = 0;
|
||||||
foreach (var (key, expectedLabel) in labelBackfill)
|
foreach (var (key, expectedLabel) in labelBackfill)
|
||||||
@ -2113,6 +2134,9 @@ public static class DbInitializer
|
|||||||
|
|
||||||
var reviewKeys = MenuKeys.All.Where(InReviewScope)
|
var reviewKeys = MenuKeys.All.Where(InReviewScope)
|
||||||
.Concat(peKeys)
|
.Concat(peKeys)
|
||||||
|
// [S159] 2 root placeholder GĐ2/GĐ4 — CanRead-only mọi role (IsPeKey=false
|
||||||
|
// ⇒ nhánh read-only). Prod đã INSERT trực tiếp; đây giữ fresh-DB hội tụ.
|
||||||
|
.Concat(new[] { MenuKeys.KeHoachKyKet, MenuKeys.HopDongCung })
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
var roles = await roleManager.Roles.ToListAsync();
|
var roles = await roleManager.Roles.ToListAsync();
|
||||||
|
|||||||
Reference in New Issue
Block a user