[CLAUDE] Docs: chốt Session 20 turn 7 — Admin Ẩn/Hiện + Đổi tên menu eOffice (Chunk E)
Wrap-up docs cho 4 chunk code đã push: -2ea2d27Chunk A — Mig 27 MenuItem +IsVisible +DisplayLabel + 3-file rule -ef394f8Chunk B — BE PATCH /menus/{key} + extend DTOs + UpdateMenuItemCommand -059bfcbChunk C — FE Admin MenuVisibilityPage ~210 LOC + menu key + seed -1ed6530Chunk D — FE User Layout filter !isVisible + render effectiveLabel Files updated: - docs/STATUS.md — Last updated + Recently Done row S20 turn 7 trên cùng (giữ S20 PE Detail UI row nguyên văn §6.5) - docs/HANDOFF.md — Last updated + TL;DR Session 20 turn 7 trên đầu + pending S21+ + carry blockers (giữ TL;DR Session 20 + 19 nguyên §6.5) - docs/changelog/migration-todos.md — Phase 9 Session 20 turn 7 done section + 3 defer item S21+ (giữ S20 + S19 nguyên §6.5) - docs/changelog/sessions/2026-05-11-1700-menu-visibility-mig27.md (NEW) — session log đầy đủ Q&A + 4 chunk + verify chain + stats KHÔNG đụng rules / architecture / PROJECT-MAP / workflow-contract / forms-spec / database-guide / schema-diagram / CLAUDE.md per §6.5 (drift S20 turn 7 defer cron audit 2026-06-01 — Mig 27 + 1 endpoint + 1 menu key sẽ check chung lúc đó). Path filter CI sẽ skip (docs-only commit). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -157,6 +157,28 @@ Session log: `2026-04-28-chot-session-4-budget.md`.
|
||||
|
||||
## 📝 Phase 9 — UAT + Ops + carry over (Session 6+ active)
|
||||
|
||||
### ✅ Session 20 turn 7 done (2026-05-11) — Admin Ẩn/Hiện + Đổi tên menu eOffice (Mig 27, 5 chunk `2ea2d27` → `ef394f8` → `059bfcb` → `1ed6530` → Chunk E Docs)
|
||||
|
||||
User UAT yêu cầu admin quản lý menu eOffice (fe-user) — Ẩn/Hiện + Đổi tên. Confirm "chưa có" → tạo mới. User Q2=b clarify quan trọng: DisplayLabel CHỈ áp fe-user, admin sidebar giữ Label gốc.
|
||||
|
||||
- [x] **Chunk A (`2ea2d27`) Schema + Mig 27** — Domain MenuItem +IsVisible bool=true +DisplayLabel string?(200). EF config HasDefaultValue + HasMaxLength. Migration 27 `AddVisibilityAndDisplayLabelToMenuItems` (2 AddColumn) — 3-file rule. Apply LocalDB `_Dev` + `_Design` qua --connection override (memory `feedback_designtime_runtime_db`).
|
||||
|
||||
- [x] **Chunk B (`ef394f8`) BE API** — DTO MenuNodeDto + MenuItemDto +isVisible +displayLabel. GetMyMenuTreeQueryHandler pass through (KHÔNG filter server-side, 2 FE tự quyết). NEW UpdateMenuItemCommand + Validator + Handler (whitespace → null). MenusController +PATCH /api/menus/{key} [Authorize Policy="Permissions.Update"] body `{isVisible, displayLabel}`.
|
||||
|
||||
- [x] **Chunk C (`059bfcb`) FE Admin** — Domain MenuKeys +MenuVisibility + All[]. DbInitializer +leaf "Menu eOffice" Icon=Eye Order=94 (Workflows shift 94→95). Manual seed Mig 27 LocalDB Dev (INSERT MenuItems + Permissions Admin). FE Admin types/menu.ts mirror, menuKeys.ts +const, Layout resolver +/system/menu-visibility, App.tsx +Route. NEW pages/system/MenuVisibilityPage.tsx ~210 LOC: PageHeader + 4 StatCard + Search + Table 5 cột (Key mono + parentKey↳ / Tên gốc / Input "Tên hiển thị" inline / Toggle Eye-EyeOff emerald-amber / Save dirty + Khôi phục custom). onSuccess invalidate ['menus','all'] + ['my-menu'] live update sidebar.
|
||||
|
||||
- [x] **Chunk D (`1ed6530`) FE User** — fe-user types/menu.ts mirror. Layout.tsx filterForUser 2 tầng (USER_HIDDEN_KEYS hardcode structural + !isVisible dynamic). Helper effectiveLabel(n) = displayLabel?.trim() || label. Replace 3 callsite {node.label} → {effectiveLabel(node)}. USER_FIXED_TOP "__inbox" +isVisible:true cho type check. **fe-admin Layout KHÔNG đụng** — admin sidebar render Label gốc + show hết menu (user Q2=b).
|
||||
|
||||
- [x] **Chunk E Docs (current)** — STATUS Recently Done top + Last updated S20 turn 7. HANDOFF TL;DR Session 20 turn 7 trên đầu (giữ S20 prev nguyên §6.5). migration-todos done section (file này) + pending S21+. Session log `2026-05-11-1700-menu-visibility-mig27.md`. KHÔNG đụng rules / architecture / PROJECT-MAP / workflow-contract / forms-spec / database-guide / schema-diagram / CLAUDE.md (defer cron audit 2026-06-01).
|
||||
|
||||
**Stats Session 20 turn 7:** 26→**27 mig** (+1 AddVisibilityAndDisplayLabelToMenuItems), 59 DB tables (no change), ~141→**142 endpoints** (+1 PATCH /menus/{key}), 33→**34 FE pages** (+1 MenuVisibilityPage), ~60→**61 menu key** (+1 MenuVisibility), 81 test pass (Q4 UAT defer), 44 gotcha (no new). Memory entries 14 (no new).
|
||||
|
||||
**Defer Session 21+ (mới sau S20 turn 7):**
|
||||
|
||||
- [ ] Test PATCH /api/menus/{key} validate Key required + DisplayLabel trim
|
||||
- [ ] Skill `permission-matrix` cross-ref section "menu visibility" — defer cron audit 2026-06-01
|
||||
- [ ] UX verify trong UAT: admin ẩn menu cha → children có ẩn theo không? (FE filter check per-node `!n.isVisible`, parent vẫn hiện thì children render. Có thể cần propagate hidden tree-level nếu UAT phản hồi)
|
||||
|
||||
### ✅ Session 20 done (2026-05-11) — PE Detail UI restructure 3 yêu cầu UX user (4 chunk `9dee00d` → `2bba851` → `f2f01f4` → Chunk D Docs)
|
||||
|
||||
User UAT live feedback: "Logic khá OK rồi, điều chỉnh UI Duyệt NCC 1 tý". 3 yêu cầu cụ thể chốt qua Q&A 4 câu (Q1=a giữ Section "Chọn NCC TP" / Q2=a NCC shared + 1 hạng mục demo / Q3=a chỉ hiện NV đã ký / Q4 public luôn skip dotnet test). FE-only restructure (1 hook BE nhẹ auto-seed Detail).
|
||||
|
||||
170
docs/changelog/sessions/2026-05-11-1700-menu-visibility-mig27.md
Normal file
170
docs/changelog/sessions/2026-05-11-1700-menu-visibility-mig27.md
Normal file
@ -0,0 +1,170 @@
|
||||
# Session 20 turn 7 — Admin Ẩn/Hiện + Đổi tên menu eOffice (Mig 27)
|
||||
|
||||
**Date:** 2026-05-11
|
||||
**Commits:** `2ea2d27` (A schema) → `ef394f8` (B API) → `059bfcb` (C admin page) → `1ed6530` (D user layout) → this (E docs)
|
||||
|
||||
## Bối cảnh
|
||||
|
||||
User UAT live yêu cầu thêm tính năng admin "Ẩn/Hiện và Đổi tên hiển thị của các Menu bên ngoài Office" — trang quản lý menu eOffice (fe-user) thực hiện trong Admin Page (fe-admin). User confirm "Hình như chưa có?" — đúng, chưa có.
|
||||
|
||||
User clarify quan trọng: **"edit hiển thị bên ngoài nhé. Chỉ của eOffice thôi"** → Q2=b: DisplayLabel CHỈ áp khi render fe-user. fe-admin sidebar luôn dùng Label gốc (admin dễ debug, không nhầm).
|
||||
|
||||
## Q&A trước khi code
|
||||
|
||||
| # | Câu hỏi | Chốt |
|
||||
|---|---|---|
|
||||
| Q1 | Scope visibility (global vs per-role)? | **a** Global — permission matrix đã handle per-role |
|
||||
| Q2 | DisplayLabel áp đâu? | **b** Chỉ fe-user, admin sidebar giữ Label gốc |
|
||||
| Q3 | Init defaults — keep `USER_HIDDEN_KEYS` hardcode 4 root? | **a** Giữ hardcode + tầng `IsVisible` dynamic combine |
|
||||
| Q4 | Verify mode? | Phase 9 UAT iteration: skip dotnet test, vẫn `npm run build` |
|
||||
|
||||
## Chunk A — Schema + Migration 27 (`2ea2d27`)
|
||||
|
||||
**Domain `MenuItem.cs`:**
|
||||
```csharp
|
||||
public bool IsVisible { get; set; } = true;
|
||||
public string? DisplayLabel { get; set; }
|
||||
```
|
||||
|
||||
**EF Configuration:** `HasDefaultValue(true)` + `HasMaxLength(200)`.
|
||||
|
||||
**Migration 27 `AddVisibilityAndDisplayLabelToMenuItems`:**
|
||||
- `AddColumn IsVisible bit NOT NULL DEFAULT 1` (backfill existing rows = true)
|
||||
- `AddColumn DisplayLabel nvarchar(200) NULL`
|
||||
- 3-file rule
|
||||
|
||||
**Verify:**
|
||||
- `dotnet build` 0 err
|
||||
- `dotnet ef database update --connection SolutionErp_Dev` applied
|
||||
- `dotnet ef database update SolutionErp_Design` applied (catchup Mig 25/26/27)
|
||||
|
||||
## Chunk B — BE API (`ef394f8`)
|
||||
|
||||
**DTOs `MenuDtos.cs`:**
|
||||
- `MenuNodeDto` +`IsVisible bool` +`DisplayLabel string?` (sau CRUD flags, trước Children)
|
||||
- `MenuItemDto` +`IsVisible` +`DisplayLabel`
|
||||
|
||||
**`GetMyMenuTreeQueryHandler`:** pass `m.IsVisible` + `m.DisplayLabel` vào DTO record. KHÔNG filter `IsVisible` server-side — 2 FE app tự quyết render gì (fe-admin show all, fe-user filter).
|
||||
|
||||
**`ListMenuItemsQueryHandler`:** projection thêm 2 field.
|
||||
|
||||
**NEW `UpdateMenuItemCommand` + Validator + Handler** (PermissionFeatures.cs):
|
||||
```csharp
|
||||
public record UpdateMenuItemCommand(string Key, bool IsVisible, string? DisplayLabel) : IRequest;
|
||||
// Handler: load by Key, set 2 field, trim DisplayLabel → null nếu whitespace, SaveAsync
|
||||
```
|
||||
|
||||
**`MenusController` +PATCH endpoint:**
|
||||
```csharp
|
||||
[HttpPatch("{key}")]
|
||||
[Authorize(Policy = "Permissions.Update")]
|
||||
public async Task<IActionResult> Update(string key, [FromBody] UpdateMenuItemRequest body, CancellationToken ct)
|
||||
{
|
||||
await mediator.Send(new UpdateMenuItemCommand(key, body.IsVisible, body.DisplayLabel), ct);
|
||||
return NoContent();
|
||||
}
|
||||
```
|
||||
Policy reuse `Permissions.Update` (admin matrix — cùng scope quản trị).
|
||||
|
||||
## Chunk C — FE Admin MenuVisibilityPage (`059bfcb`)
|
||||
|
||||
**Domain `MenuKeys.cs`:** +`MenuVisibility = "MenuVisibility"` + thêm vào `All[]`.
|
||||
|
||||
**DbInitializer `SeedMenuTreeAsync`:** +leaf `(MenuVisibility, "Menu eOffice", System, 94, "Eye")`. Workflows shift Order 94 → 95. Idempotent. Manual seed Mig 27 LocalDB Dev:
|
||||
```sql
|
||||
INSERT INTO MenuItems ([Key], Label, ParentKey, [Order], Icon, IsVisible, DisplayLabel)
|
||||
VALUES ('MenuVisibility', N'Menu eOffice', 'System', 94, 'Eye', 1, NULL);
|
||||
|
||||
INSERT INTO Permissions (Id, RoleId, MenuKey, CanRead, CanCreate, CanUpdate, CanDelete)
|
||||
SELECT NEWID(), Id, 'MenuVisibility', 1, 1, 1, 1 FROM Roles WHERE Name = 'Admin';
|
||||
```
|
||||
|
||||
**FE Admin updates:**
|
||||
- `types/menu.ts`: MenuItem + MenuNode +`isVisible` +`displayLabel`
|
||||
- `lib/menuKeys.ts`: +`MenuVisibility` const
|
||||
- `components/Layout.tsx` resolver: +`MenuVisibility: '/system/menu-visibility'`
|
||||
- `App.tsx`: +Route + import `MenuVisibilityPage`
|
||||
|
||||
**NEW `pages/system/MenuVisibilityPage.tsx` (~210 LOC):**
|
||||
|
||||
Layout pattern reuse PermissionsPage:
|
||||
- `PageHeader` + description nhắc admin sidebar dùng Tên gốc
|
||||
- 4 StatCard: Tổng / Hiển thị (eOffice) / Đã ẩn / Đã đổi tên
|
||||
- Search input — filter theo `key | label | displayLabel`
|
||||
- Table 5 cột:
|
||||
| Key (mono + parentKey ↳) | Tên gốc | Input "Tên hiển thị" inline (placeholder "Mặc định: {label}") | Toggle Hiển thị/Ẩn (button emerald/amber) | Hành động |
|
||||
- Per-row state qua `DraftMap` — dirty detect = `draft[key] !== ev`
|
||||
- Save button hiện khi dirty: `PATCH /menus/{key}` body `{ isVisible, displayLabel }` (trim empty → null)
|
||||
- "Khôi phục" button khi đã custom (hidden hoặc renamed): force `isVisible=true`, `displayLabel=null`
|
||||
- onSuccess: invalidate `['menus', 'all']` + `['my-menu']` + clear draft entry → live update sidebar
|
||||
- Row hidden: `bg-amber-50/40` highlight, input custom label `bg-brand-50/40`
|
||||
|
||||
## Chunk D — FE User Layout filter + render (`1ed6530`)
|
||||
|
||||
**`fe-user/types/menu.ts`:** mirror fe-admin (MenuItem + MenuNode +isVisible +displayLabel).
|
||||
|
||||
**`fe-user/components/Layout.tsx`:**
|
||||
|
||||
```tsx
|
||||
function filterForUser(nodes: MenuNode[]): MenuNode[] {
|
||||
// Filter 2 tầng:
|
||||
// 1. USER_HIDDEN_KEYS hardcode (Master/System/Forms/Reports — structural never-show)
|
||||
// 2. !isVisible dynamic (Mig 27 admin toggle)
|
||||
return nodes
|
||||
.filter(n => !USER_HIDDEN_KEYS.has(n.key) && n.isVisible !== false)
|
||||
.map(n => ({ ...n, children: filterForUser(n.children) }))
|
||||
}
|
||||
|
||||
function effectiveLabel(n: { label: string; displayLabel?: string | null }): string {
|
||||
return (n.displayLabel && n.displayLabel.trim()) || n.label
|
||||
}
|
||||
```
|
||||
|
||||
Replace 3 callsite `{node.label}` → `{effectiveLabel(node)}` (Group header / Leaf NavLink / nested NavLink). USER_FIXED_TOP "__inbox" entry +`isVisible:true` +`displayLabel:null` cho type check pass.
|
||||
|
||||
**fe-admin Layout KHÔNG đụng** — admin sidebar luôn render Label gốc + show hết menu, kể cả `isVisible=false` (cho admin biết menu nào đã ẩn để toggle bật lại). Đây chính là điểm khác biệt user yêu cầu Q2=b.
|
||||
|
||||
## Verify chain mỗi chunk
|
||||
|
||||
| Chunk | BE build | FE-admin build | FE-user build | DB migrate | Push |
|
||||
|---|---|---|---|---|---|
|
||||
| A | ✅ 0 warn / 0 err | (no FE change) | (no FE change) | ✅ Dev + Design applied | `2ea2d27` |
|
||||
| B | ✅ pass | (no FE change) | (no FE change) | (no DB change) | `ef394f8` |
|
||||
| C | (no BE change) | ✅ pass | (no FE change) | manual SQL seed Mig 27 | `059bfcb` |
|
||||
| D | (no BE change) | ✅ pass (re-verify) | ✅ pass | (no DB change) | `1ed6530` |
|
||||
|
||||
## Stats Session 20 turn 7
|
||||
|
||||
| Metric | Trước | Sau | Delta |
|
||||
|---|---|---|---|
|
||||
| DB tables | 59 | 59 | 0 |
|
||||
| Migrations | 26 | **27** | +1 (`AddVisibilityAndDisplayLabelToMenuItems`) |
|
||||
| Endpoints | ~141 | **~142** | +1 (`PATCH /api/menus/{key}`) |
|
||||
| FE pages | 33 | **34** | +1 (`MenuVisibilityPage`) |
|
||||
| Menu keys | ~60 | ~61 | +1 (`MenuVisibility`) |
|
||||
| Unit tests | 81 pass | 81 pass | 0 (Q4 UAT iteration skip) |
|
||||
| Gotchas | 44 | 44 | 0 |
|
||||
| Commits | — | 5 | A/B/C/D + E docs |
|
||||
|
||||
## Cross-ref
|
||||
|
||||
- Memory `feedback_audit_reuse_before_clone.md` — pattern reuse PermissionsPage UI cho MenuVisibilityPage (table + inline edit + state map)
|
||||
- Memory `feedback_per_chunk_commit.md` — A/B/C/D/E chunk discipline
|
||||
- Memory `feedback_designtime_runtime_db.md` — apply Mig 27 lên cả `_Dev` (runtime) + `_Design` (ef tooling) qua --connection override
|
||||
- Memory `feedback_uat_skip_verify.md` — Q4 Phase 9 UAT: skip test, vẫn `npm run build`
|
||||
- Skill `permission-matrix` — sẽ cross-ref menu visibility section ở audit 2026-06-01
|
||||
|
||||
## Pending S21+
|
||||
|
||||
Carry over từ HANDOFF S19+S20:
|
||||
- Test V2 Service wire + Section gộp
|
||||
- Test B4 silent 403 (HIGH §7)
|
||||
- **Contract V2 wire (Mig 28+29 mirror PE pattern)** — biggest pending
|
||||
- Phân quyền strict V2
|
||||
- Drop legacy V1 + Mig 15 cleanup
|
||||
- Cron audit 2026-06-01 (skill stale + schema-diagram §16-21)
|
||||
|
||||
Mới sau S20 turn 7:
|
||||
- Test PATCH `/api/menus/{key}` validate Key required + DisplayLabel trim
|
||||
- Skill `permission-matrix` thêm section "menu visibility" — defer audit 2026-06-01
|
||||
- Test edge case: admin ẩn menu cha → children có ẩn theo không? (hiện FE filter chỉ check `!n.isVisible`, child có thể vẫn hiện nếu parent vẫn visible — verify UX trong UAT)
|
||||
Reference in New Issue
Block a user