@burdenoff/microfe-billing 2026.623.3 → 2026.624.2

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.
Files changed (43) hide show
  1. package/dist/billing/BillingUserRoutes.js +3 -3
  2. package/dist/billing/BillingUserRoutes.js.map +1 -1
  3. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +34 -34
  4. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
  5. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +94 -94
  6. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
  7. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +162 -187
  8. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
  9. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +146 -153
  10. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  11. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +10 -10
  12. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
  13. package/dist/billing/modules/billing/pages/TransactionsPage.js +62 -62
  14. package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
  15. package/dist/billing/modules/coupons/pages/CouponDetailPage.js +164 -179
  16. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  17. package/dist/billing/modules/coupons/pages/CreateCouponPage.js +8 -8
  18. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  19. package/dist/billing/modules/earnings/pages/EarningsPage.js +243 -305
  20. package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
  21. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +73 -78
  22. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  23. package/dist/billing/modules/earnings/pages/PayoutsPage.js +173 -179
  24. package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
  25. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +4 -4
  26. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  27. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +4 -4
  28. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  29. package/dist/billing/modules/settings/pages/SettingsPage.js +181 -173
  30. package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
  31. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +111 -99
  32. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
  33. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +68 -83
  34. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
  35. package/dist/billing/shared/components/AccessDenied.js +4 -4
  36. package/dist/billing/shared/components/AccessDenied.js.map +1 -1
  37. package/dist/billing/shared/components/ActorIdentity.js +8 -8
  38. package/dist/billing/shared/components/ActorIdentity.js.map +1 -1
  39. package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js +11 -11
  40. package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
  41. package/dist/billing/shared/ui/MetricCard.js +1 -1
  42. package/dist/billing/shared/ui/MetricCard.js.map +1 -1
  43. package/package.json +1 -1
@@ -12,78 +12,77 @@ import { useActorProfiles as c } from "../../../shared/hooks/useActorProfiles.js
12
12
  import "../../../shared/hooks/index.js";
13
13
  import { useCallback as l, useEffect as u, useMemo as d, useState as f } from "react";
14
14
  import { jsx as p, jsxs as m } from "react/jsx-runtime";
15
+ import { AlertDialog as h, AlertDialogAction as g, AlertDialogCancel as _, AlertDialogContent as v, AlertDialogDescription as y, AlertDialogFooter as b, AlertDialogHeader as x, AlertDialogTitle as S } from "@burdenoff/fe-libs/ui";
15
16
  //#region src/billing/modules/settings/pages/TeamPermissionsPage.tsx
