{"version":3,"file":"member-permissions-OQIM5ebF.mjs","names":[],"sources":["../../core/src/member-permissions.ts"],"sourcesContent":["/**\n * Member-type capability helpers for the portal.\n *\n * The portal historically branched on the binary `member_type`\n * (\"customer\" vs \"rep\"). That collapses every company-custom member type\n * (affiliate, ambassador, ...) into the \"rep\" bucket and grants it full\n * rep access. These helpers read the richer `member_type_detail`\n * permissions instead, so gating follows the backend `permissions` JSONB\n * and custom types are handled correctly.\n *\n * All functions accept a nullable account so callers can pass the result\n * of `useAccount()` (which is `undefined` while loading) directly.\n */\n\nimport type { AccountRep, MemberPermissionKey } from \"./account-types\";\n\ntype MaybeAccount =\n  | Pick<AccountRep, \"member_type\" | \"member_type_detail\">\n  | null\n  | undefined;\n\n/**\n * Whether the current member has the given capability.\n *\n * Prefers the native `member_type_detail.permissions` map. When no detail\n * is present yet (backfill gap), falls back to the legacy binary type:\n * `rep_eligible` is granted to reps and denied to customers, preserving\n * today's behavior. Unknown keys deny by default.\n */\nexport function hasMemberPermission(\n  account: MaybeAccount,\n  key: MemberPermissionKey,\n): boolean {\n  if (!account) return false;\n\n  const permissions = account.member_type_detail?.permissions;\n  if (permissions) {\n    // An explicit value on the requested key always wins — never let the\n    // alias override an explicit `false` (that would grant rep access to a\n    // type whose rep_eligible is deliberately denied).\n    const requested = permissions[key];\n    if (requested !== undefined) return requested === true;\n\n    // Requested key is absent: `search_enabled` and `rep_eligible` are\n    // aliases on the backend, so fall back to the other only to fill the gap.\n    if (key === \"rep_eligible\") return permissions.search_enabled === true;\n    if (key === \"search_enabled\") return permissions.rep_eligible === true;\n    return false;\n  }\n\n  // Backfill fallback: derive rep-eligibility from the binary type.\n  if (key === \"rep_eligible\" || key === \"search_enabled\") {\n    return account.member_type !== \"customer\";\n  }\n  return false;\n}\n\n/**\n * Whether the current member may access rep-only portal surfaces\n * (Messages, My Site, and any rep-gated custom screen). This is the single\n * gate the shell should consult instead of `member_type === \"customer\"`.\n */\nexport function canAccessRepSurfaces(account: MaybeAccount): boolean {\n  return hasMemberPermission(account, \"rep_eligible\");\n}\n\n/**\n * Whether the member is KNOWN to lack rep access — true only once a loaded\n * account resolves to no rep-eligibility. An unresolved (loading) or absent\n * account is NOT treated as denied, so shell gating stays permissive while\n * the account query is in flight. This matches the pre-migration behavior\n * (which defaulted an unknown member to rep) and avoids briefly filtering a\n * rep's own navigation — which, combined with the root-path redirect, could\n * otherwise misroute a rep whose first item is rep-only. Use this for\n * gating; use `canAccessRepSurfaces` for a positive capability check on an\n * already-loaded account.\n */\nexport function deniesRepSurfaces(account: MaybeAccount): boolean {\n  return Boolean(account) && !canAccessRepSurfaces(account);\n}\n\n/**\n * The real per-company member-type slug (`rep`, `customer`, or a custom\n * slug like `affiliate`). Falls back to the binary `member_type` when no\n * detail is present.\n */\nexport function memberTypeSlug(account: MaybeAccount): string | null {\n  if (!account) return null;\n  return account.member_type_detail?.slug ?? account.member_type;\n}\n\n/**\n * The admin-facing display label for the member type (e.g. \"Affiliate\").\n * Null when no detail is present — callers should not fabricate a label\n * from the binary type.\n */\nexport function memberTypeName(account: MaybeAccount): string | null {\n  return account?.member_type_detail?.name ?? null;\n}\n"],"mappings":";;;;;;;;;AA6BA,SAAgB,oBACd,SACA,KACS;AACT,KAAI,CAAC,QAAS,QAAO;CAErB,MAAM,cAAc,QAAQ,oBAAoB;AAChD,KAAI,aAAa;EAIf,MAAM,YAAY,YAAY;AAC9B,MAAI,cAAc,KAAA,EAAW,QAAO,cAAc;AAIlD,MAAI,QAAQ,eAAgB,QAAO,YAAY,mBAAmB;AAClE,MAAI,QAAQ,iBAAkB,QAAO,YAAY,iBAAiB;AAClE,SAAO;;AAIT,KAAI,QAAQ,kBAAkB,QAAQ,iBACpC,QAAO,QAAQ,gBAAgB;AAEjC,QAAO;;;;;;;AAQT,SAAgB,qBAAqB,SAAgC;AACnE,QAAO,oBAAoB,SAAS,eAAe;;;;;;;;;;;;;AAcrD,SAAgB,kBAAkB,SAAgC;AAChE,QAAO,QAAQ,QAAQ,IAAI,CAAC,qBAAqB,QAAQ;;;;;;;AAQ3D,SAAgB,eAAe,SAAsC;AACnE,KAAI,CAAC,QAAS,QAAO;AACrB,QAAO,QAAQ,oBAAoB,QAAQ,QAAQ;;;;;;;AAQrD,SAAgB,eAAe,SAAsC;AACnE,QAAO,SAAS,oBAAoB,QAAQ"}