@burdenoff/microfe-billing 2026.1005.6 → 2026.1012.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.
@@ -3,137 +3,139 @@ import { useTr as t } from "../../../shared/hooks/useTr.js";
3
3
  import { AccessDenied as n } from "../../../shared/components/AccessDenied.js";
4
4
  import { StatCard as r } from "../../../shared/components/StatCard.js";
5
5
  import { ServerError as ee } from "../../../shared/components/ServerError.js";
6
- import { useBillingEventEmitter as te } from "../../../hooks/useBillingEventEmitter.js";
7
- import { getStoredBillingAccountId as ne, setStoredBillingAccountId as re } from "../../../hooks/useBillingAccountSelection.js";
8
- import { isServerError as ie } from "../../../shared/utils/error.js";
6
+ import { PushPermissionNudge as te } from "../../../shared/components/PushPermissionNudge.js";
7
+ import { useBillingEventEmitter as ne } from "../../../hooks/useBillingEventEmitter.js";
8
+ import { getStoredBillingAccountId as re, setStoredBillingAccountId as ie } from "../../../hooks/useBillingAccountSelection.js";
9
+ import { isServerError as ae } from "../../../shared/utils/error.js";
9
10
  import "../../../shared/utils/index.js";
10
- import { useBillingPermissions as ae } from "../../../hooks/useBillingPermissions.js";
11
- import { useDashboardMutations as oe } from "../hooks/useDashboard.js";
12
- import { useBillingOverviewData as se } from "../hooks/useBillingOverviewData.js";
11
+ import { useBillingPermissions as oe } from "../../../hooks/useBillingPermissions.js";
12
+ import { useDashboardMutations as se } from "../hooks/useDashboard.js";
13
+ import { useBillingOverviewData as ce } from "../hooks/useBillingOverviewData.js";
13
14
  import "../hooks/index.js";
14
- import { BillingAccountCard as ce } from "../components/BillingAccountCard.js";
15
- import { PaymentMethodsSection as le } from "../components/PaymentMethodsSection.js";
16
- import { ActiveSubscriptionCard as ue } from "../components/ActiveSubscriptionCard.js";
17
- import { RecommendedAddonsSection as de } from "../components/RecommendedAddonsSection.js";
15
+ import { BillingAccountCard as le } from "../components/BillingAccountCard.js";
16
+ import { PaymentMethodsSection as ue } from "../components/PaymentMethodsSection.js";
17
+ import { ActiveSubscriptionCard as de } from "../components/ActiveSubscriptionCard.js";
18
+ import { RecommendedAddonsSection as fe } from "../components/RecommendedAddonsSection.js";
18
19
  import { CreateBillingAccountModal as i } from "../components/CreateBillingAccountModal.js";
19
- import { AddPaymentMethodModal as fe } from "../components/AddPaymentMethodModal.js";
20
+ import { AddPaymentMethodModal as pe } from "../components/AddPaymentMethodModal.js";
20
21
  import "../components/index.js";
21
- import { useAddonCartStore as pe } from "../../addons/store/addonCartStore.js";
22
- import { FloatingCartDrawer as me } from "../../addons/components/FloatingCartDrawer.js";
22
+ import { useAddonCartStore as me } from "../../addons/store/addonCartStore.js";
23
+ import { FloatingCartDrawer as he } from "../../addons/components/FloatingCartDrawer.js";
23
24
  import { withBillingAccountId as a } from "../../../shared/utils/navigation.js";
24
25
  import { useCallback as o, useEffect as s, useState as c } from "react";
25
- import { Activity as he, ArrowRight as ge, Building2 as l, Check as u, ChevronDown as _e, Coins as ve, Crown as ye, Loader2 as be, Plus as d, Receipt as xe, RefreshCw as f, Sparkles as Se, Wallet as p } from "lucide-react";
26
- import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
27
- import { AuroraBackground as _, Button as Ce, CTAOverflowMenu as we, GlassCard as Te, GradientText as v, IllustratedEmptyState as Ee, PagePurpose as De } from "@burdenoff/fe-libs/ui";
26
+ import { Activity as ge, ArrowRight as _e, Building2 as l, Check as u, ChevronDown as d, Coins as ve, Crown as f, Loader2 as ye, Plus as be, Receipt as xe, RefreshCw as p, Sparkles as Se, Wallet as m } from "lucide-react";
27
+ import { Fragment as Ce, jsx as h, jsxs as g } from "react/jsx-runtime";
28
+ import { AuroraBackground as _, Button as we, CTAOverflowMenu as Te, GlassCard as Ee, GradientText as v, IllustratedEmptyState as De, PagePurpose as Oe } from "@burdenoff/fe-libs/ui";
29
+ import { shouldPromptForPushPermission as ke } from "@burdenoff/fe-libs/shared/native";
28
30
  //#region src/billing/modules/dashboard/pages/OverviewPage.tsx
