@the-portland-company/shell 0.99.0 → 0.99.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -412,6 +412,13 @@ type ChromeState = {
412
412
  * new org's key.
413
413
  */
414
414
  orgPending: boolean;
415
+ /**
416
+ * The un-mapped membership role in the ACTIVE org (e.g. "account_manager"),
417
+ * for the user card's role label. Apps collapse roles to their own
418
+ * permission pair, so their `session.role` cannot tell an Account Manager
419
+ * from a Member. Null when unknown.
420
+ */
421
+ activeRawRole?: string | null;
415
422
  navDisplay: NavDisplayMode;
416
423
  setNavDisplay: (m: NavDisplayMode) => void;
417
424
  };
@@ -412,6 +412,13 @@ type ChromeState = {
412
412
  * new org's key.
413
413
  */
414
414
  orgPending: boolean;
415
+ /**
416
+ * The un-mapped membership role in the ACTIVE org (e.g. "account_manager"),
417
+ * for the user card's role label. Apps collapse roles to their own
418
+ * permission pair, so their `session.role` cannot tell an Account Manager
419
+ * from a Member. Null when unknown.
420
+ */
421
+ activeRawRole?: string | null;
415
422
  navDisplay: NavDisplayMode;
416
423
  setNavDisplay: (m: NavDisplayMode) => void;
417
424
  };
@@ -472,6 +472,34 @@ function LoadingScreen({
472
472
  }
473
473
  );
474
474
  }
475
+
476
+ // src/lib/roleLabel.ts
477
+ var ADMIN_ROLES = /* @__PURE__ */ new Set(["owner", "admin", "account_admin"]);
478
+ var STAFF_ROLES = /* @__PURE__ */ new Set(["super_admin", "super administrator", "politogy_admin"]);
479
+ var MANAGER_ROLES = /* @__PURE__ */ new Set(["account_manager", "account manager", "manager", "campaign_manager"]);
480
+ var FIELD_ROLES = /* @__PURE__ */ new Set([
481
+ "member",
482
+ "field_user",
483
+ "field user",
484
+ "field",
485
+ "volunteer",
486
+ "canvasser",
487
+ "caller",
488
+ "staff"
489
+ ]);
490
+ var VIEWER_ROLES = /* @__PURE__ */ new Set(["viewer"]);
491
+ function roleLabel(raw) {
492
+ const role = (raw ?? "").trim().toLowerCase();
493
+ if (!role) return null;
494
+ if (ADMIN_ROLES.has(role) || STAFF_ROLES.has(role)) return "Admin";
495
+ if (MANAGER_ROLES.has(role)) return "Account Manager";
496
+ if (FIELD_ROLES.has(role)) return "Member";
497
+ if (VIEWER_ROLES.has(role)) return "Viewer";
498
+ return null;
499
+ }
500
+ function userCardRole(activeRawRole, appRole) {
501
+ return roleLabel(activeRawRole) ?? roleLabel(appRole) ?? (appRole?.trim() || null);
502
+ }
475
503
  var CORE_APP = "vrm";
476
504
  var DEFAULT_SIGN_OUT_URL = "/auth/signout";
477
505
  var POLITOGY_APPS = {
@@ -856,7 +884,9 @@ function FrameBody({
856
884
  // The chrome API's avatar wins; the app may supply a fallback chain
857
885
  // (e.g. a stored memoji) for users with no uploaded photo.
858
886
  avatarUrl: identity.avatarUrl ?? data?.fallbackAvatarUrl ?? null,
859
- role: session.role
887
+ // The active org's RAW role wins over the app's collapsed one, so an
888
+ // Account Manager is not shown as "Member" (Organization-Settings §8.4).
889
+ role: userCardRole(chrome?.activeRawRole ?? activeCoreOrg?.rawRole, session.role)
860
890
  },
861
891
  userMenu,
862
892
  onSignOut: handleSignOut,
@@ -1044,10 +1074,13 @@ function TokenChromeStateProvider({
1044
1074
  orgs,
1045
1075
  orgsLoading: !tokenOrg.orgs && orgsFetching && !orgsError && !fetchedOrgs,
1046
1076
  orgPending: pendingOrg !== null,
1077
+ // The list's raw role, else the token's `org_role` claim — only while the
1078
+ // token is still for the active org (not mid-switch).
1079
+ activeRawRole: orgs.find((o) => o.id === orgId)?.rawRole ?? (orgId && tokenOrg.orgId === orgId ? tokenOrg.orgRole ?? null : null),
1047
1080
  navDisplay,
1048
1081
  setNavDisplay
1049
1082
  }),
1050
- [orgId, setOrgId, orgs, tokenOrg.orgs, orgsFetching, orgsError, fetchedOrgs, pendingOrg, navDisplay, setNavDisplay]
1083
+ [orgId, setOrgId, orgs, tokenOrg.orgs, tokenOrg.orgId, tokenOrg.orgRole, orgsFetching, orgsError, fetchedOrgs, pendingOrg, navDisplay, setNavDisplay]
1051
1084
  );
1052
1085
  return /* @__PURE__ */ jsx(ChromeStateContext.Provider, { value, children });
1053
1086
  }
@@ -1201,6 +1234,9 @@ function ChromeStateProvider({
1201
1234
  })),
1202
1235
  orgsLoading: false,
1203
1236
  orgPending: false,
1237
+ // The app's list collapses roles (Contacts: admin | member); the
1238
+ // chrome API's list carries the raw one.
1239
+ activeRawRole: controlledOrg.orgs?.find((o) => o.id === controlledOrg.orgId)?.rawRole ?? orgs?.find((o) => o.id === controlledOrg.orgId)?.rawRole ?? null,
1204
1240
  navDisplay,
1205
1241
  setNavDisplay
1206
1242
  };
@@ -1221,6 +1257,7 @@ function ChromeStateProvider({
1221
1257
  // falls back to the app label rather than shimmering forever.
1222
1258
  orgsLoading: orgsFetching && !orgsError && !orgs,
1223
1259
  orgPending: false,
1260
+ activeRawRole: orgs?.find((o) => o.id === orgId)?.rawRole ?? null,
1224
1261
  navDisplay,
1225
1262
  setNavDisplay
1226
1263
  };