diff --git a/SW.Bitween.Web/ClientApp/src/nav.ts b/SW.Bitween.Web/ClientApp/src/nav.ts
index 6743d63f..3b36679c 100644
--- a/SW.Bitween.Web/ClientApp/src/nav.ts
+++ b/SW.Bitween.Web/ClientApp/src/nav.ts
@@ -14,6 +14,7 @@ import {
RotateCcw,
ScrollText,
Settings,
+ ShieldCheck,
SlidersHorizontal,
Users,
Webhook,
@@ -96,7 +97,11 @@ export const NAV_GROUPS: NavGroup[] = [
{
label: "Administration",
items: [
- { label: "Team", path: "/team", icon: Users, permissions: ["users.view", "roles.view"] },
+ // Who can sign in, then what signing in lets them do. Two entries rather than one
+ // "Team" with tabs inside it: they are gated on different permissions, so a session
+ // that holds only one of them used to land on a page whose other half was a dead tab.
+ { label: "Members", path: "/team/members", icon: Users, permissions: ["users.view"] },
+ { label: "Roles", path: "/team/roles", icon: ShieldCheck, permissions: ["roles.view"] },
{ label: "Settings", path: "/settings", icon: Settings, permissions: ["settings.view"] },
// Last in Administration: it reports on everything above it rather than configuring
// anything, and it is the one page whose value is that nobody can quietly change it.
diff --git a/SW.Bitween.Web/ClientApp/src/pages/team/MemberDrawer.tsx b/SW.Bitween.Web/ClientApp/src/pages/team/MemberDrawer.tsx
index d2930f99..c2ab9498 100644
--- a/SW.Bitween.Web/ClientApp/src/pages/team/MemberDrawer.tsx
+++ b/SW.Bitween.Web/ClientApp/src/pages/team/MemberDrawer.tsx
@@ -12,7 +12,7 @@ import { Badge, Button, FormError, LoadingBlock } from "../../components/ui/basi
import { Checkbox, PasswordInput } from "../../components/ui/forms";
import { ConfirmDialog } from "../../components/ui/overlays";
import { formatDate, timeAgo, timeUntil } from "../../lib/dates";
-import { statusBadge } from "./MembersTab";
+import { statusBadge } from "./MembersPage";
import { keys } from "../../api/queryKeys";
function Section({ title, children }: { title: string; children: ReactNode }) {
diff --git a/SW.Bitween.Web/ClientApp/src/pages/team/MembersTab.tsx b/SW.Bitween.Web/ClientApp/src/pages/team/MembersPage.tsx
similarity index 88%
rename from SW.Bitween.Web/ClientApp/src/pages/team/MembersTab.tsx
rename to SW.Bitween.Web/ClientApp/src/pages/team/MembersPage.tsx
index 60e59871..9bcfb291 100644
--- a/SW.Bitween.Web/ClientApp/src/pages/team/MembersTab.tsx
+++ b/SW.Bitween.Web/ClientApp/src/pages/team/MembersPage.tsx
@@ -4,6 +4,7 @@ import { useQuery } from "@tanstack/react-query";
import { Search, UserPlus, UsersRound } from "lucide-react";
import { api, type User, type UserStatus } from "../../api";
import { Can } from "../../auth/guards";
+import { PageHeader } from "../../components/layout/PageHeader";
import { Avatar } from "../../components/ui/Avatar";
import { Badge, Button, EmptyState, LoadingBlock } from "../../components/ui/basics";
import { timeAgo } from "../../lib/dates";
@@ -25,7 +26,7 @@ export function statusBadge(status: UserStatus, lockedUntil?: string | null) {
return
+ Every member holds one or more roles, and every role is a list of{" "} + permissions — page by page, action by action. A member can do + something if any of their roles allows it; everything else is hidden from them. +
++ To bring someone in: create or pick a role under Roles, then add + them here. You set their first password and pass it on — Bitween doesn't send mail. +
+ > + ), + }} + /> +- A role is a reusable set of permissions. Open one to see — and shape — exactly what its - members can do. -
+ const header = ( +- Every member holds one or more roles, and every role is a list of{" "} - permissions — page by page, action by action. A member can do - something if any of their roles allows it; everything else is hidden from them. -
-- To bring someone in: create or pick a role on the Roles tab, then add them from the - Members tab. You set their first password and pass it on — Bitween doesn't send mail. -
- > - ), - }} - /> - -