29
31
  var y = () => {
30
- let y = t(), { navigate: b, basePath: x, orgId: S, productId: Oe } = e(), C = ae(), { emit: w } = te(), [T, ke] = c(() => ne(S)), [Ae, E] = c(!1), [D, O] = c(null), [k, A] = c(!1), [je, j] = c(!1), [Me, M] = c(0), [N, P] = c(!1), { billingAccounts: F, currentAccount: I, paymentMethods: L, activeSubscriptions: R, hasActiveSubscription: z, recommendedAddons: Ne, isLoading: B, error: V, refetchAll: H } = se(T, Oe), U = I?.id, Pe = B, W = o((e) => {
31
- ke(e), re(S, e);
32
- }, [S]), { addToCart: G, isInCart: K, getCartItem: q, updateQuantity: J, removeFromCart: Y } = pe(), { createBillingAccount: X, setBillingAddress: Fe, setDefaultPaymentMethod: Ie, deletePaymentMethod: Le, isCreatingAccount: Re } = oe();
32
+ let y = t(), { navigate: b, basePath: x, orgId: S, productId: Ae } = e(), C = oe(), { emit: w } = ne(), [T, je] = c(() => re(S)), [E, D] = c(!1), [O, k] = c(null), [Me, A] = c(!1), [j, M] = c(!1), [Ne, N] = c(!1), [P, F] = c(0), [I, L] = c(!1), { billingAccounts: R, currentAccount: z, paymentMethods: B, activeSubscriptions: V, hasActiveSubscription: H, recommendedAddons: Pe, isLoading: U, error: W, refetchAll: G } = ce(T, Ae), K = z?.id, Fe = U, q = o((e) => {
33
+ je(e), ie(S, e);
34
+ }, [S]), { addToCart: J, isInCart: Y, getCartItem: Ie, updateQuantity: Le, removeFromCart: X } = me(), { createBillingAccount: Re, setBillingAddress: ze, setDefaultPaymentMethod: Be, deletePaymentMethod: Ve, isCreatingAccount: He } = se();
33
35
  s(() => {
34
- M(0), P(!1);
35
- }, [U, R.length]), s(() => {
36
- T && F.length > 0 && (F.some((e) => e.id === T) || W(null));
36
+ F(0), L(!1);
37
+ }, [K, V.length]), s(() => {
38
+ T && R.length > 0 && (R.some((e) => e.id === T) || q(null));
37
39
  }, [
38
- F,
40
+ R,
39
41
  T,
40
- W
42
+ q
41
43
  ]), s(() => {
42
- !T && F.length > 0 && W((F.find((e) => e.isDefault) ?? F[0]).id);
44
+ !T && R.length > 0 && q((R.find((e) => e.isDefault) ?? R[0]).id);
43
45
  }, [
44
- F,
46
+ R,
45
47
  T,
46
- W
48
+ q
47
49
  ]);
48
- let Z = R[Me] || R[0] || null, Q = o((e) => {
50
+ let Z = V[P] || V[0] || null, Q = o((e) => {
49
51
  let t = e;
50
52
  x && x !== "/" && (t = `${x.endsWith("/") ? x.slice(0, -1) : x}${e.startsWith("/") ? e : `/${e}`}`), b(t);
51
- }, [b, x]), ze = o(async (e, t) => {
52
- O(null);
53
+ }, [b, x]), Ue = o(async (e, t) => {
54
+ k(null);
53
55
  try {
54
- let n = await X(e);
55
- t && n.id && await Fe({
56
+ let n = await Re(e);
57
+ t && n.id && await ze({
56
58
  billingAccountId: n.id,
57
59
  ...t
58
- }), E(!1), W(n.id), w("billing.billing_account.created", {
60
+ }), D(!1), q(n.id), w("billing.billing_account.created", {
59
61
  route: "/billing/overview",
60
62
  entityId: n.id,
61
63
  source: "overview"
62
- }), await H();
64
+ }), await G(), ke() && A(!0);
63
65
  } catch (e) {
64
- O(e instanceof Error ? e.message : "Failed to create billing account");
66
+ k(e instanceof Error ? e.message : "Failed to create billing account");
65
67
  }
66
68
  }, [
67
- X,
69
+ Re,
68
70
  w,
69
- H,
70
- Fe,
71
- W
72
- ]), Be = o(() => {
73
- j(!0);
71
+ G,
72
+ ze,
73
+ q
74
+ ]), We = o(() => {
75
+ N(!0);
74
76
  }, []), $ = o(async () => {
75
- await H();
76
- }, [H]), Ve = o(async () => {
77
+ await G();
78
+ }, [G]), Ge = o(async () => {
77
79
  w("billing.payment_method.added", {
78
80
  route: "/billing/overview",
79
- entityId: U,
81
+ entityId: K,
80
82
  source: "overview"
81
83
  }), await $();
82
84
  }, [
83
- U,
85
+ K,
84
86
  w,
85
87
  $
86
- ]), He = o(async (e) => {
87
- U && (await Ie(e, U), w("billing.payment_method.default_set", {
88
+ ]), Ke = o(async (e) => {
89
+ K && (await Be(e, K), w("billing.payment_method.default_set", {
88
90
  route: "/billing/overview",
89
91
  entityId: e,
90
92
  source: "overview"
91
93
  }), await $());
92
94
  }, [
93
- U,
95
+ K,
94
96
  w,
95
- Ie,
97
+ Be,
96
98
  $
97
- ]), Ue = o(async (e) => {
98
- U && (await Le(e, U), w("billing.payment_method.deleted", {
99
+ ]), qe = o(async (e) => {
100
+ K && (await Ve(e, K), w("billing.payment_method.deleted", {
99
101
  route: "/billing/overview",
100
102
  entityId: e,
101
103
  source: "overview"
102
104
  }), await $());
103
105
  }, [
104
- U,
105
- Le,
106
+ K,
107
+ Ve,
106
108
  w,
107
109
  $
108
- ]), We = o((e) => {
109
- W(e.id), A(!1);
110
- }, [W]), Ge = o(async () => {
111
- await H();
112
- }, [H]), Ke = o((e) => {
113
- G(e);
114
- }, [G]), qe = o((e) => K(e), [K]), Je = o((e) => q(e)?.quantity || 0, [q]), Ye = o((e, t) => {
115
- t <= 0 ? Y(e) : J(e, t);
116
- }, [J, Y]), Xe = o((e) => {
117
- Y(e);
118
- }, [Y]);
119
- return C.canViewBillingAccount ? V && ie(V) && !B ? /* @__PURE__ */ h("div", {
110
+ ]), Je = o((e) => {
111
+ q(e.id), M(!1);
112
+ }, [q]), Ye = o(async () => {
113
+ await G();
114
+ }, [G]), Xe = o((e) => {
115
+ J(e);
116
+ }, [J]), Ze = o((e) => Y(e), [Y]), Qe = o((e) => Ie(e)?.quantity || 0, [Ie]), $e = o((e, t) => {
117
+ t <= 0 ? X(e) : Le(e, t);
118
+ }, [Le, X]), et = o((e) => {
119
+ X(e);
120
+ }, [X]);
121
+ return C.canViewBillingAccount ? W && ae(W) && !U ? /* @__PURE__ */ h("div", {
120
122
  className: "p-6",
121
123
  children: /* @__PURE__ */ h(ee, {
122
124
  title: y("billing.serverError.title", "Server Unavailable"),
123
125
  message: y("billing.overview.serverUnavailableMessage", "Unable to load billing overview. The server might be down or experiencing issues."),
124
- onRetry: Ge,
126
+ onRetry: Ye,
125
127
  showRetry: !0
126
128
  })
127
- }) : B && !I ? /* @__PURE__ */ h("div", {
129
+ }) : U && !z ? /* @__PURE__ */ h("div", {
128
130
  className: "flex items-center justify-center min-h-[400px]",
129
131
  children: /* @__PURE__ */ g("div", {
130
132
  className: "flex flex-col items-center gap-3",
131
- children: [/* @__PURE__ */ h(be, { className: "size-8 animate-spin text-text-link" }), /* @__PURE__ */ h("p", {
133
+ children: [/* @__PURE__ */ h(ye, { className: "size-8 animate-spin text-text-link" }), /* @__PURE__ */ h("p", {
132
134
  className: "text-sm text-text-secondary",
133
135
  children: y("billing.overview.loadingInfo", "Loading billing information...")
134
136
  })]
135
137
  })
136
- }) : !B && F.length === 0 && !I ? /* @__PURE__ */ g("div", {
138
+ }) : !U && R.length === 0 && !z ? /* @__PURE__ */ g("div", {
137
139
  className: "px-4 sm:px-6 lg:px-8 py-4 sm:py-6",
138
140
  children: [
139
141
  /* @__PURE__ */ g("header", {
@@ -150,25 +152,25 @@ var y = () => {
150
152
  })
151
153
  ]
152
154
  }),
153
- /* @__PURE__ */ h(Ee, {
155
+ /* @__PURE__ */ h(De, {
154
156
  illustration: "onboarding",
155
157
  title: y("billing.overview.emptyTitle", "No billing account"),
156
158
  description: y("billing.overview.emptyDescription", "Create a billing account to start managing your subscriptions and payments"),
157
159
  action: C.canManageBillingAccount ? /* @__PURE__ */ g("button", {
158
160
  type: "button",
159
- onClick: () => E(!0),
161
+ onClick: () => D(!0),
160
162
  className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors duration-200",
161
- children: [/* @__PURE__ */ h(d, { className: "size-4" }), y("billing.overview.createBillingAccount", "Create Billing Account")]
163
+ children: [/* @__PURE__ */ h(be, { className: "size-4" }), y("billing.overview.createBillingAccount", "Create Billing Account")]
162
164
  }) : void 0
163
165
  }),
164
166
  /* @__PURE__ */ h(i, {
165
- isOpen: Ae,
167
+ isOpen: E,
166
168
  onClose: () => {
167
- E(!1), O(null);
169
+ D(!1), k(null);
168
170
  },
169
- onSubmit: ze,
170
- isSubmitting: Re,
171
- error: D
171
+ onSubmit: Ue,
172
+ isSubmitting: He,
173
+ error: O
172
174
  })
173
175
  ]
174
176
  }) : /* @__PURE__ */ g("div", {
@@ -187,33 +189,33 @@ var y = () => {
187
189
  className: "text-sm text-text-secondary mt-1",
188
190
  children: y("billing.overview.description", "Manage your billing accounts, subscriptions, and payment methods")
189
191
  }),
190
- /* @__PURE__ */ h(De, {
192
+ /* @__PURE__ */ h(Oe, {
191
193
  className: "mt-3",
192
194
  children: y("billing.overview.purpose", "Your billing home base — see your current plan, credit balance and payment methods at a glance, and jump to transactions, usage, credits or new plans. Start here to understand what you are paying for and manage it.")
193
195
  })
194
196
  ] }),
195
- /* @__PURE__ */ h(we, {
196
- primary: C.canManageBillingAccount && F.length > 0 ? {
197
+ /* @__PURE__ */ h(Te, {
198
+ primary: C.canManageBillingAccount && R.length > 0 ? {
197
199
  label: y("billing.overview.newAccount", "New Account"),
198
- onSelect: () => E(!0),
199
- icon: /* @__PURE__ */ h(d, { className: "size-4" })
200
+ onSelect: () => D(!0),
201
+ icon: /* @__PURE__ */ h(be, { className: "size-4" })
200
202
  } : void 0,
201
203
  actions: [{
202
204
  label: y("billing.overview.refresh", "Refresh"),
203
205
  onSelect: () => {
204
- Ge();
206
+ Ye();
205
207
  },
206
- icon: /* @__PURE__ */ h(f, { className: `size-4 ${B ? "animate-spin" : ""}` })
208
+ icon: /* @__PURE__ */ h(p, { className: `size-4 ${U ? "animate-spin" : ""}` })
207
209
  }]
208
210
  })
209
211
  ]
210
212
  }),
211
- F.length > 1 && /* @__PURE__ */ g("div", {
213
+ R.length > 1 && /* @__PURE__ */ g("div", {
212
214
  className: "relative",
213
215
  "data-tour": "billing-overview-account-selector",
214
216
  children: [/* @__PURE__ */ g("button", {
215
217
  type: "button",
216
- onClick: () => A(!k),
218
+ onClick: () => M(!j),
217
219
  className: "w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200",
218
220
  children: [/* @__PURE__ */ g("div", {
219
221
  className: "flex items-center gap-3",
@@ -224,21 +226,21 @@ var y = () => {
224
226
  className: "text-left",
225
227
  children: [/* @__PURE__ */ h("p", {
226
228
  className: "text-sm font-medium text-text-primary",
227
- children: I?.name || y("billing.overview.selectAccount", "Select Account")
229
+ children: z?.name || y("billing.overview.selectAccount", "Select Account")
228
230
  }), /* @__PURE__ */ h("p", {
229
231
  className: "text-xs text-text-secondary",
230
- children: I?.email
232
+ children: z?.email
231
233
  })]
232
234
  })]
233
- }), /* @__PURE__ */ h(_e, { className: `size-4 text-text-muted transition-transform ${k ? "rotate-180" : ""}` })]
234
- }), k && /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h("div", {
235
+ }), /* @__PURE__ */ h(d, { className: `size-4 text-text-muted transition-transform ${j ? "rotate-180" : ""}` })]
236
+ }), j && /* @__PURE__ */ g(Ce, { children: [/* @__PURE__ */ h("div", {
235
237
  className: "fixed inset-0 z-10",
236
- onClick: () => A(!1)
238
+ onClick: () => M(!1)
237
239
  }), /* @__PURE__ */ h("div", {
238
240
  className: "absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-bg-elevated border border-border-seam rounded-card shadow-elevation-3 z-20 py-1 max-h-60 overflow-y-auto",
239
- children: F.map((e) => /* @__PURE__ */ g("button", {
241
+ children: R.map((e) => /* @__PURE__ */ g("button", {
240
242
  type: "button",
241
- onClick: () => We(e),
243
+ onClick: () => Je(e),
242
244
  className: "w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-bg-sunken transition-colors",
243
245
  children: [/* @__PURE__ */ g("div", {
244
246
  className: "flex items-center gap-3 min-w-0",
@@ -255,36 +257,36 @@ var y = () => {
255
257
  children: e.email
256
258
  })]
257
259
  })]
258
- }), I?.id === e.id && /* @__PURE__ */ h(u, { className: "size-4 text-text-link flex-shrink-0" })]
260
+ }), z?.id === e.id && /* @__PURE__ */ h(u, { className: "size-4 text-text-link flex-shrink-0" })]
259
261
  }, e.id))
260
262
  })] })]
261
263
  }),
262
- I && /* @__PURE__ */ g("div", {
264
+ z && /* @__PURE__ */ g("div", {
263
265
  className: "grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4",
264
266
  "data-tour": "billing-overview-stats-row",
265
267
  children: [
266
268
  /* @__PURE__ */ h(r, {
267
269
  className: "p-3 sm:p-5",
268
270
  label: y("billing.overview.stats.creditBalance", "Credit Balance"),
269
- value: `${(I.creditAmount || 0).toLocaleString()} Credits`,
270
- icon: /* @__PURE__ */ h(p, { className: "size-5" })
271
+ value: `${(z.creditAmount || 0).toLocaleString()} Credits`,
272
+ icon: /* @__PURE__ */ h(m, { className: "size-5" })
271
273
  }),
272
274
  /* @__PURE__ */ h(r, {
273
275
  className: "p-3 sm:p-5",
274
276
  label: y("billing.overview.stats.activePlans", "Active Plans"),
275
- value: R.length > 0 ? R.length === 1 ? R[0]?.plan?.name || "1 Plan" : `${R.length} ${y("billing.overview.stats.plansSuffix", "Plans")}` : y("billing.overview.stats.none", "None"),
276
- icon: /* @__PURE__ */ h(f, { className: "size-5" })
277
+ value: V.length > 0 ? V.length === 1 ? V[0]?.plan?.name || "1 Plan" : `${V.length} ${y("billing.overview.stats.plansSuffix", "Plans")}` : y("billing.overview.stats.none", "None"),
278
+ icon: /* @__PURE__ */ h(p, { className: "size-5" })
277
279
  }),
278
280
  /* @__PURE__ */ h(r, {
279
281
  className: "p-3 sm:p-5",
280
282
  label: y("billing.overview.stats.paymentMethods", "Payment Methods"),
281
- value: L.length,
283
+ value: B.length,
282
284
  icon: /* @__PURE__ */ h(l, { className: "size-5" })
283
285
  }),
284
286
  /* @__PURE__ */ h(r, {
285
287
  className: "p-3 sm:p-5",
286
288
  label: y("billing.overview.stats.paymentCurrency", "Payment Currency"),
287
- value: I.currency || "USD",
289
+ value: z.currency || "USD",
288
290
  icon: /* @__PURE__ */ h(l, { className: "size-5" })
289
291
  })
290
292
  ]
@@ -294,27 +296,27 @@ var y = () => {
294
296
  children: [/* @__PURE__ */ g("div", {
295
297
  className: "lg:col-span-2 space-y-4 sm:space-y-6",
296
298
  children: [
297
- I && /* @__PURE__ */ h(ce, {
299
+ z && /* @__PURE__ */ h(le, {
298
300
  dataTour: "billing-overview-billing-account-card",
299
- account: I,
300
- isDefault: I.isDefault ?? !1,
301
- onEdit: () => I && Q(`/settings?billingAccountId=${I.id}`),
302
- onViewTransactions: () => Q(`/transactions/${I.id}`),
303
- onViewCreditTransactions: () => Q(`/creditTransactions/${I.id}`)
301
+ account: z,
302
+ isDefault: z.isDefault ?? !1,
303
+ onEdit: () => z && Q(`/settings?billingAccountId=${z.id}`),
304
+ onViewTransactions: () => Q(`/transactions/${z.id}`),
305
+ onViewCreditTransactions: () => Q(`/creditTransactions/${z.id}`)
304
306
  }),
305
307
  /* @__PURE__ */ g("div", {
306
308
  className: "space-y-3",
307
- children: [R.length > 1 && /* @__PURE__ */ g("div", {
309
+ children: [V.length > 1 && /* @__PURE__ */ g("div", {
308
310
  className: "relative",
309
311
  children: [/* @__PURE__ */ g("button", {
310
312
  type: "button",
311
- onClick: () => P(!N),
313
+ onClick: () => L(!I),
312
314
  className: "w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200",
313
315
  children: [/* @__PURE__ */ g("div", {
314
316
  className: "flex items-center gap-3",
315
317
  children: [/* @__PURE__ */ h("div", {
316
318
  className: "size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center",
317
- children: /* @__PURE__ */ h(ye, { className: "size-4 text-text-link" })
319
+ children: /* @__PURE__ */ h(f, { className: "size-4 text-text-link" })
318
320
  }), /* @__PURE__ */ g("div", {
319
321
  className: "text-left",
320
322
  children: [/* @__PURE__ */ g("p", {
@@ -330,29 +332,29 @@ var y = () => {
330
332
  }), /* @__PURE__ */ g("p", {
331
333
  className: "text-xs text-text-secondary",
332
334
  children: [
333
- R.length,
335
+ V.length,
334
336
  " ",
335
337
  y("billing.overview.activeSubscriptions", "active subscriptions")
336
338
  ]
337
339
  })]
338
340
  })]
339
- }), /* @__PURE__ */ h(_e, { className: `size-4 text-text-muted transition-transform ${N ? "rotate-180" : ""}` })]
340
- }), N && /* @__PURE__ */ g(m, { children: [/* @__PURE__ */ h("div", {
341
+ }), /* @__PURE__ */ h(d, { className: `size-4 text-text-muted transition-transform ${I ? "rotate-180" : ""}` })]
342
+ }), I && /* @__PURE__ */ g(Ce, { children: [/* @__PURE__ */ h("div", {
341
343
  className: "fixed inset-0 z-10",
342
- onClick: () => P(!1)
344
+ onClick: () => L(!1)
343
345
  }), /* @__PURE__ */ h("div", {
344
346
  className: "absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-bg-elevated border border-border-seam rounded-card shadow-elevation-3 z-20 py-1 max-h-60 overflow-y-auto",
345
- children: R.map((e, t) => /* @__PURE__ */ g("button", {
347
+ children: V.map((e, t) => /* @__PURE__ */ g("button", {
346
348
  type: "button",
347
349
  onClick: () => {
348
- M(t), P(!1);
350
+ F(t), L(!1);
349
351
  },
350
352
  className: "w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-bg-sunken transition-colors",
351
353
  children: [/* @__PURE__ */ g("div", {
352
354
  className: "flex items-center gap-3 min-w-0",
353
355
  children: [/* @__PURE__ */ h("div", {
354
356
  className: "size-9 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0",
355
- children: /* @__PURE__ */ h(ye, { className: "size-4 text-text-secondary" })
357
+ children: /* @__PURE__ */ h(f, { className: "size-4 text-text-secondary" })
356
358
  }), /* @__PURE__ */ g("div", {
357
359
  className: "text-left min-w-0",
358
360
  children: [/* @__PURE__ */ h("p", {
@@ -370,26 +372,26 @@ var y = () => {
370
372
  ]
371
373
  })]
372
374
  })]
373
- }), Me === t && /* @__PURE__ */ h(u, { className: "size-4 text-text-link flex-shrink-0" })]
375
+ }), P === t && /* @__PURE__ */ h(u, { className: "size-4 text-text-link flex-shrink-0" })]
374
376
  }, e.id))
375
377
  })] })]
376
- }), /* @__PURE__ */ h(ue, {
378
+ }), /* @__PURE__ */ h(de, {
377
379
  dataTour: "billing-overview-active-subscription-card",
378
380
  subscription: Z,
379
- onViewDetails: () => Z && Q(a(`/subscriptions/${Z.id}`, Z.billingAccountId ?? I?.id)),
380
- onUpgrade: () => Z && Q(a(`/subscriptions/${Z.id}/upgrade`, Z.billingAccountId ?? I?.id)),
381
- onManage: () => I && Q(`/subscriptions?billingAccountId=${I.id}`),
381
+ onViewDetails: () => Z && Q(a(`/subscriptions/${Z.id}`, Z.billingAccountId ?? z?.id)),
382
+ onUpgrade: () => Z && Q(a(`/subscriptions/${Z.id}/upgrade`, Z.billingAccountId ?? z?.id)),
383
+ onManage: () => z && Q(`/subscriptions?billingAccountId=${z.id}`),
382
384
  onPurchasePlan: () => Q("/plans")
383
385
  })]
384
386
  }),
385
- I && /* @__PURE__ */ h(le, {
387
+ z && /* @__PURE__ */ h(ue, {
386
388
  dataTour: "billing-overview-payment-methods-section",
387
- paymentMethods: L,
388
- billingAccountId: I.id,
389
- onAddPaymentMethod: Be,
390
- onSetDefault: He,
391
- onDelete: Ue,
392
- isLoading: B
389
+ paymentMethods: B,
390
+ billingAccountId: z.id,
391
+ onAddPaymentMethod: We,
392
+ onSetDefault: Ke,
393
+ onDelete: qe,
394
+ isLoading: U
393
395
  })
394
396
  ]
395
397
  }), /* @__PURE__ */ g("div", {
@@ -405,8 +407,8 @@ var y = () => {
405
407
  children: [
406
408
  /* @__PURE__ */ g("button", {
407
409
  type: "button",
408
- onClick: () => I && Q(`/transactions/${I.id}`),
409
- disabled: !I,
410
+ onClick: () => z && Q(`/transactions/${z.id}`),
411
+ disabled: !z,
410
412
  className: "w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
411
413
  children: [/* @__PURE__ */ h("div", {
412
414
  className: "size-9 rounded-lg bg-status-info-bg-subtle flex items-center justify-center",
@@ -415,12 +417,12 @@ var y = () => {
415
417
  }),
416
418
  /* @__PURE__ */ g("button", {
417
419
  type: "button",
418
- onClick: () => I && Q(`/creditTransactions/${I.id}`),
419
- disabled: !I,
420
+ onClick: () => z && Q(`/creditTransactions/${z.id}`),
421
+ disabled: !z,
420
422
  className: "w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
421
423
  children: [/* @__PURE__ */ h("div", {
422
424
  className: "size-9 rounded-lg bg-status-warning-bg-subtle flex items-center justify-center",
423
- children: /* @__PURE__ */ h(p, { className: "size-4 text-status-warning-text" })
425
+ children: /* @__PURE__ */ h(m, { className: "size-4 text-status-warning-text" })
424
426
  }), y("billing.overview.creditHistory", "Credit History")]
425
427
  }),
426
428
  /* @__PURE__ */ g("button", {
@@ -434,17 +436,17 @@ var y = () => {
434
436
  }),
435
437
  /* @__PURE__ */ g("button", {
436
438
  type: "button",
437
- onClick: () => I && Q(`/usage?billingAccountId=${I.id}`),
438
- disabled: !I,
439
+ onClick: () => z && Q(`/usage?billingAccountId=${z.id}`),
440
+ disabled: !z,
439
441
  className: "w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
440
442
  children: [/* @__PURE__ */ h("div", {
441
443
  className: "size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center",
442
- children: /* @__PURE__ */ h(he, { className: "size-4 text-text-link" })
444
+ children: /* @__PURE__ */ h(ge, { className: "size-4 text-text-link" })
443
445
  }), y("billing.overview.usageAnalytics", "Usage Analytics")]
444
446
  })
445
447
  ]
446
448
  })]
447
- }), !z && /* @__PURE__ */ g(Te, {
449
+ }), !H && /* @__PURE__ */ g(Ee, {
448
450
  treatment: "glass",
449
451
  glow: !0,
450
452
  className: "p-4 sm:p-5",
@@ -460,47 +462,48 @@ var y = () => {
460
462
  className: "text-sm text-text-secondary mt-1",
461
463
  children: y("billing.overview.getStartedDescription", "Choose a plan that fits your needs to unlock all features")
462
464
  })] })]
463
- }), /* @__PURE__ */ g(Ce, {
465
+ }), /* @__PURE__ */ g(we, {
464
466
  type: "button",
465
467
  onClick: () => Q("/plans"),
466
468
  className: "w-full",
467
- children: [y("billing.overview.browsePlans", "Browse Plans"), /* @__PURE__ */ h(ge, { className: "size-4" })]
469
+ children: [y("billing.overview.browsePlans", "Browse Plans"), /* @__PURE__ */ h(_e, { className: "size-4" })]
468
470
  })]
469
471
  })]
470
472
  })]
471
473
  }),
472
- /* @__PURE__ */ h(de, {
474
+ /* @__PURE__ */ h(fe, {
473
475
  dataTour: "billing-overview-recommended-addons",
474
- addons: Ne,
475
- hasSubscription: z,
476
+ addons: Pe,
477
+ hasSubscription: H,
476
478
  onViewAddon: (e) => Q(`/addons/${e}`),
477
479
  onPurchaseAddon: (e) => Q(`/addons/${e}?action=purchase`),
478
480
  onViewAllAddons: () => Q("/addons"),
479
- onAddToCart: Ke,
480
- isInCart: qe,
481
- getCartQuantity: Je,
482
- onUpdateQuantity: Ye,
483
- onRemoveFromCart: Xe,
481
+ onAddToCart: Xe,
482
+ isInCart: Ze,
483
+ getCartQuantity: Qe,
484
+ onUpdateQuantity: $e,
485
+ onRemoveFromCart: et,
484
486
  maxAddons: 3,
485
- isLoading: Pe
487
+ isLoading: Fe
486
488
  }),
487
- /* @__PURE__ */ h(me, {}),
489
+ /* @__PURE__ */ h(he, {}),
490
+ Me && /* @__PURE__ */ h(te, { onDismiss: () => A(!1) }),
488
491
  /* @__PURE__ */ h(i, {
489
- isOpen: Ae,
492
+ isOpen: E,
490
493
  onClose: () => {
491
- E(!1), O(null);
494
+ D(!1), k(null);
492
495
  },
493
- onSubmit: ze,
494
- isSubmitting: Re,
495
- error: D
496
+ onSubmit: Ue,
497
+ isSubmitting: He,
498
+ error: O
496
499
  }),
497
- I && /* @__PURE__ */ h(fe, {
498
- isOpen: je,
499
- onClose: () => j(!1),
500
- billingAccountId: I.id,
501
- currency: I.currency,
502
- country: I.billingAddresses?.[0]?.country,
503
- onSuccess: Ve
500
+ z && /* @__PURE__ */ h(pe, {
501
+ isOpen: Ne,
502
+ onClose: () => N(!1),
503
+ billingAccountId: z.id,
504
+ currency: z.currency,
505
+ country: z.billingAddresses?.[0]?.country,
506
+ onSuccess: Ge
504
507
  })
505
508
  ]
506
509
  }) : /* @__PURE__ */ h(n, { message: "You don't have permission to view billing information." });
@@ -1 +1 @@
1
- {"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * Billing Overview Page\n * Main dashboard for billing showing accounts, subscriptions, payment methods, and addons\n */\n\nimport { type FC, useState, useCallback, useEffect } from 'react';\nimport {\n Plus,\n Building2,\n RefreshCw,\n Loader2,\n Wallet,\n ChevronDown,\n Check,\n Activity,\n Receipt,\n ArrowRight,\n Sparkles,\n Crown,\n Coins,\n} from 'lucide-react';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { StatCard } from '../../../shared/components/StatCard';\nimport { ServerError } from '../../../shared/components/ServerError';\nimport { AccessDenied } from '../../../shared/components/AccessDenied';\nimport { isServerError } from '../../../shared/utils';\nimport { useBillingOverviewData, useDashboardMutations } from '../hooks';\nimport {\n BillingAccountCard,\n PaymentMethodsSection,\n ActiveSubscriptionCard,\n RecommendedAddonsSection,\n CreateBillingAccountModal,\n AddPaymentMethodModal,\n} from '../components';\nimport { useAddonCartStore } from '../../addons/store/addonCartStore';\nimport { FloatingCartDrawer } from '../../addons/components/FloatingCartDrawer';\nimport {\n getStoredBillingAccountId,\n setStoredBillingAccountId,\n} from '../../../hooks/useBillingAccountSelection';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport type {\n BillingAccount,\n CreateBillingAccountInput,\n SetBillingAddressInput,\n Addon,\n} from '../../../shared/types';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport {\n Button,\n CTAOverflowMenu,\n GlassCard,\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nexport const OverviewPage: FC = () => {\n const tr = useTr();\n const { navigate, basePath, orgId, productId } = useBilling();\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n\n const [selectedAccountId, setSelectedAccountIdState] = useState<string | null>(() =>\n getStoredBillingAccountId(orgId)\n );\n\n const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [isAccountSelectorOpen, setIsAccountSelectorOpen] = useState(false);\n const [isAddPaymentModalOpen, setIsAddPaymentModalOpen] = useState(false);\n const [selectedSubscriptionIndex, setSelectedSubscriptionIndex] = useState(0);\n const [isSubscriptionSelectorOpen, setIsSubscriptionSelectorOpen] = useState(false);\n\n const {\n billingAccounts,\n currentAccount,\n paymentMethods,\n activeSubscriptions,\n hasActiveSubscription,\n recommendedAddons,\n isLoading,\n error,\n refetchAll,\n } = useBillingOverviewData(selectedAccountId, productId);\n\n const currentAccountId = currentAccount?.id;\n const isLoadingAddons = isLoading;\n\n const setSelectedAccountId = useCallback(\n (accountId: string | null) => {\n setSelectedAccountIdState(accountId);\n setStoredBillingAccountId(orgId, accountId);\n },\n [orgId]\n );\n\n // Addon cart store for add to cart functionality\n const { addToCart, isInCart, getCartItem, updateQuantity, removeFromCart } = useAddonCartStore();\n\n const {\n createBillingAccount,\n setBillingAddress,\n setDefaultPaymentMethod,\n deletePaymentMethod,\n isCreatingAccount,\n } = useDashboardMutations();\n\n // Reset subscription selector when account changes or subscriptions change\n useEffect(() => {\n setSelectedSubscriptionIndex(0);\n setIsSubscriptionSelectorOpen(false);\n }, [currentAccountId, activeSubscriptions.length]);\n\n useEffect(() => {\n if (selectedAccountId && billingAccounts.length > 0) {\n const accountExists = billingAccounts.some((account) => account.id === selectedAccountId);\n if (!accountExists) {\n setSelectedAccountId(null);\n }\n }\n }, [billingAccounts, selectedAccountId, setSelectedAccountId]);\n\n useEffect(() => {\n if (!selectedAccountId && billingAccounts.length > 0) {\n const defaultAccount =\n billingAccounts.find((account) => account.isDefault) ?? billingAccounts[0];\n setSelectedAccountId(defaultAccount.id);\n }\n }, [billingAccounts, selectedAccountId, setSelectedAccountId]);\n\n // Derive selected subscription from active subscriptions\n const selectedSubscription =\n activeSubscriptions[selectedSubscriptionIndex] || activeSubscriptions[0] || null;\n\n // Helper for navigation - properly joins basePath and path\n const navigateTo = useCallback(\n (path: string) => {\n // Handle path joining to avoid double slashes (e.g., / + /plans = //plans)\n let fullPath = path;\n if (basePath && basePath !== '/') {\n // Remove trailing slash from basePath and leading slash from path if needed\n const base = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;\n const suffix = path.startsWith('/') ? path : `/${path}`;\n fullPath = `${base}${suffix}`;\n }\n navigate(fullPath);\n },\n [navigate, basePath]\n );\n\n // Handlers\n const handleCreateAccount = useCallback(\n async (\n input: CreateBillingAccountInput,\n address?: Omit<SetBillingAddressInput, 'billingAccountId'>\n ) => {\n setCreateError(null);\n try {\n const newAccount = await createBillingAccount(input);\n\n // If address data provided, set the billing address\n if (address && newAccount.id) {\n await setBillingAddress({\n billingAccountId: newAccount.id,\n ...address,\n });\n }\n\n setIsCreateModalOpen(false);\n setSelectedAccountId(newAccount.id);\n emit('billing.billing_account.created', {\n route: '/billing/overview',\n entityId: newAccount.id,\n source: 'overview',\n });\n await refetchAll();\n } catch (err) {\n setCreateError(err instanceof Error ? err.message : 'Failed to create billing account');\n }\n },\n [createBillingAccount, emit, refetchAll, setBillingAddress, setSelectedAccountId]\n );\n\n const handleAddPaymentMethod = useCallback(() => {\n setIsAddPaymentModalOpen(true);\n }, []);\n\n // Helper to refetch the current account data\n const refetchCurrentAccount = useCallback(async () => {\n await refetchAll();\n }, [refetchAll]);\n\n const handlePaymentMethodAdded = useCallback(async () => {\n emit('billing.payment_method.added', {\n route: '/billing/overview',\n entityId: currentAccountId,\n source: 'overview',\n });\n await refetchCurrentAccount();\n }, [currentAccountId, emit, refetchCurrentAccount]);\n\n const handleSetDefaultPaymentMethod = useCallback(\n async (id: string) => {\n if (!currentAccountId) return;\n await setDefaultPaymentMethod(id, currentAccountId);\n emit('billing.payment_method.default_set', {\n route: '/billing/overview',\n entityId: id,\n source: 'overview',\n });\n await refetchCurrentAccount();\n },\n [currentAccountId, emit, setDefaultPaymentMethod, refetchCurrentAccount]\n );\n\n const handleDeletePaymentMethod = useCallback(\n async (id: string) => {\n if (!currentAccountId) return;\n await deletePaymentMethod(id, currentAccountId);\n emit('billing.payment_method.deleted', {\n route: '/billing/overview',\n entityId: id,\n source: 'overview',\n });\n await refetchCurrentAccount();\n },\n [currentAccountId, deletePaymentMethod, emit, refetchCurrentAccount]\n );\n\n const handleSelectAccount = useCallback(\n (account: BillingAccount) => {\n setSelectedAccountId(account.id);\n setIsAccountSelectorOpen(false);\n },\n [setSelectedAccountId]\n );\n\n const handleRefresh = useCallback(async () => {\n await refetchAll();\n }, [refetchAll]);\n\n // Handler for adding addon to cart\n const handleAddToCart = useCallback(\n (addon: Addon) => {\n addToCart(addon);\n },\n [addToCart]\n );\n\n // Handler to check if addon is in cart\n const handleIsInCart = useCallback(\n (addonId: string) => {\n return isInCart(addonId);\n },\n [isInCart]\n );\n\n // Handler to get cart quantity for addon\n const handleGetCartQuantity = useCallback(\n (addonId: string) => {\n const item = getCartItem(addonId);\n return item?.quantity || 0;\n },\n [getCartItem]\n );\n\n // Handler to update quantity in cart\n const handleUpdateQuantity = useCallback(\n (addonId: string, quantity: number) => {\n if (quantity <= 0) {\n removeFromCart(addonId);\n } else {\n updateQuantity(addonId, quantity);\n }\n },\n [updateQuantity, removeFromCart]\n );\n\n // Handler to remove from cart\n const handleRemoveFromCart = useCallback(\n (addonId: string) => {\n removeFromCart(addonId);\n },\n [removeFromCart]\n );\n\n // Permission check - must be after all hooks are defined\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view billing information.\" />;\n }\n\n // Error state\n if (error && isServerError(error) && !isLoading) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.serverError.title', 'Server Unavailable')}\n message={tr(\n 'billing.overview.serverUnavailableMessage',\n 'Unable to load billing overview. The server might be down or experiencing issues.'\n )}\n onRetry={handleRefresh}\n showRetry\n />\n </div>\n );\n }\n\n // Loading state\n if (isLoading && !currentAccount) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"flex flex-col items-center gap-3\">\n <Loader2 className=\"size-8 animate-spin text-text-link\" />\n <p className=\"text-sm text-text-secondary\">\n {tr('billing.overview.loadingInfo', 'Loading billing information...')}\n </p>\n </div>\n </div>\n );\n }\n\n // No billing accounts state. Checked against `billingAccounts` AND\n // `currentAccount` (not just the list) because getDefaultBillingAccount\n // lazy-creates a default account for a brand-new org — on that exact\n // first request, getBillingAccountsByOrg's plain read can still observe\n // zero rows in the same response that returned a real, freshly-created\n // currentAccount. A valid currentAccount always means there's a real\n // billing account regardless of what the (possibly momentarily stale)\n // list says.\n if (!isLoading && billingAccounts.length === 0 && !currentAccount) {\n return (\n <div className=\"px-4 sm:px-6 lg:px-8 py-4 sm:py-6\">\n <header className=\"relative overflow-hidden mb-6 sm:mb-8 rounded-card\">\n <AuroraBackground intensity=\"subtle\" />\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n <GradientText>{tr('billing.overview.title', 'Billing Overview')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.emptySetupDescription',\n 'Set up billing to manage subscriptions, payments, and invoices'\n )}\n </p>\n </header>\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title={tr('billing.overview.emptyTitle', 'No billing account')}\n description={tr(\n 'billing.overview.emptyDescription',\n 'Create a billing account to start managing your subscriptions and payments'\n )}\n action={\n permissions.canManageBillingAccount ? (\n <button\n type=\"button\"\n onClick={() => setIsCreateModalOpen(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\n >\n <Plus className=\"size-4\" />\n {tr('billing.overview.createBillingAccount', 'Create Billing Account')}\n </button>\n ) : undefined\n }\n />\n\n <CreateBillingAccountModal\n isOpen={isCreateModalOpen}\n onClose={() => {\n setIsCreateModalOpen(false);\n setCreateError(null);\n }}\n onSubmit={handleCreateAccount}\n isSubmitting={isCreatingAccount}\n error={createError}\n />\n </div>\n );\n }\n\n return (\n <div className=\"px-4 sm:px-6 lg:px-8 py-4 sm:py-6 space-y-4 sm:space-y-8\">\n {/* Header */}\n <header className=\"relative overflow-hidden rounded-card flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <AuroraBackground intensity=\"subtle\" />\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n <GradientText>{tr('billing.overview.title', 'Billing Overview')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.description',\n 'Manage your billing accounts, subscriptions, and payment methods'\n )}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.overview.purpose',\n 'Your billing home base — see your current plan, credit balance and payment methods at a glance, and jump to transactions, usage, credits or new plans. Start here to understand what you are paying for and manage it.'\n )}\n </PagePurpose>\n </div>\n <CTAOverflowMenu\n primary={\n permissions.canManageBillingAccount && billingAccounts.length > 0\n ? {\n label: tr('billing.overview.newAccount', 'New Account'),\n onSelect: () => setIsCreateModalOpen(true),\n icon: <Plus className=\"size-4\" />,\n }\n : undefined\n }\n actions={[\n {\n label: tr('billing.overview.refresh', 'Refresh'),\n onSelect: () => {\n void handleRefresh();\n },\n icon: <RefreshCw className={`size-4 ${isLoading ? 'animate-spin' : ''}`} />,\n },\n ]}\n />\n </header>\n\n {/* Account Selector (if multiple accounts) */}\n {billingAccounts.length > 1 && (\n <div className=\"relative\" data-tour=\"billing-overview-account-selector\">\n <button\n type=\"button\"\n onClick={() => setIsAccountSelectorOpen(!isAccountSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Building2 className=\"size-4 text-text-link\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {currentAccount?.name || tr('billing.overview.selectAccount', 'Select Account')}\n </p>\n <p className=\"text-xs text-text-secondary\">{currentAccount?.email}</p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-text-muted transition-transform ${isAccountSelectorOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {isAccountSelectorOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsAccountSelectorOpen(false)} />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-bg-elevated border border-border-seam rounded-card shadow-elevation-3 z-20 py-1 max-h-60 overflow-y-auto\">\n {billingAccounts.map((account) => (\n <button\n type=\"button\"\n key={account.id}\n onClick={() => handleSelectAccount(account)}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-9 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <Building2 className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {account.name}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">{account.email}</p>\n </div>\n </div>\n {currentAccount?.id === account.id && (\n <Check className=\"size-4 text-text-link flex-shrink-0\" />\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Stats Row */}\n {currentAccount && (\n <div\n className=\"grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4\"\n data-tour=\"billing-overview-stats-row\"\n >\n <StatCard\n className=\"p-3 sm:p-5\"\n label={tr('billing.overview.stats.creditBalance', 'Credit Balance')}\n value={`${(currentAccount.creditAmount || 0).toLocaleString()} Credits`}\n icon={<Wallet className=\"size-5\" />}\n />\n <StatCard\n className=\"p-3 sm:p-5\"\n label={tr('billing.overview.stats.activePlans', 'Active Plans')}\n value={\n activeSubscriptions.length > 0\n ? activeSubscriptions.length === 1\n ? activeSubscriptions[0]?.plan?.name || '1 Plan'\n : `${activeSubscriptions.length} ${tr('billing.overview.stats.plansSuffix', 'Plans')}`\n : tr('billing.overview.stats.none', 'None')\n }\n icon={<RefreshCw className=\"size-5\" />}\n />\n <StatCard\n className=\"p-3 sm:p-5\"\n label={tr('billing.overview.stats.paymentMethods', 'Payment Methods')}\n value={paymentMethods.length}\n icon={<Building2 className=\"size-5\" />}\n />\n <StatCard\n className=\"p-3 sm:p-5\"\n label={tr('billing.overview.stats.paymentCurrency', 'Payment Currency')}\n value={currentAccount.currency || 'USD'}\n icon={<Building2 className=\"size-5\" />}\n />\n </div>\n )}\n\n {/* Main Content Grid */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6\">\n {/* Left Column - Billing Account & Subscription */}\n <div className=\"lg:col-span-2 space-y-4 sm:space-y-6\">\n {/* Billing Account Card */}\n {currentAccount && (\n <BillingAccountCard\n dataTour=\"billing-overview-billing-account-card\"\n account={currentAccount}\n isDefault={currentAccount.isDefault ?? false}\n onEdit={() =>\n currentAccount && navigateTo(`/settings?billingAccountId=${currentAccount.id}`)\n }\n onViewTransactions={() => navigateTo(`/transactions/${currentAccount.id}`)}\n onViewCreditTransactions={() =>\n navigateTo(`/creditTransactions/${currentAccount.id}`)\n }\n />\n )}\n\n {/* Active Subscriptions */}\n <div className=\"space-y-3\">\n {/* Subscription Selector (if multiple subscriptions) */}\n {activeSubscriptions.length > 1 && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsSubscriptionSelectorOpen(!isSubscriptionSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Crown className=\"size-4 text-text-link\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {selectedSubscription?.plan?.name ||\n tr('billing.overview.selectSubscription', 'Select Subscription')}\n {selectedSubscription?.plan?.product?.name && (\n <span className=\"text-text-secondary font-normal\">\n {' '}\n · {selectedSubscription.plan.product.name}\n </span>\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {activeSubscriptions.length}{' '}\n {tr('billing.overview.activeSubscriptions', 'active subscriptions')}\n </p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-text-muted transition-transform ${isSubscriptionSelectorOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {isSubscriptionSelectorOpen && (\n <>\n <div\n className=\"fixed inset-0 z-10\"\n onClick={() => setIsSubscriptionSelectorOpen(false)}\n />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-bg-elevated border border-border-seam rounded-card shadow-elevation-3 z-20 py-1 max-h-60 overflow-y-auto\">\n {activeSubscriptions.map((subscription, index) => (\n <button\n type=\"button\"\n key={subscription.id}\n onClick={() => {\n setSelectedSubscriptionIndex(index);\n setIsSubscriptionSelectorOpen(false);\n }}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-9 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <Crown className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {subscription.plan?.name ||\n tr('billing.overview.subscription', 'Subscription')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {subscription.plan?.product?.name ||\n tr('billing.overview.unknownProduct', 'Unknown Product')}{' '}\n · {tr('billing.overview.ends', 'Ends')}{' '}\n {new Date(subscription.endDate).toLocaleDateString()}\n </p>\n </div>\n </div>\n {selectedSubscriptionIndex === index && (\n <Check className=\"size-4 text-text-link flex-shrink-0\" />\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Single Subscription Card */}\n <ActiveSubscriptionCard\n dataTour=\"billing-overview-active-subscription-card\"\n subscription={selectedSubscription}\n onViewDetails={() =>\n selectedSubscription &&\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${selectedSubscription.id}`,\n selectedSubscription.billingAccountId ?? currentAccount?.id\n )\n )\n }\n onUpgrade={() =>\n selectedSubscription &&\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${selectedSubscription.id}/upgrade`,\n selectedSubscription.billingAccountId ?? currentAccount?.id\n )\n )\n }\n onManage={() =>\n currentAccount && navigateTo(`/subscriptions?billingAccountId=${currentAccount.id}`)\n }\n onPurchasePlan={() => navigateTo('/plans')}\n />\n </div>\n\n {/* Payment Methods */}\n {currentAccount && (\n <PaymentMethodsSection\n dataTour=\"billing-overview-payment-methods-section\"\n paymentMethods={paymentMethods}\n billingAccountId={currentAccount.id}\n onAddPaymentMethod={handleAddPaymentMethod}\n onSetDefault={handleSetDefaultPaymentMethod}\n onDelete={handleDeletePaymentMethod}\n isLoading={isLoading}\n />\n )}\n </div>\n\n {/* Right Column - Quick Actions & Info */}\n <div className=\"space-y-4 sm:space-y-6\">\n {/* Quick Actions */}\n <section\n className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 p-4 sm:p-5\"\n data-tour=\"billing-overview-quick-actions\"\n >\n <h3 className=\"font-semibold text-text-primary mb-4\">\n {tr('billing.overview.quickActions', 'Quick Actions')}\n </h3>\n <div className=\"space-y-1.5\">\n <button\n type=\"button\"\n onClick={() => currentAccount && navigateTo(`/transactions/${currentAccount.id}`)}\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-status-info-bg-subtle flex items-center justify-center\">\n <Receipt className=\"size-4 text-status-info-text\" />\n </div>\n {tr('billing.overview.viewTransactions', 'View Transactions')}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n currentAccount && navigateTo(`/creditTransactions/${currentAccount.id}`)\n }\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-status-warning-bg-subtle flex items-center justify-center\">\n <Wallet className=\"size-4 text-status-warning-text\" />\n </div>\n {tr('billing.overview.creditHistory', 'Credit History')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/checkout/credits')}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <div className=\"size-9 rounded-lg bg-status-success-bg-subtle flex items-center justify-center\">\n <Coins className=\"size-4 text-status-success-text\" />\n </div>\n {tr('billing.overview.purchaseCredits', 'Purchase Credits')}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n currentAccount && navigateTo(`/usage?billingAccountId=${currentAccount.id}`)\n }\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Activity className=\"size-4 text-text-link\" />\n </div>\n {tr('billing.overview.usageAnalytics', 'Usage Analytics')}\n </button>\n </div>\n </section>\n\n {/* Get Started (if no subscription) — primary emphasis zone */}\n {!hasActiveSubscription && (\n <GlassCard treatment=\"glass\" glow className=\"p-4 sm:p-5\">\n <div className=\"flex items-start gap-3 mb-4\">\n <div className=\"size-10 rounded-lg bg-bg-surface flex items-center justify-center flex-shrink-0 shadow-elevation-1\">\n <Sparkles className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {tr('billing.overview.getStarted', 'Get Started')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.getStartedDescription',\n 'Choose a plan that fits your needs to unlock all features'\n )}\n </p>\n </div>\n </div>\n <Button type=\"button\" onClick={() => navigateTo('/plans')} className=\"w-full\">\n {tr('billing.overview.browsePlans', 'Browse Plans')}\n <ArrowRight className=\"size-4\" />\n </Button>\n </GlassCard>\n )}\n </div>\n </div>\n\n {/* Recommended Addons (only if has subscription) - Limited to 3 with cart support */}\n <RecommendedAddonsSection\n dataTour=\"billing-overview-recommended-addons\"\n addons={recommendedAddons}\n hasSubscription={hasActiveSubscription}\n onViewAddon={(addonId) => navigateTo(`/addons/${addonId}`)}\n onPurchaseAddon={(addonId) => navigateTo(`/addons/${addonId}?action=purchase`)}\n onViewAllAddons={() => navigateTo('/addons')}\n onAddToCart={handleAddToCart}\n isInCart={handleIsInCart}\n getCartQuantity={handleGetCartQuantity}\n onUpdateQuantity={handleUpdateQuantity}\n onRemoveFromCart={handleRemoveFromCart}\n maxAddons={3}\n isLoading={isLoadingAddons}\n />\n\n {/* Floating Cart Drawer - shows when items are in cart */}\n <FloatingCartDrawer />\n\n {/* Create Billing Account Modal */}\n <CreateBillingAccountModal\n isOpen={isCreateModalOpen}\n onClose={() => {\n setIsCreateModalOpen(false);\n setCreateError(null);\n }}\n onSubmit={handleCreateAccount}\n isSubmitting={isCreatingAccount}\n error={createError}\n />\n\n {/* Add Payment Method Modal */}\n {currentAccount && (\n <AddPaymentMethodModal\n isOpen={isAddPaymentModalOpen}\n onClose={() => setIsAddPaymentModalOpen(false)}\n billingAccountId={currentAccount.id}\n currency={currentAccount.currency}\n country={currentAccount.billingAddresses?.[0]?.country}\n onSuccess={handlePaymentMethodAdded}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"OverviewPage.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/pages/OverviewPage.tsx"],"sourcesContent":["/**\n * Billing Overview Page\n * Main dashboard for billing showing accounts, subscriptions, payment methods, and addons\n */\n\nimport { type FC, useState, useCallback, useEffect } from 'react';\nimport {\n Plus,\n Building2,\n RefreshCw,\n Loader2,\n Wallet,\n ChevronDown,\n Check,\n Activity,\n Receipt,\n ArrowRight,\n Sparkles,\n Crown,\n Coins,\n} from 'lucide-react';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { StatCard } from '../../../shared/components/StatCard';\nimport { ServerError } from '../../../shared/components/ServerError';\nimport { AccessDenied } from '../../../shared/components/AccessDenied';\nimport { PushPermissionNudge } from '../../../shared/components/PushPermissionNudge';\nimport { isServerError } from '../../../shared/utils';\nimport { shouldPromptForPushPermission } from '@burdenoff/fe-libs/shared/native';\nimport { useBillingOverviewData, useDashboardMutations } from '../hooks';\nimport {\n BillingAccountCard,\n PaymentMethodsSection,\n ActiveSubscriptionCard,\n RecommendedAddonsSection,\n CreateBillingAccountModal,\n AddPaymentMethodModal,\n} from '../components';\nimport { useAddonCartStore } from '../../addons/store/addonCartStore';\nimport { FloatingCartDrawer } from '../../addons/components/FloatingCartDrawer';\nimport {\n getStoredBillingAccountId,\n setStoredBillingAccountId,\n} from '../../../hooks/useBillingAccountSelection';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport type {\n BillingAccount,\n CreateBillingAccountInput,\n SetBillingAddressInput,\n Addon,\n} from '../../../shared/types';\nimport { useTr } from '../../../shared/hooks/useTr';\nimport {\n Button,\n CTAOverflowMenu,\n GlassCard,\n AuroraBackground,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nexport const OverviewPage: FC = () => {\n const tr = useTr();\n const { navigate, basePath, orgId, productId } = useBilling();\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n\n const [selectedAccountId, setSelectedAccountIdState] = useState<string | null>(() =>\n getStoredBillingAccountId(orgId)\n );\n\n const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [showPushNudge, setShowPushNudge] = useState(false);\n const [isAccountSelectorOpen, setIsAccountSelectorOpen] = useState(false);\n const [isAddPaymentModalOpen, setIsAddPaymentModalOpen] = useState(false);\n const [selectedSubscriptionIndex, setSelectedSubscriptionIndex] = useState(0);\n const [isSubscriptionSelectorOpen, setIsSubscriptionSelectorOpen] = useState(false);\n\n const {\n billingAccounts,\n currentAccount,\n paymentMethods,\n activeSubscriptions,\n hasActiveSubscription,\n recommendedAddons,\n isLoading,\n error,\n refetchAll,\n } = useBillingOverviewData(selectedAccountId, productId);\n\n const currentAccountId = currentAccount?.id;\n const isLoadingAddons = isLoading;\n\n const setSelectedAccountId = useCallback(\n (accountId: string | null) => {\n setSelectedAccountIdState(accountId);\n setStoredBillingAccountId(orgId, accountId);\n },\n [orgId]\n );\n\n // Addon cart store for add to cart functionality\n const { addToCart, isInCart, getCartItem, updateQuantity, removeFromCart } = useAddonCartStore();\n\n const {\n createBillingAccount,\n setBillingAddress,\n setDefaultPaymentMethod,\n deletePaymentMethod,\n isCreatingAccount,\n } = useDashboardMutations();\n\n // Reset subscription selector when account changes or subscriptions change\n useEffect(() => {\n setSelectedSubscriptionIndex(0);\n setIsSubscriptionSelectorOpen(false);\n }, [currentAccountId, activeSubscriptions.length]);\n\n useEffect(() => {\n if (selectedAccountId && billingAccounts.length > 0) {\n const accountExists = billingAccounts.some((account) => account.id === selectedAccountId);\n if (!accountExists) {\n setSelectedAccountId(null);\n }\n }\n }, [billingAccounts, selectedAccountId, setSelectedAccountId]);\n\n useEffect(() => {\n if (!selectedAccountId && billingAccounts.length > 0) {\n const defaultAccount =\n billingAccounts.find((account) => account.isDefault) ?? billingAccounts[0];\n setSelectedAccountId(defaultAccount.id);\n }\n }, [billingAccounts, selectedAccountId, setSelectedAccountId]);\n\n // Derive selected subscription from active subscriptions\n const selectedSubscription =\n activeSubscriptions[selectedSubscriptionIndex] || activeSubscriptions[0] || null;\n\n // Helper for navigation - properly joins basePath and path\n const navigateTo = useCallback(\n (path: string) => {\n // Handle path joining to avoid double slashes (e.g., / + /plans = //plans)\n let fullPath = path;\n if (basePath && basePath !== '/') {\n // Remove trailing slash from basePath and leading slash from path if needed\n const base = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;\n const suffix = path.startsWith('/') ? path : `/${path}`;\n fullPath = `${base}${suffix}`;\n }\n navigate(fullPath);\n },\n [navigate, basePath]\n );\n\n // Handlers\n const handleCreateAccount = useCallback(\n async (\n input: CreateBillingAccountInput,\n address?: Omit<SetBillingAddressInput, 'billingAccountId'>\n ) => {\n setCreateError(null);\n try {\n const newAccount = await createBillingAccount(input);\n\n // If address data provided, set the billing address\n if (address && newAccount.id) {\n await setBillingAddress({\n billingAccountId: newAccount.id,\n ...address,\n });\n }\n\n setIsCreateModalOpen(false);\n setSelectedAccountId(newAccount.id);\n emit('billing.billing_account.created', {\n route: '/billing/overview',\n entityId: newAccount.id,\n source: 'overview',\n });\n await refetchAll();\n\n // Contextual push-permission ask, right after a meaningful action —\n // not hidden behind the notification-centre bell. Only when the OS/\n // browser has never been asked AND this device hasn't dismissed the\n // nudge before; see shouldPromptForPushPermission's own doc for why.\n if (shouldPromptForPushPermission()) {\n setShowPushNudge(true);\n }\n } catch (err) {\n setCreateError(err instanceof Error ? err.message : 'Failed to create billing account');\n }\n },\n [createBillingAccount, emit, refetchAll, setBillingAddress, setSelectedAccountId]\n );\n\n const handleAddPaymentMethod = useCallback(() => {\n setIsAddPaymentModalOpen(true);\n }, []);\n\n // Helper to refetch the current account data\n const refetchCurrentAccount = useCallback(async () => {\n await refetchAll();\n }, [refetchAll]);\n\n const handlePaymentMethodAdded = useCallback(async () => {\n emit('billing.payment_method.added', {\n route: '/billing/overview',\n entityId: currentAccountId,\n source: 'overview',\n });\n await refetchCurrentAccount();\n }, [currentAccountId, emit, refetchCurrentAccount]);\n\n const handleSetDefaultPaymentMethod = useCallback(\n async (id: string) => {\n if (!currentAccountId) return;\n await setDefaultPaymentMethod(id, currentAccountId);\n emit('billing.payment_method.default_set', {\n route: '/billing/overview',\n entityId: id,\n source: 'overview',\n });\n await refetchCurrentAccount();\n },\n [currentAccountId, emit, setDefaultPaymentMethod, refetchCurrentAccount]\n );\n\n const handleDeletePaymentMethod = useCallback(\n async (id: string) => {\n if (!currentAccountId) return;\n await deletePaymentMethod(id, currentAccountId);\n emit('billing.payment_method.deleted', {\n route: '/billing/overview',\n entityId: id,\n source: 'overview',\n });\n await refetchCurrentAccount();\n },\n [currentAccountId, deletePaymentMethod, emit, refetchCurrentAccount]\n );\n\n const handleSelectAccount = useCallback(\n (account: BillingAccount) => {\n setSelectedAccountId(account.id);\n setIsAccountSelectorOpen(false);\n },\n [setSelectedAccountId]\n );\n\n const handleRefresh = useCallback(async () => {\n await refetchAll();\n }, [refetchAll]);\n\n // Handler for adding addon to cart\n const handleAddToCart = useCallback(\n (addon: Addon) => {\n addToCart(addon);\n },\n [addToCart]\n );\n\n // Handler to check if addon is in cart\n const handleIsInCart = useCallback(\n (addonId: string) => {\n return isInCart(addonId);\n },\n [isInCart]\n );\n\n // Handler to get cart quantity for addon\n const handleGetCartQuantity = useCallback(\n (addonId: string) => {\n const item = getCartItem(addonId);\n return item?.quantity || 0;\n },\n [getCartItem]\n );\n\n // Handler to update quantity in cart\n const handleUpdateQuantity = useCallback(\n (addonId: string, quantity: number) => {\n if (quantity <= 0) {\n removeFromCart(addonId);\n } else {\n updateQuantity(addonId, quantity);\n }\n },\n [updateQuantity, removeFromCart]\n );\n\n // Handler to remove from cart\n const handleRemoveFromCart = useCallback(\n (addonId: string) => {\n removeFromCart(addonId);\n },\n [removeFromCart]\n );\n\n // Permission check - must be after all hooks are defined\n if (!permissions.canViewBillingAccount) {\n return <AccessDenied message=\"You don't have permission to view billing information.\" />;\n }\n\n // Error state\n if (error && isServerError(error) && !isLoading) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.serverError.title', 'Server Unavailable')}\n message={tr(\n 'billing.overview.serverUnavailableMessage',\n 'Unable to load billing overview. The server might be down or experiencing issues.'\n )}\n onRetry={handleRefresh}\n showRetry\n />\n </div>\n );\n }\n\n // Loading state\n if (isLoading && !currentAccount) {\n return (\n <div className=\"flex items-center justify-center min-h-[400px]\">\n <div className=\"flex flex-col items-center gap-3\">\n <Loader2 className=\"size-8 animate-spin text-text-link\" />\n <p className=\"text-sm text-text-secondary\">\n {tr('billing.overview.loadingInfo', 'Loading billing information...')}\n </p>\n </div>\n </div>\n );\n }\n\n // No billing accounts state. Checked against `billingAccounts` AND\n // `currentAccount` (not just the list) because getDefaultBillingAccount\n // lazy-creates a default account for a brand-new org — on that exact\n // first request, getBillingAccountsByOrg's plain read can still observe\n // zero rows in the same response that returned a real, freshly-created\n // currentAccount. A valid currentAccount always means there's a real\n // billing account regardless of what the (possibly momentarily stale)\n // list says.\n if (!isLoading && billingAccounts.length === 0 && !currentAccount) {\n return (\n <div className=\"px-4 sm:px-6 lg:px-8 py-4 sm:py-6\">\n <header className=\"relative overflow-hidden mb-6 sm:mb-8 rounded-card\">\n <AuroraBackground intensity=\"subtle\" />\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n <GradientText>{tr('billing.overview.title', 'Billing Overview')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.emptySetupDescription',\n 'Set up billing to manage subscriptions, payments, and invoices'\n )}\n </p>\n </header>\n <IllustratedEmptyState\n illustration=\"onboarding\"\n title={tr('billing.overview.emptyTitle', 'No billing account')}\n description={tr(\n 'billing.overview.emptyDescription',\n 'Create a billing account to start managing your subscriptions and payments'\n )}\n action={\n permissions.canManageBillingAccount ? (\n <button\n type=\"button\"\n onClick={() => setIsCreateModalOpen(true)}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors duration-200\"\n >\n <Plus className=\"size-4\" />\n {tr('billing.overview.createBillingAccount', 'Create Billing Account')}\n </button>\n ) : undefined\n }\n />\n\n <CreateBillingAccountModal\n isOpen={isCreateModalOpen}\n onClose={() => {\n setIsCreateModalOpen(false);\n setCreateError(null);\n }}\n onSubmit={handleCreateAccount}\n isSubmitting={isCreatingAccount}\n error={createError}\n />\n </div>\n );\n }\n\n return (\n <div className=\"px-4 sm:px-6 lg:px-8 py-4 sm:py-6 space-y-4 sm:space-y-8\">\n {/* Header */}\n <header className=\"relative overflow-hidden rounded-card flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4\">\n <AuroraBackground intensity=\"subtle\" />\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n <GradientText>{tr('billing.overview.title', 'Billing Overview')}</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.description',\n 'Manage your billing accounts, subscriptions, and payment methods'\n )}\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.overview.purpose',\n 'Your billing home base — see your current plan, credit balance and payment methods at a glance, and jump to transactions, usage, credits or new plans. Start here to understand what you are paying for and manage it.'\n )}\n </PagePurpose>\n </div>\n <CTAOverflowMenu\n primary={\n permissions.canManageBillingAccount && billingAccounts.length > 0\n ? {\n label: tr('billing.overview.newAccount', 'New Account'),\n onSelect: () => setIsCreateModalOpen(true),\n icon: <Plus className=\"size-4\" />,\n }\n : undefined\n }\n actions={[\n {\n label: tr('billing.overview.refresh', 'Refresh'),\n onSelect: () => {\n void handleRefresh();\n },\n icon: <RefreshCw className={`size-4 ${isLoading ? 'animate-spin' : ''}`} />,\n },\n ]}\n />\n </header>\n\n {/* Account Selector (if multiple accounts) */}\n {billingAccounts.length > 1 && (\n <div className=\"relative\" data-tour=\"billing-overview-account-selector\">\n <button\n type=\"button\"\n onClick={() => setIsAccountSelectorOpen(!isAccountSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Building2 className=\"size-4 text-text-link\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {currentAccount?.name || tr('billing.overview.selectAccount', 'Select Account')}\n </p>\n <p className=\"text-xs text-text-secondary\">{currentAccount?.email}</p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-text-muted transition-transform ${isAccountSelectorOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {isAccountSelectorOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsAccountSelectorOpen(false)} />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-bg-elevated border border-border-seam rounded-card shadow-elevation-3 z-20 py-1 max-h-60 overflow-y-auto\">\n {billingAccounts.map((account) => (\n <button\n type=\"button\"\n key={account.id}\n onClick={() => handleSelectAccount(account)}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-9 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <Building2 className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {account.name}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">{account.email}</p>\n </div>\n </div>\n {currentAccount?.id === account.id && (\n <Check className=\"size-4 text-text-link flex-shrink-0\" />\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Stats Row */}\n {currentAccount && (\n <div\n className=\"grid grid-cols-2 sm:grid-cols-4 gap-3 sm:gap-4\"\n data-tour=\"billing-overview-stats-row\"\n >\n <StatCard\n className=\"p-3 sm:p-5\"\n label={tr('billing.overview.stats.creditBalance', 'Credit Balance')}\n value={`${(currentAccount.creditAmount || 0).toLocaleString()} Credits`}\n icon={<Wallet className=\"size-5\" />}\n />\n <StatCard\n className=\"p-3 sm:p-5\"\n label={tr('billing.overview.stats.activePlans', 'Active Plans')}\n value={\n activeSubscriptions.length > 0\n ? activeSubscriptions.length === 1\n ? activeSubscriptions[0]?.plan?.name || '1 Plan'\n : `${activeSubscriptions.length} ${tr('billing.overview.stats.plansSuffix', 'Plans')}`\n : tr('billing.overview.stats.none', 'None')\n }\n icon={<RefreshCw className=\"size-5\" />}\n />\n <StatCard\n className=\"p-3 sm:p-5\"\n label={tr('billing.overview.stats.paymentMethods', 'Payment Methods')}\n value={paymentMethods.length}\n icon={<Building2 className=\"size-5\" />}\n />\n <StatCard\n className=\"p-3 sm:p-5\"\n label={tr('billing.overview.stats.paymentCurrency', 'Payment Currency')}\n value={currentAccount.currency || 'USD'}\n icon={<Building2 className=\"size-5\" />}\n />\n </div>\n )}\n\n {/* Main Content Grid */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-4 sm:gap-6\">\n {/* Left Column - Billing Account & Subscription */}\n <div className=\"lg:col-span-2 space-y-4 sm:space-y-6\">\n {/* Billing Account Card */}\n {currentAccount && (\n <BillingAccountCard\n dataTour=\"billing-overview-billing-account-card\"\n account={currentAccount}\n isDefault={currentAccount.isDefault ?? false}\n onEdit={() =>\n currentAccount && navigateTo(`/settings?billingAccountId=${currentAccount.id}`)\n }\n onViewTransactions={() => navigateTo(`/transactions/${currentAccount.id}`)}\n onViewCreditTransactions={() =>\n navigateTo(`/creditTransactions/${currentAccount.id}`)\n }\n />\n )}\n\n {/* Active Subscriptions */}\n <div className=\"space-y-3\">\n {/* Subscription Selector (if multiple subscriptions) */}\n {activeSubscriptions.length > 1 && (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsSubscriptionSelectorOpen(!isSubscriptionSelectorOpen)}\n className=\"w-full sm:w-auto flex items-center justify-between gap-3 px-4 py-3 border border-border-seam rounded-card bg-bg-surface hover:bg-bg-sunken hover:border-border-strong transition-all duration-200\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Crown className=\"size-4 text-text-link\" />\n </div>\n <div className=\"text-left\">\n <p className=\"text-sm font-medium text-text-primary\">\n {selectedSubscription?.plan?.name ||\n tr('billing.overview.selectSubscription', 'Select Subscription')}\n {selectedSubscription?.plan?.product?.name && (\n <span className=\"text-text-secondary font-normal\">\n {' '}\n · {selectedSubscription.plan.product.name}\n </span>\n )}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {activeSubscriptions.length}{' '}\n {tr('billing.overview.activeSubscriptions', 'active subscriptions')}\n </p>\n </div>\n </div>\n <ChevronDown\n className={`size-4 text-text-muted transition-transform ${isSubscriptionSelectorOpen ? 'rotate-180' : ''}`}\n />\n </button>\n\n {isSubscriptionSelectorOpen && (\n <>\n <div\n className=\"fixed inset-0 z-10\"\n onClick={() => setIsSubscriptionSelectorOpen(false)}\n />\n <div className=\"absolute top-full left-0 right-0 sm:right-auto mt-1 w-full sm:w-80 bg-bg-elevated border border-border-seam rounded-card shadow-elevation-3 z-20 py-1 max-h-60 overflow-y-auto\">\n {activeSubscriptions.map((subscription, index) => (\n <button\n type=\"button\"\n key={subscription.id}\n onClick={() => {\n setSelectedSubscriptionIndex(index);\n setIsSubscriptionSelectorOpen(false);\n }}\n className=\"w-full flex items-center justify-between gap-3 px-4 py-3 hover:bg-bg-sunken transition-colors\"\n >\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-9 rounded-lg bg-bg-sunken flex items-center justify-center flex-shrink-0\">\n <Crown className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"text-left min-w-0\">\n <p className=\"text-sm font-medium text-text-primary truncate\">\n {subscription.plan?.name ||\n tr('billing.overview.subscription', 'Subscription')}\n </p>\n <p className=\"text-xs text-text-secondary truncate\">\n {subscription.plan?.product?.name ||\n tr('billing.overview.unknownProduct', 'Unknown Product')}{' '}\n · {tr('billing.overview.ends', 'Ends')}{' '}\n {new Date(subscription.endDate).toLocaleDateString()}\n </p>\n </div>\n </div>\n {selectedSubscriptionIndex === index && (\n <Check className=\"size-4 text-text-link flex-shrink-0\" />\n )}\n </button>\n ))}\n </div>\n </>\n )}\n </div>\n )}\n\n {/* Single Subscription Card */}\n <ActiveSubscriptionCard\n dataTour=\"billing-overview-active-subscription-card\"\n subscription={selectedSubscription}\n onViewDetails={() =>\n selectedSubscription &&\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${selectedSubscription.id}`,\n selectedSubscription.billingAccountId ?? currentAccount?.id\n )\n )\n }\n onUpgrade={() =>\n selectedSubscription &&\n navigateTo(\n withBillingAccountId(\n `/subscriptions/${selectedSubscription.id}/upgrade`,\n selectedSubscription.billingAccountId ?? currentAccount?.id\n )\n )\n }\n onManage={() =>\n currentAccount && navigateTo(`/subscriptions?billingAccountId=${currentAccount.id}`)\n }\n onPurchasePlan={() => navigateTo('/plans')}\n />\n </div>\n\n {/* Payment Methods */}\n {currentAccount && (\n <PaymentMethodsSection\n dataTour=\"billing-overview-payment-methods-section\"\n paymentMethods={paymentMethods}\n billingAccountId={currentAccount.id}\n onAddPaymentMethod={handleAddPaymentMethod}\n onSetDefault={handleSetDefaultPaymentMethod}\n onDelete={handleDeletePaymentMethod}\n isLoading={isLoading}\n />\n )}\n </div>\n\n {/* Right Column - Quick Actions & Info */}\n <div className=\"space-y-4 sm:space-y-6\">\n {/* Quick Actions */}\n <section\n className=\"border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 p-4 sm:p-5\"\n data-tour=\"billing-overview-quick-actions\"\n >\n <h3 className=\"font-semibold text-text-primary mb-4\">\n {tr('billing.overview.quickActions', 'Quick Actions')}\n </h3>\n <div className=\"space-y-1.5\">\n <button\n type=\"button\"\n onClick={() => currentAccount && navigateTo(`/transactions/${currentAccount.id}`)}\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-status-info-bg-subtle flex items-center justify-center\">\n <Receipt className=\"size-4 text-status-info-text\" />\n </div>\n {tr('billing.overview.viewTransactions', 'View Transactions')}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n currentAccount && navigateTo(`/creditTransactions/${currentAccount.id}`)\n }\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-status-warning-bg-subtle flex items-center justify-center\">\n <Wallet className=\"size-4 text-status-warning-text\" />\n </div>\n {tr('billing.overview.creditHistory', 'Credit History')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/checkout/credits')}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n >\n <div className=\"size-9 rounded-lg bg-status-success-bg-subtle flex items-center justify-center\">\n <Coins className=\"size-4 text-status-success-text\" />\n </div>\n {tr('billing.overview.purchaseCredits', 'Purchase Credits')}\n </button>\n <button\n type=\"button\"\n onClick={() =>\n currentAccount && navigateTo(`/usage?billingAccountId=${currentAccount.id}`)\n }\n disabled={!currentAccount}\n className=\"w-full flex items-center gap-3 px-3 py-2.5 text-sm text-left text-text-primary hover:bg-bg-sunken rounded-button transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <div className=\"size-9 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <Activity className=\"size-4 text-text-link\" />\n </div>\n {tr('billing.overview.usageAnalytics', 'Usage Analytics')}\n </button>\n </div>\n </section>\n\n {/* Get Started (if no subscription) — primary emphasis zone */}\n {!hasActiveSubscription && (\n <GlassCard treatment=\"glass\" glow className=\"p-4 sm:p-5\">\n <div className=\"flex items-start gap-3 mb-4\">\n <div className=\"size-10 rounded-lg bg-bg-surface flex items-center justify-center flex-shrink-0 shadow-elevation-1\">\n <Sparkles className=\"size-5 text-text-link\" />\n </div>\n <div>\n <h3 className=\"font-semibold text-text-primary\">\n {tr('billing.overview.getStarted', 'Get Started')}\n </h3>\n <p className=\"text-sm text-text-secondary mt-1\">\n {tr(\n 'billing.overview.getStartedDescription',\n 'Choose a plan that fits your needs to unlock all features'\n )}\n </p>\n </div>\n </div>\n <Button type=\"button\" onClick={() => navigateTo('/plans')} className=\"w-full\">\n {tr('billing.overview.browsePlans', 'Browse Plans')}\n <ArrowRight className=\"size-4\" />\n </Button>\n </GlassCard>\n )}\n </div>\n </div>\n\n {/* Recommended Addons (only if has subscription) - Limited to 3 with cart support */}\n <RecommendedAddonsSection\n dataTour=\"billing-overview-recommended-addons\"\n addons={recommendedAddons}\n hasSubscription={hasActiveSubscription}\n onViewAddon={(addonId) => navigateTo(`/addons/${addonId}`)}\n onPurchaseAddon={(addonId) => navigateTo(`/addons/${addonId}?action=purchase`)}\n onViewAllAddons={() => navigateTo('/addons')}\n onAddToCart={handleAddToCart}\n isInCart={handleIsInCart}\n getCartQuantity={handleGetCartQuantity}\n onUpdateQuantity={handleUpdateQuantity}\n onRemoveFromCart={handleRemoveFromCart}\n maxAddons={3}\n isLoading={isLoadingAddons}\n />\n\n {/* Floating Cart Drawer - shows when items are in cart */}\n <FloatingCartDrawer />\n\n {/* Contextual push-permission nudge, shown once after creating an account */}\n {showPushNudge && <PushPermissionNudge onDismiss={() => setShowPushNudge(false)} />}\n\n {/* Create Billing Account Modal */}\n <CreateBillingAccountModal\n isOpen={isCreateModalOpen}\n onClose={() => {\n setIsCreateModalOpen(false);\n setCreateError(null);\n }}\n onSubmit={handleCreateAccount}\n isSubmitting={isCreatingAccount}\n error={createError}\n />\n\n {/* Add Payment Method Modal */}\n {currentAccount && (\n <AddPaymentMethodModal\n isOpen={isAddPaymentModalOpen}\n onClose={() => setIsAddPaymentModalOpen(false)}\n billingAccountId={currentAccount.id}\n currency={currentAccount.currency}\n country={currentAccount.billingAddresses?.[0]?.country}\n onSuccess={handlePaymentMethodAdded}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { useTr as e } from "../hooks/useTr.js";
2
+ import { useState as t } from "react";
3
+ import { Bell as n, X as r } from "lucide-react";
4
+ import { jsx as i, jsxs as a } from "react/jsx-runtime";
5
+ import { getPushOptInController as o, recordPushPermissionPromptDismissed as s } from "@burdenoff/fe-libs/shared/native";
6
+ //#region src/billing/shared/components/PushPermissionNudge.tsx
7
+ var c = ({ onDismiss: c }) => {
8
+ let l = e(), [u, d] = t(!1), f = async () => {
9
+ let e = o();
10
+ if (!(!e || u)) {
11
+ d(!0);
12
+ try {
13
+ await e.enable();
14
+ } finally {
15
+ d(!1), c();
16
+ }
17
+ }
18
+ }, p = () => {
19
+ s(), c();
20
+ };
21
+ return /* @__PURE__ */ i("div", {
22
+ role: "status",
23
+ className: "fixed bottom-6 right-6 z-50 w-full max-w-sm bg-bg-surface border border-border-subtle rounded-card shadow-elevation-3 p-4",
24
+ children: /* @__PURE__ */ a("div", {
25
+ className: "flex items-start gap-3",
26
+ children: [
27
+ /* @__PURE__ */ i("div", {
28
+ className: "size-9 rounded-full bg-bg-sunken flex items-center justify-center shrink-0",
29
+ children: /* @__PURE__ */ i(n, { className: "size-4 text-text-secondary" })
30
+ }),
31
+ /* @__PURE__ */ a("div", {
32
+ className: "flex-1 min-w-0",
33
+ children: [
34
+ /* @__PURE__ */ i("p", {
35
+ className: "text-sm font-medium text-text-primary",
36
+ children: l("billing.pushNudge.title", "Stay on top of your account")
37
+ }),
38
+ /* @__PURE__ */ i("p", {
39
+ className: "mt-1 text-sm text-text-secondary",
40
+ children: l("billing.pushNudge.body", "Turn on notifications and we'll let you know about billing updates like this one.")
41
+ }),
42
+ /* @__PURE__ */ a("div", {
43
+ className: "mt-3 flex items-center gap-2",
44
+ children: [/* @__PURE__ */ i("button", {
45
+ type: "button",
46
+ onClick: f,
47
+ disabled: u,
48
+ className: "px-3 py-1.5 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors duration-200 disabled:opacity-60",
49
+ children: u ? l("billing.pushNudge.enabling", "Enabling…") : l("billing.pushNudge.enable", "Enable notifications")
50
+ }), /* @__PURE__ */ i("button", {
51
+ type: "button",
52
+ onClick: p,
53
+ className: "px-3 py-1.5 text-sm font-medium text-text-secondary hover:bg-bg-sunken rounded-button transition-colors duration-200",
54
+ children: l("billing.pushNudge.notNow", "Not now")
55
+ })]
56
+ })
57
+ ]
58
+ }),
59
+ /* @__PURE__ */ i("button", {
60
+ type: "button",
61
+ onClick: p,
62
+ "aria-label": l("billing.pushNudge.close", "Close"),
63
+ className: "p-1 text-text-secondary hover:bg-bg-sunken rounded-button transition-colors duration-200 shrink-0",
64
+ children: /* @__PURE__ */ i(r, { className: "size-4" })
65
+ })
66
+ ]
67
+ })
68
+ });
69
+ };
70
+ //#endregion
71
+ export { c as PushPermissionNudge };
72
+
73
+ //# sourceMappingURL=PushPermissionNudge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PushPermissionNudge.js","names":[],"sources":["../../../../src/billing/shared/components/PushPermissionNudge.tsx"],"sourcesContent":["/**\n * Push Permission Nudge\n *\n * A one-time, contextual ask for push permission shown right after a\n * meaningful action (e.g. creating a billing account) rather than hidden\n * behind the notification-centre bell, which many users never open.\n *\n * Still respects the one-shot-prompt rule fe-libs enforces everywhere else:\n * it only renders when `shouldPromptForPushPermission()` says permission has\n * never been asked and the nudge was never dismissed before, and it never\n * re-prompts itself — `enable()`/dismissal both make it disappear for good\n * on this device.\n */\nimport { useState, type FC } from 'react';\nimport { Bell, X } from 'lucide-react';\nimport {\n getPushOptInController,\n recordPushPermissionPromptDismissed,\n} from '@burdenoff/fe-libs/shared/native';\nimport { useTr } from '../hooks/useTr';\n\ninterface PushPermissionNudgeProps {\n onDismiss: () => void;\n}\n\nexport const PushPermissionNudge: FC<PushPermissionNudgeProps> = ({ onDismiss }) => {\n const tr = useTr();\n const [enabling, setEnabling] = useState(false);\n\n const handleEnable = async () => {\n const controller = getPushOptInController();\n if (!controller || enabling) return;\n setEnabling(true);\n try {\n await controller.enable();\n } finally {\n setEnabling(false);\n onDismiss();\n }\n };\n\n const handleDismiss = () => {\n recordPushPermissionPromptDismissed();\n onDismiss();\n };\n\n return (\n <div\n role=\"status\"\n className=\"fixed bottom-6 right-6 z-50 w-full max-w-sm bg-bg-surface border border-border-subtle rounded-card shadow-elevation-3 p-4\"\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"size-9 rounded-full bg-bg-sunken flex items-center justify-center shrink-0\">\n <Bell className=\"size-4 text-text-secondary\" />\n </div>\n <div className=\"flex-1 min-w-0\">\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('billing.pushNudge.title', 'Stay on top of your account')}\n </p>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {tr(\n 'billing.pushNudge.body',\n \"Turn on notifications and we'll let you know about billing updates like this one.\"\n )}\n </p>\n <div className=\"mt-3 flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={handleEnable}\n disabled={enabling}\n className=\"px-3 py-1.5 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-button hover:bg-action-primary-bgHover transition-colors duration-200 disabled:opacity-60\"\n >\n {enabling\n ? tr('billing.pushNudge.enabling', 'Enabling…')\n : tr('billing.pushNudge.enable', 'Enable notifications')}\n </button>\n <button\n type=\"button\"\n onClick={handleDismiss}\n className=\"px-3 py-1.5 text-sm font-medium text-text-secondary hover:bg-bg-sunken rounded-button transition-colors duration-200\"\n >\n {tr('billing.pushNudge.notNow', 'Not now')}\n </button>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={handleDismiss}\n aria-label={tr('billing.pushNudge.close', 'Close')}\n className=\"p-1 text-text-secondary hover:bg-bg-sunken rounded-button transition-colors duration-200 shrink-0\"\n >\n <X className=\"size-4\" />\n </button>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;AAyBA,IAAa,KAAqD,EAAE,mBAAgB;CAClF,IAAM,IAAK,EAAM,GACX,CAAC,GAAU,KAAe,EAAS,EAAK,GAExC,IAAe,YAAY;EAC/B,IAAM,IAAa,EAAuB;EACtC,OAAC,KAAc,IACnB;KAAY,EAAI;GAChB,IAAI;IACF,MAAM,EAAW,OAAO;GAC1B,UAAU;IAER,AADA,EAAY,EAAK,GACjB,EAAU;GACZ;EANgB;CAOlB,GAEM,UAAsB;EAE1B,AADA,EAAoC,GACpC,EAAU;CACZ;CAEA,OACE,kBAAC,OAAD;EACE,MAAK;EACL,WAAU;YAEV,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAM,WAAU,6BAA8B,CAAA;IAC3C,CAAA;IACL,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,2BAA2B,6BAA6B;MAC3D,CAAA;MACH,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,0BACA,mFACF;MACC,CAAA;MACH,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,UAAU;QACV,WAAU;kBAET,IACG,EAAG,8BAA8B,WAAW,IAC5C,EAAG,4BAA4B,sBAAsB;OACnD,CAAA,GACR,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,WAAU;kBAET,EAAG,4BAA4B,SAAS;OACnC,CAAA,CACL;;KACF;;IACL,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,cAAY,EAAG,2BAA2B,OAAO;KACjD,WAAU;eAEV,kBAAC,GAAD,EAAG,WAAU,SAAU,CAAA;IACjB,CAAA;GACL;;CACF,CAAA;AAET"}
@@ -6,3 +6,4 @@ import "./ServerError.js";
6
6
  import "./GeoPricingOverridesEditor.js";
7
7
  import "./CurrencyPriceOverridesEditor.js";
8
8
  import "./ActorIdentity.js";
9
+ import "./PushPermissionNudge.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-billing",
3
- "version": "2026.1005.6",
3
+ "version": "2026.1012.1",
4
4
  "description": "Billing microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [
@@ -44,7 +44,7 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "@apollo/client": "^4.1.4",
47
- "@burdenoff/fe-libs": "2026.801.1",
47
+ "@burdenoff/fe-libs": "2026.1012.1",
48
48
  "@tanstack/react-query": "^5.90.16",
49
49
  "clsx": "^2.1.1",
50
50
  "graphql": "^16.10.0",