[CLAUDE] Domain+Infra+App+FE-Admin: per-ContractType nested sidebar menu
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m48s
User request: mỗi loại HĐ có menu riêng với 3 action Danh sách / Thao tác / Duyệt. Sidebar giờ 3-level under "Hợp đồng": Hợp đồng (group, expandable) ├── HĐ Thầu phụ (sub-group) │ ├── Danh sách → /contracts?type=1 │ ├── Thao tác → /contracts/new?type=1 │ └── Duyệt → /contracts?type=1&pendingMe=1 ├── HĐ Giao khoán (sub-group) ├── HĐ NCC / Dịch vụ / Mua bán / Nguyên tắc NCC / Nguyên tắc DV └── ... (7 types × 4 = 28 new menu items) BE: - MenuKeys.cs: ContractTypeCodes array + helpers ContractTypeGroup/ List/Create/Pending → key format Ct_<TypeCode>[_<Action>] - DbInitializer.SeedMenuTreeAsync: loop seeds 28 entries under Contracts - GetMyMenuTreeQuery.BuildChildren: descendants of `Contracts` inherit parent permission (avoid adding 28 rows to Permissions table per role) FE: - Layout.tsx recursive: MenuNodeRenderer dispatches group vs leaf by depth; nested groups collapsed by default (top-level expanded). Deeper levels get smaller padding/text + left border guide. - Pattern-based resolvePath: Ct_<Type>_<Action> → URL with query. - Contract type code → int map (matches Domain ContractType enum). - ContractsListPage reads ?type + ?pendingMe, filters client-side. Header title + description reflect active filter. "← Tất cả loại" quick-reset button. - ContractCreatePage new cho admin (copy từ fe-user), pre-select type từ ?type URL param. - App.tsx route /contracts/new → ContractCreatePage. Pure navigation UX; no new permissions needed. Admin + any role with Contracts.Read see full menu; leaves click-through to filtered views. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -88,7 +88,18 @@ public static class DbInitializer
|
||||
|
||||
private static async Task SeedMenuTreeAsync(ApplicationDbContext db, ILogger logger)
|
||||
{
|
||||
var tree = new (string Key, string Label, string? Parent, int Order, string Icon)[]
|
||||
var typeLabels = new Dictionary<string, string>
|
||||
{
|
||||
["ThauPhu"] = "HĐ Thầu phụ",
|
||||
["GiaoKhoan"] = "HĐ Giao khoán",
|
||||
["NhaCungCap"] = "HĐ Nhà cung cấp",
|
||||
["DichVu"] = "HĐ Dịch vụ",
|
||||
["MuaBan"] = "HĐ Mua bán",
|
||||
["NguyenTacNcc"] = "HĐ Nguyên tắc NCC",
|
||||
["NguyenTacDv"] = "HĐ Nguyên tắc Dịch vụ",
|
||||
};
|
||||
|
||||
var tree = new List<(string Key, string Label, string? Parent, int Order, string Icon)>
|
||||
{
|
||||
(MenuKeys.Dashboard, "Tổng quan", null, 10, "LayoutDashboard"),
|
||||
(MenuKeys.Master, "Danh mục", null, 20, "Database"),
|
||||
@ -104,12 +115,24 @@ public static class DbInitializer
|
||||
(MenuKeys.Permissions, "Phân quyền", MenuKeys.System, 93, "KeyRound"),
|
||||
};
|
||||
|
||||
// Per-type sub-menu under Contracts: 1 group + 3 leaves each
|
||||
// (Danh sách / Thao tác / Duyệt).
|
||||
var order = 31;
|
||||
foreach (var code in MenuKeys.ContractTypeCodes)
|
||||
{
|
||||
var label = typeLabels.GetValueOrDefault(code, code);
|
||||
tree.Add((MenuKeys.ContractTypeGroup(code), label, MenuKeys.Contracts, order++, "FileText"));
|
||||
tree.Add((MenuKeys.ContractTypeList(code), "Danh sách", MenuKeys.ContractTypeGroup(code), order++, "List"));
|
||||
tree.Add((MenuKeys.ContractTypeCreate(code), "Thao tác", MenuKeys.ContractTypeGroup(code), order++, "Plus"));
|
||||
tree.Add((MenuKeys.ContractTypePending(code),"Duyệt", MenuKeys.ContractTypeGroup(code), order++, "CheckCircle2"));
|
||||
}
|
||||
|
||||
var existingKeys = await db.MenuItems.Select(m => m.Key).ToListAsync();
|
||||
var added = 0;
|
||||
foreach (var (key, label, parent, order, icon) in tree)
|
||||
foreach (var (key, label, parent, o, icon) in tree)
|
||||
{
|
||||
if (existingKeys.Contains(key)) continue;
|
||||
db.MenuItems.Add(new MenuItem { Key = key, Label = label, ParentKey = parent, Order = order, Icon = icon });
|
||||
db.MenuItems.Add(new MenuItem { Key = key, Label = label, ParentKey = parent, Order = o, Icon = icon });
|
||||
added++;
|
||||
}
|
||||
if (added > 0)
|
||||
|
||||
Reference in New Issue
Block a user