For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 將系統側邊欄選單從 39 個扁平項目重組為 7 個可展開分組,讓使用者快速找到對應功能,並實現空分組自動隱藏。
Architecture: 三層改動,主專案 BE 程式碼零改動:(1) DB migration 新增 9 筆 public.ui_routes 分組節點 + 更新 27 筆葉節點的 pid/sort;(2) jedi-auth 套件在 get_user_web_ui_routes() 末段加剪枝邏輯(無子節點分組自動隱藏);(3) FE AppMenu.vue 移除寫死的 grcItems + 補 7 個 i18n 分組 key。
Tech Stack: PostgreSQL(migration SQL)、Python / pytest(jedi-auth unit tests)、Vue 3(AppMenu.vue)、JSON(i18n menu.json)
scripts/sql/2026-05-26-menu-group-restructure.sql — DB migration(7 分組節點 + 2 搬遷項插入 + 27 葉節點 pid/sort 更新)scripts/sql/backup/ — 新建備份目錄(migration 前手動產生 CSV 備份)~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/jedi_auth/app/service/user_service.py — 加 _prune_empty_groups method + 修改 get_user_web_ui_routes return 行~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/tests/unittest/test_user_service.py — 新增 3 個 prune 相關 test case~/Projects/Billows/Audit-Manager/compliance-manager-fe/src/layout/AppMenu.vue — 移除 grcItems 寫死區塊~/Projects/Billows/Audit-Manager/compliance-manager-fe/src/config/locales/i18n/zh-tw/menu.json — 補 7 個 group-* key~/Projects/Billows/Audit-Manager/compliance-manager-fe/src/config/locales/i18n/en/menu.json — 補 7 個 group-* key(英文版)~/Projects/Billows/Audit-Manager/compliance-manager-fe/src/config/locales/i18n/zh-cn/menu.json — 新建(zh-cn 目前無 menu.json),補 7 個 group-* key 簡體版~/Projects/Billows/Audit-Manager/compliance-manager-be/pyproject.toml — path dependency 指向本地 jedi-auth(Task B,不 commit;Task F 收尾時還原 + 更新 pin)git branch --show-currentExpected: feature/menu-group
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
-c "SELECT count(*) FROM public.ui_routes WHERE enable = 1 AND (pid IS NULL OR pid = 0);"Expected: 約 27 筆(目前所有 enable=1 的項目 pid 都是 0)
grep "^version" ~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/pyproject.tomlExpected: version = "0.1.18"
Files:
~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/jedi_auth/app/service/user_service.py (line 527-528 附近)~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/tests/unittest/test_user_service.pygrep -n "def test_get_user_web_ui_routes" \
~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/tests/unittest/test_user_service.pyExpected output 含 3 行(現有 3 個 test)。找最後一個既有 test 的行號,確認要在哪行後面追加新 tests。
在 test_user_service.py 末尾的 TestUserService class 內,緊接在現有最後一個 method 後面插入:
def test_get_user_web_ui_routes_prune_empty_group(
self, user_service, mock_user_domain_service, mock_ui_route_domain_service
):
"""剪枝:分組節點(無 url)底下無可見子節點時,應從回傳清單移除"""
# Arrange
user_uid = "test-uid-001"
tenant_id = 1
from unittest.mock import Mock
mock_user_domain_service.get_user_by_uid.return_value = Mock(roles=[])
mock_group = Mock()
mock_group.id = 100
mock_group.pid = None
mock_group.name = "group-project-task"
mock_group.icon = "pi pi-briefcase"
mock_group.url = None
mock_group.sort = 10
mock_group.route_capabilities = []
mock_ui_route_domain_service.get_viewable_routes_by_user_uid.return_value = [mock_group]
# Act
result = user_service.get_user_web_ui_routes(user_uid, tenant_id)
# Assert
assert result == []
def test_get_user_web_ui_routes_keep_group_with_visible_children(
self, user_service, mock_user_domain_service, mock_ui_route_domain_service
):
"""保留:分組節點底下有 2 個可見子節點時,應保留分組並 items 有 2 筆"""
# Arrange
user_uid = "test-uid-001"
tenant_id = 1
from unittest.mock import Mock
mock_user_domain_service.get_user_by_uid.return_value = Mock(roles=[])
mock_group = Mock()
mock_group.id = 100
mock_group.pid = None
mock_group.name = "group-project-task"
mock_group.icon = "pi pi-briefcase"
mock_group.url = None
mock_group.sort = 10
mock_group.route_capabilities = []
mock_child1 = Mock()
mock_child1.id = 1
mock_child1.pid = 100
mock_child1.name = "project-dashboard"
mock_child1.icon = "pi pi-home"
mock_child1.url = "/project/dashboard"
mock_child1.sort = 10
mock_child1.route_capabilities = []
mock_child2 = Mock()
mock_child2.id = 2
mock_child2.pid = 100
mock_child2.name = "my-tasks"
mock_child2.icon = "pi pi-check-square"
mock_child2.url = "/project/task-manage"
mock_child2.sort = 20
mock_child2.route_capabilities = []
mock_ui_route_domain_service.get_viewable_routes_by_user_uid.return_value = [
mock_group, mock_child1, mock_child2
]
# Act
result = user_service.get_user_web_ui_routes(user_uid, tenant_id)
# Assert
assert len(result) == 1
assert result[0]["label"] == "group-project-task"
assert len(result[0]["items"]) == 2
def test_get_user_web_ui_routes_keep_group_with_partial_visible_children(
self, user_service, mock_user_domain_service, mock_ui_route_domain_service
):
"""保留:分組節點底下有 1 個可見子節點(另一個已被 RBAC 過濾)時,分組仍保留"""
# Arrange
user_uid = "test-uid-001"
tenant_id = 1
from unittest.mock import Mock
mock_user_domain_service.get_user_by_uid.return_value = Mock(roles=[])
mock_group = Mock()
mock_group.id = 100
mock_group.pid = None
mock_group.name = "group-tenant-org"
mock_group.icon = "pi pi-building"
mock_group.url = None
mock_group.sort = 40
mock_group.route_capabilities = []
mock_child = Mock()
mock_child.id = 10
mock_child.pid = 100
mock_child.name = "user-manage"
mock_child.icon = "pi pi-users"
mock_child.url = "/auth/user-manage"
mock_child.sort = 30
mock_child.route_capabilities = []
mock_ui_route_domain_service.get_viewable_routes_by_user_uid.return_value = [
mock_group, mock_child
]
# Act
result = user_service.get_user_web_ui_routes(user_uid, tenant_id)
# Assert
assert len(result) == 1
assert result[0]["label"] == "group-tenant-org"
assert len(result[0]["items"]) == 1
assert result[0]["items"][0]["label"] == "user-manage"cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-auth
pytest tests/unittest/test_user_service.py::TestUserService::test_get_user_web_ui_routes_prune_empty_group \
tests/unittest/test_user_service.py::TestUserService::test_get_user_web_ui_routes_keep_group_with_visible_children \
tests/unittest/test_user_service.py::TestUserService::test_get_user_web_ui_routes_keep_group_with_partial_visible_children \
-vExpected:
test_prune_empty_group → FAIL(AssertionError: assert [{'label': 'group-project-task', ...}] == [])test_keep_group_with_visible_children → PASS(既有邏輯已正確處理)test_keep_group_with_partial_visible_children → PASS(既有邏輯已正確處理)4a. 修改 get_user_web_ui_routes 的 return 行(第 528 行):
# 原本:
sort_nodes(roots)
return roots
# 改成:
sort_nodes(roots)
return self._prune_empty_groups(roots)4b. 在 get_user_web_ui_routes 同一個 class 內、method 後面新增 _prune_empty_groups:
def _prune_empty_groups(self, nodes: list[dict]) -> list[dict]:
"""
遞迴剪枝:移除「無 url 且 items 為空」的分組節點。
葉節點(有 url)永遠保留;分組節點若還有可見子項也保留。
"""
pruned = []
for n in nodes:
if n.get("items"):
n["items"] = self._prune_empty_groups(n["items"])
is_group = not n.get("url")
if is_group and not n.get("items"):
continue
pruned.append(n)
return prunedcd ~/Projects/Jedicogy/module/jedi-python-package/jedi-auth
pytest tests/unittest/test_user_service.py::TestUserService::test_get_user_web_ui_routes_prune_empty_group \
tests/unittest/test_user_service.py::TestUserService::test_get_user_web_ui_routes_keep_group_with_visible_children \
tests/unittest/test_user_service.py::TestUserService::test_get_user_web_ui_routes_keep_group_with_partial_visible_children \
-vExpected:3 PASSED
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-auth
pytest tests/unittest/ -v --tb=short 2>&1 | tail -20Expected:所有既有 tests PASS,3 個新 tests PASS,無 FAILED。
cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-auth
git add jedi_auth/app/service/user_service.py tests/unittest/test_user_service.py
git commit -m "feat(user-service): add _prune_empty_groups to hide empty menu group nodes"Files:
~/Projects/Billows/Audit-Manager/compliance-manager-be/pyproject.toml在 pyproject.toml 找到(約在 dependencies 區段):
"jedi-auth==0.1.18",
#jedi-auth = { path = "/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/jedi-auth", develop = true}改為:
# "jedi-auth==0.1.18",
jedi-auth = { path = "/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/jedi-auth", develop = true}⚠️ 注意:此改動不 commit。確保 working tree 是 dirty 狀態、git status 看得到這個改動。
請使用者重啟 BE(BE 不支援 hot reload,path dep 改動需重啟才生效)。
重啟後驗證新 jedi-auth 已載入:
grep "_prune_empty_groups" ~/Projects/Jedicogy/module/jedi-python-package/jedi-auth/jedi_auth/app/service/user_service.pyExpected:找到該 method 定義。
Files:
scripts/sql/2026-05-26-menu-group-restructure.sqlscripts/sql/backup/ (directory)mkdir -p /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/backup
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
-At -F',' \
-c "SELECT id, name, pid, sort, enable FROM public.ui_routes ORDER BY id;" \
> /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/backup/2026-05-26-ui_routes-pre-menu-group.csv
wc -l /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/backup/2026-05-26-ui_routes-pre-menu-group.csvExpected:39 行(含所有現有 ui_routes)
建立 scripts/sql/2026-05-26-menu-group-restructure.sql,內容如下:
-- Date: 2026-05-26
-- Menu group restructure: 7 group nodes + 2 migrated items + 27 leaf pid/sort updates
-- Run as: cmmgr (not cm_app — RLS blocks system-level writes)
BEGIN;
-- ============================================================
-- Step 1: 新增 7 個分組節點(pid=NULL,無 url,僅作為展開父節點)
-- ============================================================
-- 1. group-project-task (2026-05-26)
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-project-task', NULL, 'pi pi-briefcase', 1, '專案與任務分組', 10
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-project-task');
-- 2. group-compliance-audit (2026-05-26)
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-compliance-audit', NULL, 'pi pi-shield', 1, '合規與稽核分組', 20
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-compliance-audit');
-- 3. group-communication (2026-05-26)
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-communication', NULL, 'pi pi-comments', 1, '溝通與互動分組', 30
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-communication');
-- 4. group-tenant-org (2026-05-26)
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-tenant-org', NULL, 'pi pi-building', 1, '租戶與組織分組', 40
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-tenant-org');
-- 5. group-system-admin (2026-05-26)
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-system-admin', NULL, 'pi pi-cog', 1, '系統管理分組', 50
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-system-admin');
-- 6. group-system-config (2026-05-26)
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-system-config', NULL, 'pi pi-sliders-h', 1, '系統設定分組', 60
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-system-config');
-- 7. group-ai-analytics (2026-05-26)
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), NULL, 'group-ai-analytics', NULL, 'pi pi-chart-line', 1, 'AI 與分析分組', 70
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-ai-analytics');
-- ============================================================
-- Step 2: 新增 2 個搬遷項(AppMenu.vue 原本寫死,搬進 DB)(2026-05-26)
-- pid 指向 group-project-task,不掛 capability(所有 user 可見)
-- ============================================================
-- 8. project-dashboard (2026-05-26)
WITH g AS (SELECT id FROM public.ui_routes WHERE name = 'group-project-task')
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), g.id, 'project-dashboard', '/project/dashboard', 'pi pi-home', 1, '專案儀表板', 10 FROM g
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'project-dashboard');
-- 9. my-tasks (2026-05-26)
WITH g AS (SELECT id FROM public.ui_routes WHERE name = 'group-project-task')
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort)
SELECT gen_random_uuid(), g.id, 'my-tasks', '/project/task-manage', 'pi pi-check-square', 1, '待辦任務', 20 FROM g
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'my-tasks');
-- ============================================================
-- Step 3: 更新 27 筆既有 ui_routes 的 pid / sort (2026-05-26)
-- ============================================================
-- Group 1 (project-task): sort 30/40/50(10/20 保留給 project-dashboard/my-tasks)
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-project-task'), sort = 30 WHERE name = 'project-list';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-project-task'), sort = 40 WHERE name = 'audit-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-project-task'), sort = 50 WHERE name = 'report-manage';
-- Group 2 (compliance-audit): sort 10/20/30/40
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-compliance-audit'), sort = 10 WHERE name = 'module-frame';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-compliance-audit'), sort = 20 WHERE name = 'compliance-framework-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-compliance-audit'), sort = 30 WHERE name = 'tool-plugin-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-compliance-audit'), sort = 40 WHERE name = 'flow-template-manage';
-- Group 3 (communication): sort 10/20/30/40/50/60
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'), sort = 10 WHERE name = 'bulletin-list';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'), sort = 20 WHERE name = 'bulletin-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'), sort = 30 WHERE name = 'survey-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'), sort = 40 WHERE name = 'system-feedback-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'), sort = 50 WHERE name = 'system-feedback-view';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-communication'), sort = 60 WHERE name = 'issue-integrate-config';
-- Group 4 (tenant-org): sort 10/20/30/40
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-tenant-org'), sort = 10 WHERE name = 'tenant-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-tenant-org'), sort = 20 WHERE name = 'department-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-tenant-org'), sort = 30 WHERE name = 'user-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-tenant-org'), sort = 40 WHERE name = 'role-manage';
-- Group 5 (system-admin): sort 10/20/30/40/50
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'), sort = 10 WHERE name = 'system-menu-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'), sort = 20 WHERE name = 'information-system-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'), sort = 30 WHERE name = 'device-manage';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'), sort = 40 WHERE name = 'storage-config';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-admin'), sort = 50 WHERE name = 'cloud-integrations';
-- Group 6 (system-config): sort 10/20/30/40
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-config'), sort = 10 WHERE name = 'smtp-config';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-config'), sort = 20 WHERE name = 'ldap-config';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-config'), sort = 30 WHERE name = 'notify-config';
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-system-config'), sort = 40 WHERE name = 'user-log';
-- Group 7 (ai-analytics): sort 10
UPDATE public.ui_routes SET pid = (SELECT id FROM public.ui_routes WHERE name = 'group-ai-analytics'), sort = 10 WHERE name = 'ai-dashboard';
COMMIT;用 BEGIN; ... ROLLBACK; 模式跑確認沒有 SQL 錯誤:
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
-c "BEGIN; \
INSERT INTO public.ui_routes (uid, pid, name, url, icon, enable, description, sort) \
SELECT gen_random_uuid(), NULL, 'group-project-task', NULL, 'pi pi-briefcase', 1, '測試', 10 \
WHERE NOT EXISTS (SELECT 1 FROM public.ui_routes WHERE name = 'group-project-task'); \
SELECT name FROM public.ui_routes WHERE name = 'group-project-task'; \
ROLLBACK;"Expected:在 ROLLBACK 前能查到 group-project-task,ROLLBACK 後不存在。
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
-f /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be/scripts/sql/2026-05-26-menu-group-restructure.sqlExpected:無 ERROR,最後印 COMMIT。
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
-c "SELECT name, pid, sort FROM public.ui_routes WHERE name LIKE 'group-%' ORDER BY sort;"Expected:7 行(group-project-task 10, group-compliance-audit 20, ... group-ai-analytics 70)
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
-c "SELECT count(*) FROM public.ui_routes WHERE pid IS NOT NULL AND pid != 0 AND enable = 1;"Expected:29 筆(27 既有更新 + 2 新增搬遷項)
注意:現有 DB 的根節點用
pid=0(整數 0)而非 NULL,所以改用pid != 0才能正確排除舊根節點。
cd /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be
git add scripts/sql/2026-05-26-menu-group-restructure.sql scripts/sql/backup/2026-05-26-ui_routes-pre-menu-group.csv
git commit -m "feat(db): menu-group restructure migration — 7 groups + 29 leaf nodes assigned"請使用者重啟 BE(確保 path dep 的 jedi-auth + 剪枝邏輯已載入)。
# 先取 JWT token(用 blsadmin 登入)
TOKEN=$(curl -s -X POST http://localhost:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"login_name":"blsadmin","password":"Billows@123!"}' \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('data',{}).get('access_token',''))")
echo "Token: ${TOKEN:0:20}..."
# 呼叫 web-menu
curl -s http://localhost:8000/user/web-menu \
-H "Authorization: Bearer $TOKEN" \
-H "X-Tenant-ID: 102" \
| python3 -c "import sys,json; d=json.load(sys.stdin); [print(n['label']) for n in d.get('data',[])]"Expected:7 行輸出(group-project-task / group-compliance-audit / ... / group-ai-analytics)
PGPASSWORD='jedi@123!' psql -h 192.168.50.188 -p 25432 -U cmmgr -d guidant_ai_dev \
-c "
SELECT g.name as group_name, count(c.id) as child_count
FROM public.ui_routes g
LEFT JOIN public.ui_routes c ON c.pid = g.id AND c.enable = 1
WHERE g.name LIKE 'group-%' AND g.enable = 1
GROUP BY g.name, g.sort
ORDER BY g.sort;
"Expected:7 行,每行顯示分組名稱 + 子節點數(group-project-task: 5, group-compliance-audit: 4, group-communication: 6, group-tenant-org: 4, group-system-admin: 5, group-system-config: 4, group-ai-analytics: 1)
請使用者在瀏覽器依序驗證:
| # | 操作 | 預期結果 |
|---|---|---|
| 1 | admin user 登入 | 看到 7 個分組標題 |
| 2 | 點開「專案與任務」 | 展開顯示:專案儀表板 / 待辦任務 / 專案管理 / 我的稽核 / 專案調查結果 |
| 3 | 點葉節點(如「專案管理」) | 正常跳轉到對應路由 |
| 4 | 點分組標題本身(無 url) | 不跳轉,僅 toggle 展開/收合 |
| 5 | zh-tw 切換到 zh-cn | 分組標題顯示中文(注:zh-cn/menu.json 未建,若有 fallback 顯示 zh-tw 中文亦可) |
| 6 | 切換到 en locale | 顯示英文分組標題(Projects & Tasks 等) |
| 7 | 新進無 role user 登入 | 只看到「專案儀表板 / 待辦任務」兩項(無分組標題) |
Edge case 注意:若某個分組節點回傳時
items: [](空陣列),genMenuNode的else分支會賦值tree_node.to = null並刪除items,使其成為無法點擊的葉節點。jedi-auth 剪枝邏輯保證空分組不會出現在 API 回傳中,但若 BE/DB 部署順序錯誤(FE 先上、BE 後上),此情況可能暫時發生。部署順序必須嚴格遵守 BE → DB migration → FE。
⚠️ 此 Task 等 user 說「smoke 通過,可以收尾」或「準備進版」才執行。
# 在 jedi-auth pyproject.toml 把 version = "0.1.18" 改為 "0.1.19"
# (patch bump — 剪枝邏輯屬 bug-fix-style 增量改進)cd ~/Projects/Jedicogy/module/jedi-python-package/jedi-auth
git add pyproject.toml
git commit -m "chore: bump version 0.1.18 -> 0.1.19 (prune empty menu groups)"
git push origin <current-branch>jedi-auth 推 Nexus 需使用者自行執行,Claude 不代為操作。
在 compliance-manager-be/pyproject.toml 把 Task B 的 path dep 還原:
"jedi-auth==0.1.19",
#jedi-auth = { path = "/Users/chouraymond/Projects/Jedicogy/module/jedi-python-package/jedi-auth", develop = true}poetry update jedi-authcd /Users/chouraymond/Projects/Billows/Audit-Manager/compliance-manager-be
git add pyproject.toml poetry.lock
git commit -m "chore(deps): bump jedi-auth 0.1.18 -> 0.1.19 (menu group prune logic)"若 migration 出問題需還原:
BEGIN;
-- 1. 移除本期新增 9 筆(2 搬遷項 + 7 分組節點)
DELETE FROM public.ui_routes WHERE name IN (
'project-dashboard', 'my-tasks',
'group-project-task', 'group-compliance-audit', 'group-communication',
'group-tenant-org', 'group-system-admin', 'group-system-config', 'group-ai-analytics'
);
-- 2. 還原 27 筆葉節點 pid 為 NULL(sort 用備份 CSV 還原,或接受現有數值)
UPDATE public.ui_routes SET pid = NULL WHERE name IN (
'project-list', 'audit-manage', 'report-manage',
'module-frame', 'compliance-framework-manage', 'tool-plugin-manage', 'flow-template-manage',
'bulletin-list', 'bulletin-manage', 'survey-manage',
'system-feedback-manage', 'system-feedback-view', 'issue-integrate-config',
'tenant-manage', 'department-manage', 'user-manage', 'role-manage',
'system-menu-manage', 'information-system-manage', 'device-manage', 'storage-config', 'cloud-integrations',
'smtp-config', 'ldap-config', 'notify-config', 'user-log',
'ai-dashboard'
);
COMMIT;jedi-auth 剪枝邏輯對扁平 tree(無分組節點)是 no-op,Rollback DB 後舊行為自然恢復,不需另外 rollback jedi-auth。