16
- var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "\n query BillingScopeRoles($scopeId: String!) {\n roles(scopeId: $scopeId) {\n id\n name\n description\n priority\n isSystem\n }\n }\n", _ = "\n query BillingScopeActors($scopeId: String!) {\n actors(scopeId: $scopeId) {\n actorId\n actorType\n roleAssignments {\n id\n actorId\n actorType\n roleId\n scopeId\n isActive\n expiresAt\n role { id name priority }\n }\n }\n }\n", v = "\n mutation AssignBillingRole($input: AssignRoleInput!) {\n assignRoleToActor(input: $input) {\n id\n actorId\n roleId\n scopeId\n }\n }\n", y = "\n mutation RemoveBillingRole($assignmentId: String!) {\n removeRoleFromActor(assignmentId: $assignmentId)\n }\n", b = () => {
17
- let { apiGatewayUrl: b, authToken: x, orgId: S } = e(), { billingAccounts: C } = s(), { selectedAccountId: w } = i({
18
- orgId: S,
19
- billingAccounts: C,
17
+ var C = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, w = "\n query BillingScopeRoles($scopeId: String!) {\n roles(scopeId: $scopeId) {\n id\n name\n description\n priority\n isSystem\n }\n }\n", T = "\n query BillingScopeActors($scopeId: String!) {\n actors(scopeId: $scopeId) {\n actorId\n actorType\n roleAssignments {\n id\n actorId\n actorType\n roleId\n scopeId\n isActive\n expiresAt\n role { id name priority }\n }\n }\n }\n", E = "\n mutation AssignBillingRole($input: AssignRoleInput!) {\n assignRoleToActor(input: $input) {\n id\n actorId\n roleId\n scopeId\n }\n }\n", D = "\n mutation RemoveBillingRole($assignmentId: String!) {\n removeRoleFromActor(assignmentId: $assignmentId)\n }\n", O = () => {
18
+ let { apiGatewayUrl: O, authToken: k, orgId: A } = e(), { billingAccounts: j } = s(), { selectedAccountId: M } = i({
19
+ orgId: A,
20
+ billingAccounts: j,
20
21
  syncFromUrl: !0,
21
22
  urlParamName: "billingAccountId"
22
- }), T = w ?? void 0, E = o({ billingAccountIdOverride: T }), [D, O] = f([]), [k, A] = f([]), [j, M] = f(!1), [N, P] = f(null), [F, I] = f(""), [L, R] = f(""), [z, B] = f(!1), V = l(async (e, t) => a({
23
- apiGatewayUrl: b,
24
- authToken: x,
25
- orgId: S,
23
+ }), N = M ?? void 0, P = o({ billingAccountIdOverride: N }), [F, I] = f([]), [L, R] = f([]), [z, B] = f(!1), [V, H] = f(null), [U, W] = f(""), [G, K] = f(""), [q, J] = f(!1), [Y, X] = f(null), Z = l(async (e, t) => a({
24
+ apiGatewayUrl: O,
25
+ authToken: k,
26
+ orgId: A,
26
27
  query: e,
27
28
  variables: t
28
29
  }), [
29
- b,
30
- x,
31
- S
32
- ]), H = l(async () => {
33
- if (T) {
34
- M(!0), P(null);
30
+ O,
31
+ k,
32
+ A
33
+ ]), Q = l(async () => {
34
+ if (N) {
35
+ B(!0), H(null);
35
36
  try {
36
- let [e, t] = await Promise.all([V(g, { scopeId: T }), V(_, { scopeId: T })]);
37
- O(e.roles ?? []), A(t.actors ?? []), R((e.roles ?? []).find((e) => /viewer/i.test(e.name))?.id ?? e.roles?.[0]?.id ?? "");
37
+ let [e, t] = await Promise.all([Z(w, { scopeId: N }), Z(T, { scopeId: N })]);
38
+ I(e.roles ?? []), R(t.actors ?? []), K((e.roles ?? []).find((e) => /viewer/i.test(e.name))?.id ?? e.roles?.[0]?.id ?? "");
38
39
  } catch (e) {
39
- P(e instanceof Error ? e.message : "Failed to load team data");
40
+ H(e instanceof Error ? e.message : "Failed to load team data");
40
41
  } finally {
41
- M(!1);
42
+ B(!1);
42
43
  }
43
44
  }
44
- }, [T, V]);
45
+ }, [N, Z]);
45
46
  u(() => {
46
- H();
47
- }, [H]);
48
- let U = d(() => [...k].sort((e, t) => {
47
+ Q();
48
+ }, [Q]);
49
+ let $ = d(() => [...L].sort((e, t) => {
49
50
  let n = Math.max(...e.roleAssignments.map((e) => e.role?.priority ?? 0), 0);
50
51
  return Math.max(...t.roleAssignments.map((e) => e.role?.priority ?? 0), 0) - n;
51
- }), [k]), W = c(d(() => U.map((e) => ({
52
+ }), [L]), ee = c(d(() => $.map((e) => ({
52
53
  actorId: e.actorId,
53
54
  actorType: e.actorType
54
- })), [U])), G = d(() => {
55
- if (T) return C.find((e) => e.id === T)?.name ?? void 0;
56
- }, [T, C]), K = async (e) => {
57
- if (e.preventDefault(), !T || !F.trim() || !L) return;
58
- let t = F.trim();
59
- if (!h.test(t)) {
60
- P("User ID must be a UUID (e.g. 1a2b3c4d-...). Get it from the user profile page.");
55
+ })), [$])), te = d(() => {
56
+ if (N) return j.find((e) => e.id === N)?.name ?? void 0;
57
+ }, [N, j]), ne = async (e) => {
58
+ if (e.preventDefault(), !N || !U.trim() || !G) return;
59
+ let t = U.trim();
60
+ if (!C.test(t)) {
61
+ H("User ID must be a UUID (e.g. 1a2b3c4d-...). Get it from the user profile page.");
61
62
  return;
62
63
  }
63
- B(!0), P(null);
64
+ J(!0), H(null);
64
65
  try {
65
- await V(v, { input: {
66
+ await Z(E, { input: {
66
67
  actorId: t,
67
68
  actorType: "USER",
68
- roleId: L,
69
- scopeId: T
70
- } }), I(""), await H();
69
+ roleId: G,
70
+ scopeId: N
71
+ } }), W(""), await Q();
71
72
  } catch (e) {
72
- P(e instanceof Error ? e.message : "Failed to assign role");
73
+ H(e instanceof Error ? e.message : "Failed to assign role");
73
74
  } finally {
74
- B(!1);
75
+ J(!1);
75
76
  }
76
- }, q = async (e) => {
77
- if (confirm("Remove this role assignment?")) {
78
- P(null);
79
- try {
80
- await V(y, { assignmentId: e }), await H();
81
- } catch (e) {
82
- P(e instanceof Error ? e.message : "Failed to remove role");
83
- }
77
+ }, re = async (e) => {
78
+ H(null);
79
+ try {
80
+ await Z(D, { assignmentId: e }), await Q();
81
+ } catch (e) {
82
+ H(e instanceof Error ? e.message : "Failed to remove role");
84
83
  }
85
84
  };
86
- return E.canManageBillingAccount ? T ? /* @__PURE__ */ m("div", {
85
+ return P.canManageBillingAccount ? N ? /* @__PURE__ */ m("div", {
87
86
  className: "space-y-6",
88
87
  children: [
89
88
  /* @__PURE__ */ p(n, {
@@ -91,39 +90,39 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
91
90
  description: "Control who can manage this billing account and what they can do."
92
91
  }),
93
92
  /* @__PURE__ */ m("div", {
94
- className: "rounded-lg border border-border bg-card p-4 text-sm",
93
+ className: "rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm",
95
94
  children: [
96
95
  /* @__PURE__ */ p("span", {
97
- className: "font-medium text-foreground",
96
+ className: "font-medium text-text-primary",
98
97
  children: "Billing account:"
99
98
  }),
100
99
  " ",
101
100
  /* @__PURE__ */ p("span", {
102
- className: "text-foreground",
103
- children: G ?? "Unnamed account"
101
+ className: "text-text-primary",
102
+ children: te ?? "Unnamed account"
104
103
  }),
105
- T ? /* @__PURE__ */ m("details", {
106
- className: "mt-2 text-xs text-muted-foreground",
104
+ N ? /* @__PURE__ */ m("details", {
105
+ className: "mt-2 text-xs text-text-secondary",
107
106
  children: [/* @__PURE__ */ p("summary", {
108
107
  className: "cursor-pointer",
109
108
  children: "Account ID"
110
109
  }), /* @__PURE__ */ p("span", {
111
110
  className: "mt-1 inline-block font-mono",
112
- children: T
111
+ children: N
113
112
  })]
114
113
  }) : null
115
114
  ]
116
115
  }),
117
- N && /* @__PURE__ */ p("div", {
118
- className: "rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive",
119
- children: N
116
+ V && /* @__PURE__ */ p("div", {
117
+ className: "rounded-lg border border-status-error-border/30 bg-status-error-bg/5 p-4 text-sm text-status-error-text",
118
+ children: V
120
119
  }),
121
120
  /* @__PURE__ */ m("form", {
122
- onSubmit: (e) => void K(e),
123
- className: "rounded-lg border border-border bg-card p-4 space-y-3",
121
+ onSubmit: (e) => void ne(e),
122
+ className: "rounded-lg border border-border-subtle bg-bg-surface p-4 space-y-3",
124
123
  children: [
125
124
  /* @__PURE__ */ p("h3", {
126
- className: "text-sm font-semibold text-foreground",
125
+ className: "text-sm font-semibold text-text-primary",
127
126
  children: "Add member"
128
127
  }),
129
128
  /* @__PURE__ */ m("div", {
@@ -133,34 +132,34 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
133
132
  children: [
134
133
  /* @__PURE__ */ p("label", {
135
134
  htmlFor: "newActorId",
136
- className: "block text-xs font-medium text-muted-foreground",
135
+ className: "block text-xs font-medium text-text-secondary",
137
136
  children: "User ID"
138
137
  }),
139
138
  /* @__PURE__ */ p("input", {
140
139
  id: "newActorId",
141
140
  type: "text",
142
- value: F,
143
- onChange: (e) => I(e.target.value),
141
+ value: U,
142
+ onChange: (e) => W(e.target.value),
144
143
  placeholder: "Paste the user ID from their profile page",
145
- className: "mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm",
144
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm",
146
145
  required: !0
147
146
  }),
148
147
  /* @__PURE__ */ p("p", {
149
- className: "mt-1 text-[11px] text-muted-foreground",
148
+ className: "mt-1 text-[11px] text-text-secondary",
150
149
  children: "Find this on the user's profile page."
151
150
  })
152
151
  ]
153
152
  }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
154
153
  htmlFor: "newRoleId",
155
- className: "block text-xs font-medium text-muted-foreground",
154
+ className: "block text-xs font-medium text-text-secondary",
156
155
  children: "Role"
157
156
  }), /* @__PURE__ */ p("select", {
158
157
  id: "newRoleId",
159
- value: L,
160
- onChange: (e) => R(e.target.value),
161
- className: "mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm",
158
+ value: G,
159
+ onChange: (e) => K(e.target.value),
160
+ className: "mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm",
162
161
  required: !0,
163
- children: D.map((e) => /* @__PURE__ */ p("option", {
162
+ children: F.map((e) => /* @__PURE__ */ p("option", {
164
163
  value: e.id,
165
164
  children: e.name
166
165
  }, e.id))
@@ -168,33 +167,33 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
168
167
  }),
169
168
  /* @__PURE__ */ p("button", {
170
169
  type: "submit",
171
- disabled: z || !F.trim() || !L,
172
- className: "rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground disabled:opacity-50",
173
- children: z ? "Adding…" : "Add member"
170
+ disabled: q || !U.trim() || !G,
171
+ className: "rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text disabled:opacity-50",
172
+ children: q ? "Adding…" : "Add member"
174
173
  })
175
174
  ]
176
175
  }),
177
176
  /* @__PURE__ */ m("div", {
178
- className: "rounded-lg border border-border bg-card",
177
+ className: "rounded-lg border border-border-subtle bg-bg-surface",
179
178
  children: [/* @__PURE__ */ m("div", {
180
- className: "border-b border-border px-4 py-3",
179
+ className: "border-b border-border-subtle px-4 py-3",
181
180
  children: [/* @__PURE__ */ p("h3", {
182
- className: "text-sm font-semibold text-foreground",
181
+ className: "text-sm font-semibold text-text-primary",
183
182
  children: "Members"
184
183
  }), /* @__PURE__ */ p("p", {
185
- className: "text-xs text-muted-foreground",
184
+ className: "text-xs text-text-secondary",
186
185
  children: "Each member's role applies only to this billing account."
187
186
  })]
188
- }), j ? /* @__PURE__ */ p("div", {
189
- className: "p-6 text-sm text-muted-foreground",
187
+ }), z ? /* @__PURE__ */ p("div", {
188
+ className: "p-6 text-sm text-text-secondary",
190
189
  children: "Loading…"
191
- }) : U.length === 0 ? /* @__PURE__ */ p("div", {
192
- className: "p-6 text-sm text-muted-foreground",
190
+ }) : $.length === 0 ? /* @__PURE__ */ p("div", {
191
+ className: "p-6 text-sm text-text-secondary",
193
192
  children: "No members assigned yet."
194
193
  }) : /* @__PURE__ */ m("table", {
195
194
  className: "w-full text-sm",
196
195
  children: [/* @__PURE__ */ p("thead", { children: /* @__PURE__ */ m("tr", {
197
- className: "border-b border-border text-left text-xs text-muted-foreground",
196
+ className: "border-b border-border-subtle text-left text-xs text-text-secondary",
198
197
  children: [
199
198
  /* @__PURE__ */ p("th", {
200
199
  className: "px-4 py-2",
@@ -213,13 +212,13 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
213
212
  children: "Actions"
214
213
  })
215
214
  ]
216
- }) }), /* @__PURE__ */ p("tbody", { children: U.map((e) => /* @__PURE__ */ m("tr", {
217
- className: "border-b border-border last:border-0",
215
+ }) }), /* @__PURE__ */ p("tbody", { children: $.map((e) => /* @__PURE__ */ m("tr", {
216
+ className: "border-b border-border-subtle last:border-0",
218
217
  children: [
219
218
  /* @__PURE__ */ p("td", {
220
219
  className: "px-4 py-3",
221
220
  children: /* @__PURE__ */ p(r, {
222
- profile: W[e.actorId] ?? {
221
+ profile: ee[e.actorId] ?? {
223
222
  actorId: e.actorId,
224
223
  actorType: e.actorType,
225
224
  displayName: e.actorType.toLowerCase() === "user" ? `User ${e.actorId.slice(0, 8)}…` : e.actorType,
@@ -230,7 +229,7 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
230
229
  })
231
230
  }),
232
231
  /* @__PURE__ */ p("td", {
233
- className: "px-4 py-3 text-xs capitalize text-muted-foreground",
232
+ className: "px-4 py-3 text-xs capitalize text-text-secondary",
234
233
  children: e.actorType.toLowerCase()
235
234
  }),
236
235
  /* @__PURE__ */ p("td", {
@@ -238,11 +237,11 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
238
237
  children: /* @__PURE__ */ p("div", {
239
238
  className: "flex flex-wrap gap-1",
240
239
  children: e.roleAssignments.filter((e) => e.isActive).map((e) => /* @__PURE__ */ m("span", {
241
- className: "inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-xs",
240
+ className: "inline-flex items-center gap-1 rounded-full border border-border-subtle bg-bg-sunken px-2 py-0.5 text-xs",
242
241
  children: [e.role?.name ?? e.roleId, /* @__PURE__ */ p("button", {
243
242
  type: "button",
244
- onClick: () => void q(e.id),
245
- className: "text-muted-foreground hover:text-destructive",
243
+ onClick: () => X(e.id),
244
+ className: "text-text-secondary hover:text-status-error-text",
246
245
  "aria-label": `Remove ${e.role?.name ?? "role"}`,
247
246
  children: "×"
248
247
  })]
@@ -252,7 +251,7 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
252
251
  /* @__PURE__ */ p("td", {
253
252
  className: "px-4 py-3 text-right",
254
253
  children: /* @__PURE__ */ m("span", {
255
- className: "text-xs text-muted-foreground",
254
+ className: "text-xs text-text-secondary",
256
255
  children: [e.effectivePermissionCount ?? "—", " permissions"]
257
256
  })
258
257
  })
@@ -261,36 +260,49 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
261
260
  })]
262
261
  }),
263
262
  /* @__PURE__ */ m("div", {
264
- className: "rounded-lg border border-border bg-card p-4",
263
+ className: "rounded-lg border border-border-subtle bg-bg-surface p-4",
265
264
  children: [
266
265
  /* @__PURE__ */ p("h3", {
267
- className: "text-sm font-semibold text-foreground",
266
+ className: "text-sm font-semibold text-text-primary",
268
267
  children: "Available roles"
269
268
  }),
270
269
  /* @__PURE__ */ p("p", {
271
- className: "mt-1 text-xs text-muted-foreground",
270
+ className: "mt-1 text-xs text-text-secondary",
272
271
  children: "Roles are cloned from the system templates when a billing account is created."
273
272
  }),
274
273
  /* @__PURE__ */ p("ul", {
275
274
  className: "mt-3 space-y-2",
276
- children: D.map((e) => /* @__PURE__ */ m("li", {
277
- className: "flex items-start gap-3 rounded-md border border-border bg-background p-3",
275
+ children: F.map((e) => /* @__PURE__ */ m("li", {
276
+ className: "flex items-start gap-3 rounded-md border border-border-subtle bg-bg-surface p-3",
278
277
  children: [/* @__PURE__ */ m("div", {
279
278
  className: "flex-1",
280
279
  children: [/* @__PURE__ */ p("div", {
281
- className: "text-sm font-medium text-foreground",
280
+ className: "text-sm font-medium text-text-primary",
282
281
  children: e.name
283
282
  }), e.description && /* @__PURE__ */ p("p", {
284
- className: "mt-1 text-xs text-muted-foreground",
283
+ className: "mt-1 text-xs text-text-secondary",
285
284
  children: e.description
286
285
  })]
287
286
  }), /* @__PURE__ */ m("span", {
288
- className: "text-xs text-muted-foreground",
287
+ className: "text-xs text-text-secondary",
289
288
  children: ["priority ", e.priority]
290
289
  })]
291
290
  }, e.id))
292
291
  })
293
292
  ]
293
+ }),
294
+ /* @__PURE__ */ p(h, {
295
+ open: Y !== null,
296
+ onOpenChange: (e) => {
297
+ e || X(null);
298
+ },
299
+ children: /* @__PURE__ */ m(v, { children: [/* @__PURE__ */ m(x, { children: [/* @__PURE__ */ p(S, { children: "Remove role assignment?" }), /* @__PURE__ */ p(y, { children: "Remove this role assignment? The member will lose the access this role grants for this billing account." })] }), /* @__PURE__ */ m(b, { children: [/* @__PURE__ */ p(_, { children: "Cancel" }), /* @__PURE__ */ p(g, {
300
+ onClick: () => {
301
+ let e = Y;
302
+ X(null), e && re(e);
303
+ },
304
+ children: "Remove"
305
+ })] })] })
294
306
  })
295
307
  ]
296
308
  }) : /* @__PURE__ */ m("div", {
@@ -299,12 +311,12 @@ var h = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, g = "
299
311
  title: "Team & Permissions",
300
312
  description: "Select a billing account to manage who can access it."
301
313
  }), /* @__PURE__ */ p("div", {
302
- className: "rounded-lg border border-border bg-card p-6 text-sm text-muted-foreground",
314
+ className: "rounded-lg border border-border-subtle bg-bg-surface p-6 text-sm text-text-secondary",
303
315
  children: "No billing account selected. Pick one from the billing account selector to manage its members and roles."
304
316
  })]
305
317
  }) : /* @__PURE__ */ p(t, { message: "You don't have permission to manage team and permissions for this billing account." });
306
318
  };
307
319
  //#endregion
308
- export { b as TeamPermissionsPage };
320
+ export { O as TeamPermissionsPage };
309
321
 
310
322
  //# sourceMappingURL=TeamPermissionsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamPermissionsPage.js","names":[],"sources":["../../../../../src/billing/modules/settings/pages/TeamPermissionsPage.tsx"],"sourcesContent":["/**\n * Settings Module - Team & Permissions Page\n *\n * Lets a Billing Admin manage billing-scope role assignments for a specific\n * billing account. Calls global-rbac-svc directly via the same gateway URL\n * used by the rest of the billing module.\n *\n * Scope: `scopeType: \"billing\"`, `scopeId: <billingAccountId>`\n * Roles cloned per billing account at creation time:\n * - Billing Admin (full access to this billing account)\n * - Billing Viewer (read-only access to this billing account)\n */\nimport { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, ActorIdentity, PageHeader } from '../../../shared/components';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingAccountSelection } from '../../../hooks/useBillingAccountSelection';\nimport { useBillingAccounts } from '../../dashboard/hooks/useDashboard';\nimport { directBillingGraphqlRequest } from '../../../shared/utils';\nimport { useActorProfiles } from '../../../shared/hooks';\n\n// Basic UUID-v1-through-v5 shape — accept any 8-4-4-4-12 hex pattern.\n// We intentionally don't strictly validate the version nibble; the rbac\n// service is the authoritative ID validator.\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ninterface Role {\n id: string;\n name: string;\n description?: string | null;\n priority: number;\n isSystem: boolean;\n}\n\ninterface ActorRoleAssignment {\n id: string;\n actorId: string;\n actorType: string;\n roleId: string;\n scopeId: string;\n isActive: boolean;\n expiresAt?: string | null;\n role?: Role;\n}\n\ninterface ActorRbacSummary {\n actorId: string;\n actorType: string;\n roleAssignments: ActorRoleAssignment[];\n effectivePermissionCount?: number;\n}\n\nconst ROLES_QUERY = `\n query BillingScopeRoles($scopeId: String!) {\n roles(scopeId: $scopeId) {\n id\n name\n description\n priority\n isSystem\n }\n }\n`;\n\nconst ACTORS_QUERY = `\n query BillingScopeActors($scopeId: String!) {\n actors(scopeId: $scopeId) {\n actorId\n actorType\n roleAssignments {\n id\n actorId\n actorType\n roleId\n scopeId\n isActive\n expiresAt\n role { id name priority }\n }\n }\n }\n`;\n\nconst ASSIGN_ROLE_MUTATION = `\n mutation AssignBillingRole($input: AssignRoleInput!) {\n assignRoleToActor(input: $input) {\n id\n actorId\n roleId\n scopeId\n }\n }\n`;\n\nconst REMOVE_ROLE_MUTATION = `\n mutation RemoveBillingRole($assignmentId: String!) {\n removeRoleFromActor(assignmentId: $assignmentId)\n }\n`;\n\nexport const TeamPermissionsPage: FC = () => {\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n // Resolution priority: URL `?billingAccountId=` → persisted localStorage →\n // default/first available account. Pass `billingAccounts` so first-time\n // users with empty localStorage still land on the default account\n // (without it the hook's auto-select effect can't fire and the page\n // dead-ends on \"No billing account selected\").\n const { billingAccounts } = useBillingAccounts();\n const { selectedAccountId } = useBillingAccountSelection({\n orgId,\n billingAccounts,\n syncFromUrl: true,\n urlParamName: 'billingAccountId',\n });\n const billingAccountId = selectedAccountId ?? undefined;\n\n // Share the resolved account with `useBillingPermissions` so its\n // billing-scope permissions fetch observes the same ID. Without this,\n // a first-time user (no URL param, empty localStorage) would see the\n // page resolve to the default account here while the permissions hook\n // still sat at null — billing-scope-only admins would see AccessDenied\n // until a navigation/reload propagated the localStorage write.\n const permissions = useBillingPermissions({ billingAccountIdOverride: billingAccountId });\n\n const [roles, setRoles] = useState<Role[]>([]);\n const [actors, setActors] = useState<ActorRbacSummary[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Add-member form\n const [newActorId, setNewActorId] = useState('');\n const [newRoleId, setNewRoleId] = useState('');\n const [isAdding, setIsAdding] = useState(false);\n\n const gqlPost = useCallback(\n async <T,>(query: string, variables: Record<string, unknown>): Promise<T> => {\n return directBillingGraphqlRequest<T>({\n apiGatewayUrl,\n authToken,\n orgId,\n query,\n variables,\n });\n },\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = useCallback(async () => {\n if (!billingAccountId) return;\n setIsLoading(true);\n setError(null);\n try {\n const [rolesResp, actorsResp] = await Promise.all([\n gqlPost<{ roles: Role[] }>(ROLES_QUERY, { scopeId: billingAccountId }),\n gqlPost<{ actors: ActorRbacSummary[] }>(ACTORS_QUERY, {\n scopeId: billingAccountId,\n }),\n ]);\n setRoles(rolesResp.roles ?? []);\n setActors(actorsResp.actors ?? []);\n // Default the role-picker to \"Billing Viewer\" if available, else first role\n const viewer = (rolesResp.roles ?? []).find((r) => /viewer/i.test(r.name));\n setNewRoleId(viewer?.id ?? rolesResp.roles?.[0]?.id ?? '');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load team data');\n } finally {\n setIsLoading(false);\n }\n }, [billingAccountId, gqlPost]);\n\n useEffect(() => {\n void loadData();\n }, [loadData]);\n\n const sortedActors = useMemo(\n () =>\n [...actors].sort((a, b) => {\n const priA = Math.max(...a.roleAssignments.map((ra) => ra.role?.priority ?? 0), 0);\n const priB = Math.max(...b.roleAssignments.map((ra) => ra.role?.priority ?? 0), 0);\n return priB - priA;\n }),\n [actors]\n );\n\n const profileInputs = useMemo(\n () =>\n sortedActors.map((a) => ({\n actorId: a.actorId,\n actorType: a.actorType,\n })),\n [sortedActors]\n );\n const profileMap = useActorProfiles(profileInputs);\n\n const billingAccountName = useMemo(() => {\n if (!billingAccountId) return undefined;\n return billingAccounts.find((acct) => acct.id === billingAccountId)?.name ?? undefined;\n }, [billingAccountId, billingAccounts]);\n\n const handleAddMember = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!billingAccountId || !newActorId.trim() || !newRoleId) return;\n const trimmedActorId = newActorId.trim();\n if (!UUID_RE.test(trimmedActorId)) {\n setError('User ID must be a UUID (e.g. 1a2b3c4d-...). Get it from the user profile page.');\n return;\n }\n setIsAdding(true);\n setError(null);\n try {\n await gqlPost(ASSIGN_ROLE_MUTATION, {\n input: {\n actorId: trimmedActorId,\n actorType: 'USER',\n roleId: newRoleId,\n scopeId: billingAccountId,\n },\n });\n setNewActorId('');\n await loadData();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to assign role');\n } finally {\n setIsAdding(false);\n }\n };\n\n const handleRemoveAssignment = async (assignmentId: string) => {\n if (!confirm('Remove this role assignment?')) return;\n setError(null);\n try {\n await gqlPost(REMOVE_ROLE_MUTATION, { assignmentId });\n await loadData();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to remove role');\n }\n };\n\n if (!permissions.canManageBillingAccount) {\n return (\n <AccessDenied message=\"You don't have permission to manage team and permissions for this billing account.\" />\n );\n }\n\n if (!billingAccountId) {\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Team & Permissions\"\n description=\"Select a billing account to manage who can access it.\"\n />\n <div className=\"rounded-lg border border-border bg-card p-6 text-sm text-muted-foreground\">\n No billing account selected. Pick one from the billing account selector to manage its\n members and roles.\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Team & Permissions\"\n description=\"Control who can manage this billing account and what they can do.\"\n />\n\n <div className=\"rounded-lg border border-border bg-card p-4 text-sm\">\n <span className=\"font-medium text-foreground\">Billing account:</span>{' '}\n <span className=\"text-foreground\">{billingAccountName ?? 'Unnamed account'}</span>\n {billingAccountId ? (\n <details className=\"mt-2 text-xs text-muted-foreground\">\n <summary className=\"cursor-pointer\">Account ID</summary>\n <span className=\"mt-1 inline-block font-mono\">{billingAccountId}</span>\n </details>\n ) : null}\n </div>\n\n {error && (\n <div className=\"rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm text-destructive\">\n {error}\n </div>\n )}\n\n {/* Add member form */}\n <form\n onSubmit={(e) => void handleAddMember(e)}\n className=\"rounded-lg border border-border bg-card p-4 space-y-3\"\n >\n <h3 className=\"text-sm font-semibold text-foreground\">Add member</h3>\n <div className=\"grid grid-cols-1 gap-3 md:grid-cols-3\">\n <div className=\"md:col-span-2\">\n <label htmlFor=\"newActorId\" className=\"block text-xs font-medium text-muted-foreground\">\n User ID\n </label>\n <input\n id=\"newActorId\"\n type=\"text\"\n value={newActorId}\n onChange={(e) => setNewActorId(e.target.value)}\n placeholder=\"Paste the user ID from their profile page\"\n className=\"mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm\"\n required\n />\n <p className=\"mt-1 text-[11px] text-muted-foreground\">\n Find this on the user's profile page.\n </p>\n </div>\n <div>\n <label htmlFor=\"newRoleId\" className=\"block text-xs font-medium text-muted-foreground\">\n Role\n </label>\n <select\n id=\"newRoleId\"\n value={newRoleId}\n onChange={(e) => setNewRoleId(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border bg-background px-3 py-2 text-sm\"\n required\n >\n {roles.map((r) => (\n <option key={r.id} value={r.id}>\n {r.name}\n </option>\n ))}\n </select>\n </div>\n </div>\n <button\n type=\"submit\"\n disabled={isAdding || !newActorId.trim() || !newRoleId}\n className=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground disabled:opacity-50\"\n >\n {isAdding ? 'Adding…' : 'Add member'}\n </button>\n </form>\n\n {/* Members table */}\n <div className=\"rounded-lg border border-border bg-card\">\n <div className=\"border-b border-border px-4 py-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">Members</h3>\n <p className=\"text-xs text-muted-foreground\">\n Each member's role applies only to this billing account.\n </p>\n </div>\n {isLoading ? (\n <div className=\"p-6 text-sm text-muted-foreground\">Loading…</div>\n ) : sortedActors.length === 0 ? (\n <div className=\"p-6 text-sm text-muted-foreground\">No members assigned yet.</div>\n ) : (\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border text-left text-xs text-muted-foreground\">\n <th className=\"px-4 py-2\">Member</th>\n <th className=\"px-4 py-2\">Type</th>\n <th className=\"px-4 py-2\">Roles</th>\n <th className=\"px-4 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {sortedActors.map((actor) => (\n <tr key={actor.actorId} className=\"border-b border-border last:border-0\">\n <td className=\"px-4 py-3\">\n <ActorIdentity\n profile={\n profileMap[actor.actorId] ?? {\n actorId: actor.actorId,\n actorType: actor.actorType,\n displayName:\n actor.actorType.toLowerCase() === 'user'\n ? `User ${actor.actorId.slice(0, 8)}…`\n : actor.actorType,\n secondaryLabel: actor.actorId.slice(0, 12),\n resolved: false,\n }\n }\n variant=\"cell\"\n />\n </td>\n <td className=\"px-4 py-3 text-xs capitalize text-muted-foreground\">\n {actor.actorType.toLowerCase()}\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex flex-wrap gap-1\">\n {actor.roleAssignments\n .filter((ra) => ra.isActive)\n .map((ra) => (\n <span\n key={ra.id}\n className=\"inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-xs\"\n >\n {ra.role?.name ?? ra.roleId}\n <button\n type=\"button\"\n onClick={() => void handleRemoveAssignment(ra.id)}\n className=\"text-muted-foreground hover:text-destructive\"\n aria-label={`Remove ${ra.role?.name ?? 'role'}`}\n >\n ×\n </button>\n </span>\n ))}\n </div>\n </td>\n <td className=\"px-4 py-3 text-right\">\n <span className=\"text-xs text-muted-foreground\">\n {actor.effectivePermissionCount ?? '—'} permissions\n </span>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n )}\n </div>\n\n {/* Roles reference */}\n <div className=\"rounded-lg border border-border bg-card p-4\">\n <h3 className=\"text-sm font-semibold text-foreground\">Available roles</h3>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n Roles are cloned from the system templates when a billing account is created.\n </p>\n <ul className=\"mt-3 space-y-2\">\n {roles.map((r) => (\n <li\n key={r.id}\n className=\"flex items-start gap-3 rounded-md border border-border bg-background p-3\"\n >\n <div className=\"flex-1\">\n <div className=\"text-sm font-medium text-foreground\">{r.name}</div>\n {r.description && (\n <p className=\"mt-1 text-xs text-muted-foreground\">{r.description}</p>\n )}\n </div>\n <span className=\"text-xs text-muted-foreground\">priority {r.priority}</span>\n </li>\n ))}\n </ul>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;AAwBA,IAAM,IAAU,mEA4BV,IAAc,2KAYd,IAAe,+TAmBf,IAAuB,0KAWvB,IAAuB,yHAMhB,UAAgC;CAC3C,IAAM,EAAE,kBAAe,cAAW,aAAU,GAAY,EAOlD,EAAE,uBAAoB,GAAoB,EAC1C,EAAE,yBAAsB,EAA2B;EACvD;EACA;EACA,aAAa;EACb,cAAc;EACf,CAAC,EACI,IAAmB,KAAqB,KAAA,GAQxC,IAAc,EAAsB,EAAE,0BAA0B,GAAkB,CAAC,EAEnF,CAAC,GAAO,KAAY,EAAiB,EAAE,CAAC,EACxC,CAAC,GAAQ,KAAa,EAA6B,EAAE,CAAC,EACtD,CAAC,GAAW,KAAgB,EAAS,GAAM,EAC3C,CAAC,GAAO,KAAY,EAAwB,KAAK,EAGjD,CAAC,GAAY,KAAiB,EAAS,GAAG,EAC1C,CAAC,GAAW,KAAgB,EAAS,GAAG,EACxC,CAAC,GAAU,KAAe,EAAS,GAAM,EAEzC,IAAU,EACd,OAAW,GAAe,MACjB,EAA+B;EACpC;EACA;EACA;EACA;EACA;EACD,CAAC,EAEJ;EAAC;EAAe;EAAW;EAAM,CAClC,EAEK,IAAW,EAAY,YAAY;AAClC,SAEL;GADA,EAAa,GAAK,EAClB,EAAS,KAAK;AACd,OAAI;IACF,IAAM,CAAC,GAAW,KAAc,MAAM,QAAQ,IAAI,CAChD,EAA2B,GAAa,EAAE,SAAS,GAAkB,CAAC,EACtE,EAAwC,GAAc,EACpD,SAAS,GACV,CAAC,CACH,CAAC;AAKF,IAJA,EAAS,EAAU,SAAS,EAAE,CAAC,EAC/B,EAAU,EAAW,UAAU,EAAE,CAAC,EAGlC,GADgB,EAAU,SAAS,EAAE,EAAE,MAAM,MAAM,UAAU,KAAK,EAAE,KAAK,CAAC,EACrD,MAAM,EAAU,QAAQ,IAAI,MAAM,GAAG;YACnD,GAAK;AACZ,MAAS,aAAe,QAAQ,EAAI,UAAU,2BAA2B;aACjE;AACR,MAAa,GAAM;;;IAEpB,CAAC,GAAkB,EAAQ,CAAC;AAE/B,SAAgB;AACT,KAAU;IACd,CAAC,EAAS,CAAC;CAEd,IAAM,IAAe,QAEjB,CAAC,GAAG,EAAO,CAAC,MAAM,GAAG,MAAM;EACzB,IAAM,IAAO,KAAK,IAAI,GAAG,EAAE,gBAAgB,KAAK,MAAO,EAAG,MAAM,YAAY,EAAE,EAAE,EAAE;AAElF,SADa,KAAK,IAAI,GAAG,EAAE,gBAAgB,KAAK,MAAO,EAAG,MAAM,YAAY,EAAE,EAAE,EAAE,GACpE;GACd,EACJ,CAAC,EAAO,CACT,EAUK,IAAa,EARG,QAElB,EAAa,KAAK,OAAO;EACvB,SAAS,EAAE;EACX,WAAW,EAAE;EACd,EAAE,EACL,CAAC,EAAa,CACf,CACiD,EAE5C,IAAqB,QAAc;AAClC,QACL,QAAO,EAAgB,MAAM,MAAS,EAAK,OAAO,EAAiB,EAAE,QAAQ,KAAA;IAC5E,CAAC,GAAkB,EAAgB,CAAC,EAEjC,IAAkB,OAAO,MAAuB;AAEpD,MADA,EAAE,gBAAgB,EACd,CAAC,KAAoB,CAAC,EAAW,MAAM,IAAI,CAAC,EAAW;EAC3D,IAAM,IAAiB,EAAW,MAAM;AACxC,MAAI,CAAC,EAAQ,KAAK,EAAe,EAAE;AACjC,KAAS,iFAAiF;AAC1F;;AAGF,EADA,EAAY,GAAK,EACjB,EAAS,KAAK;AACd,MAAI;AAUF,GATA,MAAM,EAAQ,GAAsB,EAClC,OAAO;IACL,SAAS;IACT,WAAW;IACX,QAAQ;IACR,SAAS;IACV,EACF,CAAC,EACF,EAAc,GAAG,EACjB,MAAM,GAAU;WACT,GAAK;AACZ,KAAS,aAAe,QAAQ,EAAI,UAAU,wBAAwB;YAC9D;AACR,KAAY,GAAM;;IAIhB,IAAyB,OAAO,MAAyB;AACxD,cAAQ,+BAA+B,EAC5C;KAAS,KAAK;AACd,OAAI;AAEF,IADA,MAAM,EAAQ,GAAsB,EAAE,iBAAc,CAAC,EACrD,MAAM,GAAU;YACT,GAAK;AACZ,MAAS,aAAe,QAAQ,EAAI,UAAU,wBAAwB;;;;AAyB1E,QArBK,EAAY,0BAMZ,IAgBH,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,GAAD;IACE,OAAM;IACN,aAAY;IACZ,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,QAAD;MAAM,WAAU;gBAA8B;MAAuB,CAAA;KAAC;KACtE,kBAAC,QAAD;MAAM,WAAU;gBAAmB,KAAsB;MAAyB,CAAA;KACjF,IACC,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,WAAD;OAAS,WAAU;iBAAiB;OAAoB,CAAA,EACxD,kBAAC,QAAD;OAAM,WAAU;iBAA+B;OAAwB,CAAA,CAC/D;UACR;KACA;;GAEL,KACC,kBAAC,OAAD;IAAK,WAAU;cACZ;IACG,CAAA;GAIR,kBAAC,QAAD;IACE,WAAW,MAAM,KAAK,EAAgB,EAAE;IACxC,WAAU;cAFZ;KAIE,kBAAC,MAAD;MAAI,WAAU;gBAAwC;MAAe,CAAA;KACrE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,SAAD;SAAO,SAAQ;SAAa,WAAU;mBAAkD;SAEhF,CAAA;QACR,kBAAC,SAAD;SACE,IAAG;SACH,MAAK;SACL,OAAO;SACP,WAAW,MAAM,EAAc,EAAE,OAAO,MAAM;SAC9C,aAAY;SACZ,WAAU;SACV,UAAA;SACA,CAAA;QACF,kBAAC,KAAD;SAAG,WAAU;mBAAyC;SAElD,CAAA;QACA;UACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;OAAO,SAAQ;OAAY,WAAU;iBAAkD;OAE/E,CAAA,EACR,kBAAC,UAAD;OACE,IAAG;OACH,OAAO;OACP,WAAW,MAAM,EAAa,EAAE,OAAO,MAAM;OAC7C,WAAU;OACV,UAAA;iBAEC,EAAM,KAAK,MACV,kBAAC,UAAD;QAAmB,OAAO,EAAE;kBACzB,EAAE;QACI,EAFI,EAAE,GAEN,CACT;OACK,CAAA,CACL,EAAA,CAAA,CACF;;KACN,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,KAAY,CAAC,EAAW,MAAM,IAAI,CAAC;MAC7C,WAAU;gBAET,IAAW,YAAY;MACjB,CAAA;KACJ;;GAGP,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAwC;MAAY,CAAA,EAClE,kBAAC,KAAD;MAAG,WAAU;gBAAgC;MAEzC,CAAA,CACA;QACL,IACC,kBAAC,OAAD;KAAK,WAAU;eAAoC;KAAc,CAAA,GAC/D,EAAa,WAAW,IAC1B,kBAAC,OAAD;KAAK,WAAU;eAAoC;KAA8B,CAAA,GAEjF,kBAAC,SAAD;KAAO,WAAU;eAAjB,CACE,kBAAC,SAAD,EAAA,UACE,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,MAAD;QAAI,WAAU;kBAAY;QAAW,CAAA;OACrC,kBAAC,MAAD;QAAI,WAAU;kBAAY;QAAS,CAAA;OACnC,kBAAC,MAAD;QAAI,WAAU;kBAAY;QAAU,CAAA;OACpC,kBAAC,MAAD;QAAI,WAAU;kBAAuB;QAAY,CAAA;OAC9C;SACC,CAAA,EACR,kBAAC,SAAD,EAAA,UACG,EAAa,KAAK,MACjB,kBAAC,MAAD;MAAwB,WAAU;gBAAlC;OACE,kBAAC,MAAD;QAAI,WAAU;kBACZ,kBAAC,GAAD;SACE,SACE,EAAW,EAAM,YAAY;UAC3B,SAAS,EAAM;UACf,WAAW,EAAM;UACjB,aACE,EAAM,UAAU,aAAa,KAAK,SAC9B,QAAQ,EAAM,QAAQ,MAAM,GAAG,EAAE,CAAC,KAClC,EAAM;UACZ,gBAAgB,EAAM,QAAQ,MAAM,GAAG,GAAG;UAC1C,UAAU;UACX;SAEH,SAAQ;SACR,CAAA;QACC,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAM,UAAU,aAAa;QAC3B,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBACZ,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAM,gBACJ,QAAQ,MAAO,EAAG,SAAS,CAC3B,KAAK,MACJ,kBAAC,QAAD;UAEE,WAAU;oBAFZ,CAIG,EAAG,MAAM,QAAQ,EAAG,QACrB,kBAAC,UAAD;WACE,MAAK;WACL,eAAe,KAAK,EAAuB,EAAG,GAAG;WACjD,WAAU;WACV,cAAY,UAAU,EAAG,MAAM,QAAQ;qBACxC;WAEQ,CAAA,CACJ;YAZA,EAAG,GAYH,CACP;SACA,CAAA;QACH,CAAA;OACL,kBAAC,MAAD;QAAI,WAAU;kBACZ,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACG,EAAM,4BAA4B,KAAI,eAClC;;QACJ,CAAA;OACF;QAhDI,EAAM,QAgDV,CACL,EACI,CAAA,CACF;OAEN;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,MAAD;MAAI,WAAU;gBAAwC;MAAoB,CAAA;KAC1E,kBAAC,KAAD;MAAG,WAAU;gBAAqC;MAE9C,CAAA;KACJ,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAM,KAAK,MACV,kBAAC,MAAD;OAEE,WAAU;iBAFZ,CAIE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,OAAD;SAAK,WAAU;mBAAuC,EAAE;SAAW,CAAA,EAClE,EAAE,eACD,kBAAC,KAAD;SAAG,WAAU;mBAAsC,EAAE;SAAgB,CAAA,CAEnE;WACN,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAAgD,aAAU,EAAE,SAAgB;UACzE;SAVE,EAAE,GAUJ,CACL;MACC,CAAA;KACD;;GACF;MA/LJ,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,OAAM;GACN,aAAY;GACZ,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aAA4E;GAGrF,CAAA,CACF;MAfN,kBAAC,GAAD,EAAc,SAAQ,sFAAuF,CAAA"}
1
+ {"version":3,"file":"TeamPermissionsPage.js","names":[],"sources":["../../../../../src/billing/modules/settings/pages/TeamPermissionsPage.tsx"],"sourcesContent":["/**\n * Settings Module - Team & Permissions Page\n *\n * Lets a Billing Admin manage billing-scope role assignments for a specific\n * billing account. Calls global-rbac-svc directly via the same gateway URL\n * used by the rest of the billing module.\n *\n * Scope: `scopeType: \"billing\"`, `scopeId: <billingAccountId>`\n * Roles cloned per billing account at creation time:\n * - Billing Admin (full access to this billing account)\n * - Billing Viewer (read-only access to this billing account)\n */\nimport { useCallback, useEffect, useMemo, useState, type FC } from 'react';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, ActorIdentity, PageHeader } from '../../../shared/components';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingAccountSelection } from '../../../hooks/useBillingAccountSelection';\nimport { useBillingAccounts } from '../../dashboard/hooks/useDashboard';\nimport { directBillingGraphqlRequest } from '../../../shared/utils';\nimport { useActorProfiles } from '../../../shared/hooks';\n\n// Basic UUID-v1-through-v5 shape — accept any 8-4-4-4-12 hex pattern.\n// We intentionally don't strictly validate the version nibble; the rbac\n// service is the authoritative ID validator.\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\ninterface Role {\n id: string;\n name: string;\n description?: string | null;\n priority: number;\n isSystem: boolean;\n}\n\ninterface ActorRoleAssignment {\n id: string;\n actorId: string;\n actorType: string;\n roleId: string;\n scopeId: string;\n isActive: boolean;\n expiresAt?: string | null;\n role?: Role;\n}\n\ninterface ActorRbacSummary {\n actorId: string;\n actorType: string;\n roleAssignments: ActorRoleAssignment[];\n effectivePermissionCount?: number;\n}\n\nconst ROLES_QUERY = `\n query BillingScopeRoles($scopeId: String!) {\n roles(scopeId: $scopeId) {\n id\n name\n description\n priority\n isSystem\n }\n }\n`;\n\nconst ACTORS_QUERY = `\n query BillingScopeActors($scopeId: String!) {\n actors(scopeId: $scopeId) {\n actorId\n actorType\n roleAssignments {\n id\n actorId\n actorType\n roleId\n scopeId\n isActive\n expiresAt\n role { id name priority }\n }\n }\n }\n`;\n\nconst ASSIGN_ROLE_MUTATION = `\n mutation AssignBillingRole($input: AssignRoleInput!) {\n assignRoleToActor(input: $input) {\n id\n actorId\n roleId\n scopeId\n }\n }\n`;\n\nconst REMOVE_ROLE_MUTATION = `\n mutation RemoveBillingRole($assignmentId: String!) {\n removeRoleFromActor(assignmentId: $assignmentId)\n }\n`;\n\nexport const TeamPermissionsPage: FC = () => {\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n // Resolution priority: URL `?billingAccountId=` → persisted localStorage →\n // default/first available account. Pass `billingAccounts` so first-time\n // users with empty localStorage still land on the default account\n // (without it the hook's auto-select effect can't fire and the page\n // dead-ends on \"No billing account selected\").\n const { billingAccounts } = useBillingAccounts();\n const { selectedAccountId } = useBillingAccountSelection({\n orgId,\n billingAccounts,\n syncFromUrl: true,\n urlParamName: 'billingAccountId',\n });\n const billingAccountId = selectedAccountId ?? undefined;\n\n // Share the resolved account with `useBillingPermissions` so its\n // billing-scope permissions fetch observes the same ID. Without this,\n // a first-time user (no URL param, empty localStorage) would see the\n // page resolve to the default account here while the permissions hook\n // still sat at null — billing-scope-only admins would see AccessDenied\n // until a navigation/reload propagated the localStorage write.\n const permissions = useBillingPermissions({ billingAccountIdOverride: billingAccountId });\n\n const [roles, setRoles] = useState<Role[]>([]);\n const [actors, setActors] = useState<ActorRbacSummary[]>([]);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n // Add-member form\n const [newActorId, setNewActorId] = useState('');\n const [newRoleId, setNewRoleId] = useState('');\n const [isAdding, setIsAdding] = useState(false);\n const [removeAssignmentId, setRemoveAssignmentId] = useState<string | null>(null);\n\n const gqlPost = useCallback(\n async <T,>(query: string, variables: Record<string, unknown>): Promise<T> => {\n return directBillingGraphqlRequest<T>({\n apiGatewayUrl,\n authToken,\n orgId,\n query,\n variables,\n });\n },\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = useCallback(async () => {\n if (!billingAccountId) return;\n setIsLoading(true);\n setError(null);\n try {\n const [rolesResp, actorsResp] = await Promise.all([\n gqlPost<{ roles: Role[] }>(ROLES_QUERY, { scopeId: billingAccountId }),\n gqlPost<{ actors: ActorRbacSummary[] }>(ACTORS_QUERY, {\n scopeId: billingAccountId,\n }),\n ]);\n setRoles(rolesResp.roles ?? []);\n setActors(actorsResp.actors ?? []);\n // Default the role-picker to \"Billing Viewer\" if available, else first role\n const viewer = (rolesResp.roles ?? []).find((r) => /viewer/i.test(r.name));\n setNewRoleId(viewer?.id ?? rolesResp.roles?.[0]?.id ?? '');\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load team data');\n } finally {\n setIsLoading(false);\n }\n }, [billingAccountId, gqlPost]);\n\n useEffect(() => {\n void loadData();\n }, [loadData]);\n\n const sortedActors = useMemo(\n () =>\n [...actors].sort((a, b) => {\n const priA = Math.max(...a.roleAssignments.map((ra) => ra.role?.priority ?? 0), 0);\n const priB = Math.max(...b.roleAssignments.map((ra) => ra.role?.priority ?? 0), 0);\n return priB - priA;\n }),\n [actors]\n );\n\n const profileInputs = useMemo(\n () =>\n sortedActors.map((a) => ({\n actorId: a.actorId,\n actorType: a.actorType,\n })),\n [sortedActors]\n );\n const profileMap = useActorProfiles(profileInputs);\n\n const billingAccountName = useMemo(() => {\n if (!billingAccountId) return undefined;\n return billingAccounts.find((acct) => acct.id === billingAccountId)?.name ?? undefined;\n }, [billingAccountId, billingAccounts]);\n\n const handleAddMember = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!billingAccountId || !newActorId.trim() || !newRoleId) return;\n const trimmedActorId = newActorId.trim();\n if (!UUID_RE.test(trimmedActorId)) {\n setError('User ID must be a UUID (e.g. 1a2b3c4d-...). Get it from the user profile page.');\n return;\n }\n setIsAdding(true);\n setError(null);\n try {\n await gqlPost(ASSIGN_ROLE_MUTATION, {\n input: {\n actorId: trimmedActorId,\n actorType: 'USER',\n roleId: newRoleId,\n scopeId: billingAccountId,\n },\n });\n setNewActorId('');\n await loadData();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to assign role');\n } finally {\n setIsAdding(false);\n }\n };\n\n const handleRemoveAssignment = async (assignmentId: string) => {\n setError(null);\n try {\n await gqlPost(REMOVE_ROLE_MUTATION, { assignmentId });\n await loadData();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to remove role');\n }\n };\n\n if (!permissions.canManageBillingAccount) {\n return (\n <AccessDenied message=\"You don't have permission to manage team and permissions for this billing account.\" />\n );\n }\n\n if (!billingAccountId) {\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Team & Permissions\"\n description=\"Select a billing account to manage who can access it.\"\n />\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-6 text-sm text-text-secondary\">\n No billing account selected. Pick one from the billing account selector to manage its\n members and roles.\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6\">\n <PageHeader\n title=\"Team & Permissions\"\n description=\"Control who can manage this billing account and what they can do.\"\n />\n\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 text-sm\">\n <span className=\"font-medium text-text-primary\">Billing account:</span>{' '}\n <span className=\"text-text-primary\">{billingAccountName ?? 'Unnamed account'}</span>\n {billingAccountId ? (\n <details className=\"mt-2 text-xs text-text-secondary\">\n <summary className=\"cursor-pointer\">Account ID</summary>\n <span className=\"mt-1 inline-block font-mono\">{billingAccountId}</span>\n </details>\n ) : null}\n </div>\n\n {error && (\n <div className=\"rounded-lg border border-status-error-border/30 bg-status-error-bg/5 p-4 text-sm text-status-error-text\">\n {error}\n </div>\n )}\n\n {/* Add member form */}\n <form\n onSubmit={(e) => void handleAddMember(e)}\n className=\"rounded-lg border border-border-subtle bg-bg-surface p-4 space-y-3\"\n >\n <h3 className=\"text-sm font-semibold text-text-primary\">Add member</h3>\n <div className=\"grid grid-cols-1 gap-3 md:grid-cols-3\">\n <div className=\"md:col-span-2\">\n <label htmlFor=\"newActorId\" className=\"block text-xs font-medium text-text-secondary\">\n User ID\n </label>\n <input\n id=\"newActorId\"\n type=\"text\"\n value={newActorId}\n onChange={(e) => setNewActorId(e.target.value)}\n placeholder=\"Paste the user ID from their profile page\"\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm\"\n required\n />\n <p className=\"mt-1 text-[11px] text-text-secondary\">\n Find this on the user's profile page.\n </p>\n </div>\n <div>\n <label htmlFor=\"newRoleId\" className=\"block text-xs font-medium text-text-secondary\">\n Role\n </label>\n <select\n id=\"newRoleId\"\n value={newRoleId}\n onChange={(e) => setNewRoleId(e.target.value)}\n className=\"mt-1 w-full rounded-md border border-border-subtle bg-bg-surface px-3 py-2 text-sm\"\n required\n >\n {roles.map((r) => (\n <option key={r.id} value={r.id}>\n {r.name}\n </option>\n ))}\n </select>\n </div>\n </div>\n <button\n type=\"submit\"\n disabled={isAdding || !newActorId.trim() || !newRoleId}\n className=\"rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text disabled:opacity-50\"\n >\n {isAdding ? 'Adding…' : 'Add member'}\n </button>\n </form>\n\n {/* Members table */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface\">\n <div className=\"border-b border-border-subtle px-4 py-3\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Members</h3>\n <p className=\"text-xs text-text-secondary\">\n Each member's role applies only to this billing account.\n </p>\n </div>\n {isLoading ? (\n <div className=\"p-6 text-sm text-text-secondary\">Loading…</div>\n ) : sortedActors.length === 0 ? (\n <div className=\"p-6 text-sm text-text-secondary\">No members assigned yet.</div>\n ) : (\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b border-border-subtle text-left text-xs text-text-secondary\">\n <th className=\"px-4 py-2\">Member</th>\n <th className=\"px-4 py-2\">Type</th>\n <th className=\"px-4 py-2\">Roles</th>\n <th className=\"px-4 py-2 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {sortedActors.map((actor) => (\n <tr key={actor.actorId} className=\"border-b border-border-subtle last:border-0\">\n <td className=\"px-4 py-3\">\n <ActorIdentity\n profile={\n profileMap[actor.actorId] ?? {\n actorId: actor.actorId,\n actorType: actor.actorType,\n displayName:\n actor.actorType.toLowerCase() === 'user'\n ? `User ${actor.actorId.slice(0, 8)}…`\n : actor.actorType,\n secondaryLabel: actor.actorId.slice(0, 12),\n resolved: false,\n }\n }\n variant=\"cell\"\n />\n </td>\n <td className=\"px-4 py-3 text-xs capitalize text-text-secondary\">\n {actor.actorType.toLowerCase()}\n </td>\n <td className=\"px-4 py-3\">\n <div className=\"flex flex-wrap gap-1\">\n {actor.roleAssignments\n .filter((ra) => ra.isActive)\n .map((ra) => (\n <span\n key={ra.id}\n className=\"inline-flex items-center gap-1 rounded-full border border-border-subtle bg-bg-sunken px-2 py-0.5 text-xs\"\n >\n {ra.role?.name ?? ra.roleId}\n <button\n type=\"button\"\n onClick={() => setRemoveAssignmentId(ra.id)}\n className=\"text-text-secondary hover:text-status-error-text\"\n aria-label={`Remove ${ra.role?.name ?? 'role'}`}\n >\n ×\n </button>\n </span>\n ))}\n </div>\n </td>\n <td className=\"px-4 py-3 text-right\">\n <span className=\"text-xs text-text-secondary\">\n {actor.effectivePermissionCount ?? '—'} permissions\n </span>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n )}\n </div>\n\n {/* Roles reference */}\n <div className=\"rounded-lg border border-border-subtle bg-bg-surface p-4\">\n <h3 className=\"text-sm font-semibold text-text-primary\">Available roles</h3>\n <p className=\"mt-1 text-xs text-text-secondary\">\n Roles are cloned from the system templates when a billing account is created.\n </p>\n <ul className=\"mt-3 space-y-2\">\n {roles.map((r) => (\n <li\n key={r.id}\n className=\"flex items-start gap-3 rounded-md border border-border-subtle bg-bg-surface p-3\"\n >\n <div className=\"flex-1\">\n <div className=\"text-sm font-medium text-text-primary\">{r.name}</div>\n {r.description && (\n <p className=\"mt-1 text-xs text-text-secondary\">{r.description}</p>\n )}\n </div>\n <span className=\"text-xs text-text-secondary\">priority {r.priority}</span>\n </li>\n ))}\n </ul>\n </div>\n\n <AlertDialog\n open={removeAssignmentId !== null}\n onOpenChange={(open) => {\n if (!open) setRemoveAssignmentId(null);\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Remove role assignment?</AlertDialogTitle>\n <AlertDialogDescription>\n Remove this role assignment? The member will lose the access this role grants for\n this billing account.\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction\n onClick={() => {\n const id = removeAssignmentId;\n setRemoveAssignmentId(null);\n if (id) void handleRemoveAssignment(id);\n }}\n >\n Remove\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </div>\n );\n};\n"],"mappings":"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"}