refactor: 菜单栏角色化降噪(role-based-navigation-simplification)
这个提交包含在:
@@ -6,13 +6,31 @@ const {
|
||||
} = require("../../src/services/workspaceSettings");
|
||||
const { hasPermission, resolvePermissionsForRoles } = require("../../src/constants/permissions");
|
||||
|
||||
function buildPrisma() {
|
||||
function buildPrisma(enabledFlagKeys = []) {
|
||||
const enabled = new Set(enabledFlagKeys);
|
||||
return {
|
||||
workspaceSetting: {
|
||||
findFirst: vi.fn().mockResolvedValue(null),
|
||||
},
|
||||
featureFlag: {
|
||||
findUnique: vi.fn().mockResolvedValue(null),
|
||||
findUnique: vi.fn().mockImplementation(async ({ where }) => {
|
||||
if (!enabled.has(where.flag_key)) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
id: `flag-${where.flag_key}`,
|
||||
flag_key: where.flag_key,
|
||||
override_mode: "force_on",
|
||||
rollout_type: "global",
|
||||
department_ids: [],
|
||||
note: "",
|
||||
version: 1,
|
||||
created_at: null,
|
||||
created_by: null,
|
||||
updated_at: null,
|
||||
updated_by: null,
|
||||
};
|
||||
}),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -37,7 +55,7 @@ function buildConfig() {
|
||||
}
|
||||
|
||||
describe("workspace small-team navigation defaults", () => {
|
||||
it("keeps only the R66 daily path in default main navigation", () => {
|
||||
it("keeps only the small-team task path in default main navigation", () => {
|
||||
const mainModules = Object.entries(DEFAULT_MODULE_VISIBILITY)
|
||||
.filter(([, placement]) => placement === "main")
|
||||
.map(([key]) => key);
|
||||
@@ -47,12 +65,10 @@ describe("workspace small-team navigation defaults", () => {
|
||||
"team",
|
||||
"leaveRequests",
|
||||
"reimbursements",
|
||||
"assets",
|
||||
"weeklyReports",
|
||||
"todos",
|
||||
"announcements",
|
||||
"projects",
|
||||
]);
|
||||
expect(DEFAULT_MODULE_VISIBILITY.todos).toBe("hidden");
|
||||
expect(DEFAULT_MODULE_VISIBILITY.assets).toBe("admin");
|
||||
expect(DEFAULT_MODULE_VISIBILITY.attendance).toBe("hidden");
|
||||
});
|
||||
|
||||
@@ -77,7 +93,7 @@ describe("workspace small-team navigation defaults", () => {
|
||||
expect(workspace.moduleVisibility.attendance).toBe("hidden");
|
||||
});
|
||||
|
||||
it("keeps P2 and finance-only backup modules out of employee navigation", async () => {
|
||||
it("keeps low-frequency and finance-only modules out of employee primary navigation", async () => {
|
||||
const employee = {
|
||||
id: "employee-1",
|
||||
department_id: "dept-1",
|
||||
@@ -86,12 +102,18 @@ describe("workspace small-team navigation defaults", () => {
|
||||
};
|
||||
|
||||
const navigation = await getSystemNavigation({
|
||||
prisma: buildPrisma(),
|
||||
prisma: buildPrisma(["presence_board_v1", "project_domain_v1"]),
|
||||
config: buildConfig(),
|
||||
employee,
|
||||
hasPermission: (candidate, permissionCode) => hasPermission(candidate.permissionCodes, permissionCode),
|
||||
});
|
||||
|
||||
const mainLabels = navigation.sections.main.map((item) => item.label);
|
||||
expect(mainLabels).toEqual(["工作台", "报销", "请假记录"]);
|
||||
expect(mainLabels).not.toContain("待我处理");
|
||||
expect(mainLabels).not.toContain("票据");
|
||||
expect(mainLabels).not.toContain("个人设置");
|
||||
|
||||
const backupKeys = navigation.sections.backup.map((item) => item.key);
|
||||
expect(backupKeys).toContain("purchaseRequests");
|
||||
expect(backupKeys).not.toContain("travelRequests");
|
||||
@@ -104,7 +126,30 @@ describe("workspace small-team navigation defaults", () => {
|
||||
expect(backupKeys).not.toContain("dashboard");
|
||||
});
|
||||
|
||||
it("does not expose workflow task entry to admin-only navigation", async () => {
|
||||
it("shows待我处理 for business approvers and keeps admin-only users out", async () => {
|
||||
const manager = {
|
||||
id: "manager-1",
|
||||
department_id: "dept-1",
|
||||
roleCodes: ["manager"],
|
||||
permissionCodes: resolvePermissionsForRoles(["manager"]),
|
||||
};
|
||||
|
||||
const managerNavigation = await getSystemNavigation({
|
||||
prisma: buildPrisma(["presence_board_v1", "project_domain_v1"]),
|
||||
config: { ...buildConfig(), projectDomainEnabled: true, presenceBoardEnabled: true },
|
||||
employee: manager,
|
||||
hasPermission: (candidate, permissionCode) => hasPermission(candidate.permissionCodes, permissionCode),
|
||||
});
|
||||
|
||||
expect(managerNavigation.sections.main.map((item) => item.label)).toEqual([
|
||||
"工作台",
|
||||
"待我处理",
|
||||
"报销",
|
||||
"项目",
|
||||
"团队",
|
||||
"请假记录",
|
||||
]);
|
||||
|
||||
const adminOnly = {
|
||||
id: "admin-1",
|
||||
department_id: "dept-1",
|
||||
@@ -113,7 +158,7 @@ describe("workspace small-team navigation defaults", () => {
|
||||
};
|
||||
|
||||
const navigation = await getSystemNavigation({
|
||||
prisma: buildPrisma(),
|
||||
prisma: buildPrisma(["payment_batch_v1"]),
|
||||
config: { ...buildConfig(), workflowDefinitionAdminEnabled: true },
|
||||
employee: adminOnly,
|
||||
hasPermission: (candidate, permissionCode) => hasPermission(candidate.permissionCodes, permissionCode),
|
||||
@@ -125,7 +170,32 @@ describe("workspace small-team navigation defaults", () => {
|
||||
...navigation.sections.backup,
|
||||
].map((item) => item.key);
|
||||
|
||||
expect(allKeys).toContain("auditLogs");
|
||||
expect(allKeys).toContain("systemSettings");
|
||||
expect(allKeys).not.toContain("auditLogs");
|
||||
expect(allKeys).not.toContain("workflowTasks");
|
||||
});
|
||||
|
||||
it("uses work names for finance navigation", async () => {
|
||||
const finance = {
|
||||
id: "finance-1",
|
||||
department_id: "dept-1",
|
||||
roleCodes: ["finance"],
|
||||
permissionCodes: resolvePermissionsForRoles(["finance"]),
|
||||
};
|
||||
|
||||
const navigation = await getSystemNavigation({
|
||||
prisma: buildPrisma(["payment_batch_v1"]),
|
||||
config: { ...buildConfig(), paymentBatchEnabled: true },
|
||||
employee: finance,
|
||||
hasPermission: (candidate, permissionCode) => hasPermission(candidate.permissionCodes, permissionCode),
|
||||
});
|
||||
|
||||
const mainLabels = navigation.sections.main.map((item) => item.label);
|
||||
expect(mainLabels).toContain("待我处理");
|
||||
expect(mainLabels).toContain("付款");
|
||||
expect(mainLabels).toContain("应付账款");
|
||||
expect(mainLabels).toContain("票据");
|
||||
expect(mainLabels).not.toContain("AP 运营");
|
||||
expect(mainLabels).not.toContain("付款批次");
|
||||
});
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户