diff --git a/bwh_hive/bwh_hive/permissions.py b/bwh_hive/bwh_hive/permissions.py index e33b9a1..7b79155 100644 --- a/bwh_hive/bwh_hive/permissions.py +++ b/bwh_hive/bwh_hive/permissions.py @@ -200,3 +200,19 @@ def pinned_project_has_permission(doc, ptype: str | None = None, user: str | Non if ptype == "create": return True return doc.user == user + + +# Roles that grant access to Hive at all. Anyone on the team or on a client's +# side belongs in the app; everyone else is kept off the apps screen. +HIVE_ROLES = ("System Manager", "Hive Team", "Hive Client") + + +def has_hive_access(user: str | None = None) -> bool: + """Whether `user` is allowed to see Hive at all.""" + if not user: + user = frappe.session.user + + if user == "Administrator": + return True + + return any(role in frappe.get_roles(user) for role in HIVE_ROLES) diff --git a/bwh_hive/hooks.py b/bwh_hive/hooks.py index ed57b99..cf4ade6 100644 --- a/bwh_hive/hooks.py +++ b/bwh_hive/hooks.py @@ -11,15 +11,15 @@ # required_apps = [] # Each item in the list will be shown as an app in the apps page -# add_to_apps_screen = [ -# { -# "name": "bwh_hive", -# "logo": "/assets/bwh_hive/logo.png", -# "title": "BWH Hive", -# "route": "/bwh_hive", -# "has_permission": "bwh_hive.api.permission.has_app_permission" -# } -# ] +add_to_apps_screen = [ + { + "name": "bwh_hive", + "logo": "/assets/bwh_hive/images/hive-mark.svg", + "title": "Hive", + "route": "/hive", + "has_permission": "bwh_hive.bwh_hive.permissions.has_hive_access", + } +] # Includes in
# ------------------ diff --git a/bwh_hive/public/images/hive-mark.svg b/bwh_hive/public/images/hive-mark.svg new file mode 100644 index 0000000..31f0905 --- /dev/null +++ b/bwh_hive/public/images/hive-mark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/bwh_hive/www/hive.py b/bwh_hive/www/hive.py index cb01b38..9ed73f7 100644 --- a/bwh_hive/www/hive.py +++ b/bwh_hive/www/hive.py @@ -26,5 +26,7 @@ def get_boot(): "site_name": frappe.local.site, "read_only_mode": frappe.flags.read_only, "system_timezone": get_system_timezone(), + # The app switcher offers Desk only to those who can actually open it. + "is_system_manager": "System Manager" in frappe.get_roles(), } ) diff --git a/e2e/tests/app-switcher.spec.ts b/e2e/tests/app-switcher.spec.ts new file mode 100644 index 0000000..7fbf2d2 --- /dev/null +++ b/e2e/tests/app-switcher.spec.ts @@ -0,0 +1,35 @@ +import { test, expect } from "../helpers/app"; +import { callMethodGet } from "../helpers/frappe"; +import { gotoHive } from "../helpers/ui"; + +/** + * The switcher is fed by `frappe.apps.get_apps` — the site-wide apps-screen + * list — so what it offers depends on which apps the site has installed. The + * one row it always has is Desk, added client-side for System Managers. + */ +test.describe("App switcher", () => { + test("lists Desk and the site's other apps, but not Hive itself", async ({ + page, + request, + }) => { + const apps = await callMethodGet< + { name: string; title: string; route: string }[] + >(request, "frappe.apps.get_apps"); + + await gotoHive(page, "/"); + + await page.locator('[data-slot="sidebar-header"] button').click(); + await page.getByRole("menuitem", { name: "Switch app" }).hover(); + + const desk = page.getByRole("menuitem", { name: "Desk" }); + await expect(desk).toBeVisible({ timeout: 10000 }); + await expect(page.getByRole("menuitem", { name: "Hive" })).toHaveCount(0); + + for (const app of apps.filter((app) => app.name !== "bwh_hive")) { + await expect(page.getByRole("menuitem", { name: app.title })).toBeVisible(); + } + + await desk.click(); + await expect(page).toHaveURL(/\/desk/); + }); +}); diff --git a/frontend/src/components/shell/AppSidebar.vue b/frontend/src/components/shell/AppSidebar.vue index 37d4747..83f90db 100644 --- a/frontend/src/components/shell/AppSidebar.vue +++ b/frontend/src/components/shell/AppSidebar.vue @@ -58,7 +58,8 @@