@burdenoff/microfe-billing 2026.527.1 → 2026.529.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.
Files changed (30) hide show
  1. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +82 -81
  2. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  3. package/dist/billing/modules/plans/pages/PlanDetailPage.js +105 -104
  4. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  5. package/dist/billing/modules/plans/pages/PlanEditPage.js +162 -161
  6. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  7. package/dist/billing/modules/plans/pages/PlansBrowsePage.js +85 -84
  8. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  9. package/dist/billing/modules/plans/pages/PlansListPage.js +115 -114
  10. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  11. package/dist/billing/modules/settings/pages/SettingsPage.js +338 -337
  12. package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
  13. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +157 -123
  14. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
  15. package/dist/billing/shared/components/ActorIdentity.js +67 -0
  16. package/dist/billing/shared/components/ActorIdentity.js.map +1 -0
  17. package/dist/billing/shared/components/EmptyState.js +7 -21
  18. package/dist/billing/shared/components/EmptyState.js.map +1 -1
  19. package/dist/billing/shared/components/PageHeader.js +7 -19
  20. package/dist/billing/shared/components/PageHeader.js.map +1 -1
  21. package/dist/billing/shared/components/index.js +1 -0
  22. package/dist/billing/shared/hooks/index.js +1 -0
  23. package/dist/billing/shared/hooks/useActorProfiles.js +113 -0
  24. package/dist/billing/shared/hooks/useActorProfiles.js.map +1 -0
  25. package/dist/billing/shared/utils/index.js +1 -0
  26. package/dist/billing/shared/utils/planFeatureQuantity.js +11 -0
  27. package/dist/billing/shared/utils/planFeatureQuantity.js.map +1 -0
  28. package/dist/generated/global-operations.js +1 -0
  29. package/dist/generated/global-operations.js.map +1 -1
  30. package/package.json +2 -2
@@ -2,120 +2,121 @@ import { ServerError as e } from "../../../shared/components/ServerError.js";
2
2
  import "../../../shared/components/index.js";
3
3
  import { formatCurrency as t, formatPlanDuration as n, formatPlanDurationLabel as r } from "../../../shared/utils/format.js";
4
4
  import { isServerError as i } from "../../../shared/utils/error.js";
5
+ import { getPlanFeatureQuantity as a } from "../../../shared/utils/planFeatureQuantity.js";
5
6
  import "../../../shared/utils/index.js";
6
- import { useBillingNavigate as a } from "../../../hooks/useBillingNavigate.js";
7
- import { usePlan as o } from "../hooks/usePlans.js";
7
+ import { useBillingNavigate as o } from "../../../hooks/useBillingNavigate.js";
8
+ import { usePlan as s } from "../hooks/usePlans.js";
8
9
  import "../hooks/index.js";
9
- import { useMemo as s } from "react";
10
- import { useParams as c } from "react-router-dom";
11
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
12
- import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
+ import { useMemo as c } from "react";
11
+ import { useParams as l } from "react-router-dom";
12
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
13
+ import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
13
14
  //#region src/billing/modules/plans/pages/PlanBrowseDetailPage.tsx
14
- function f(e) {
15
+ function p(e) {
15
16
  let t = /* @__PURE__ */ new Map();
16
17
  for (let n of e) {
17
18
  if (!n.quota) continue;
18
- let e = n.quota.id, r = n.quota.limits, i = r?.type ?? "unknown", a = r?.value ?? 0, o = t.get(e);
19
- o ? o.totalValue += a : t.set(e, {
19
+ let e = n.quota.id, r = n.quota.limits?.type ?? "unknown", i = a(n), o = t.get(e);
20
+ o ? o.totalValue += i : t.set(e, {
20
21
  quotaId: e,
21
22
  name: n.quota.name ?? "Unknown Quota",
22
23
  quotaType: n.quota.quotaType ?? "",
23
- limitType: i,
24
- totalValue: a
24
+ limitType: r,
25
+ totalValue: i
25
26
  });
26
27
  }
27
28
  return Array.from(t.values());
28
29
  }
29
- var p = () => {
30
- let { t: p } = d(), m = (e, t) => {
31
- let n = p(e);
30
+ var m = () => {
31
+ let { t: a } = f(), m = (e, t) => {
32
+ let n = a(e);
32
33
  return n === e ? t : n;
33
- }, { planId: h } = c(), g = a(), { plan: _, isLoading: v, error: y, refetch: b } = o(h), x = s(() => f(_?.features || []), [_?.features]);
34
- return v ? /* @__PURE__ */ u("div", {
34
+ }, { planId: h } = l(), g = o(), { plan: _, isLoading: v, error: y, refetch: b } = s(h), x = c(() => p(_?.features || []), [_?.features]);
35
+ return v ? /* @__PURE__ */ d("div", {
35
36
  className: "space-y-6 p-6",
36
- children: [/* @__PURE__ */ u("div", {
37
+ children: [/* @__PURE__ */ d("div", {
37
38
  className: "flex items-center gap-4",
38
- children: [/* @__PURE__ */ l("div", { className: "size-10 bg-muted animate-pulse rounded" }), /* @__PURE__ */ l("div", { className: "h-8 w-48 bg-muted animate-pulse rounded" })]
39
- }), /* @__PURE__ */ u("div", {
39
+ children: [/* @__PURE__ */ u("div", { className: "size-10 bg-muted animate-pulse rounded" }), /* @__PURE__ */ u("div", { className: "h-8 w-48 bg-muted animate-pulse rounded" })]
40
+ }), /* @__PURE__ */ d("div", {
40
41
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
41
- children: [/* @__PURE__ */ l("div", {
42
+ children: [/* @__PURE__ */ u("div", {
42
43
  className: "lg:col-span-2 space-y-6",
43
- children: /* @__PURE__ */ u("div", {
44
+ children: /* @__PURE__ */ d("div", {
44
45
  className: "border border-border rounded-lg p-6 space-y-4",
45
- children: [/* @__PURE__ */ l("div", { className: "h-6 w-32 bg-muted animate-pulse rounded" }), /* @__PURE__ */ l("div", { className: "h-4 w-full bg-muted animate-pulse rounded" })]
46
+ children: [/* @__PURE__ */ u("div", { className: "h-6 w-32 bg-muted animate-pulse rounded" }), /* @__PURE__ */ u("div", { className: "h-4 w-full bg-muted animate-pulse rounded" })]
46
47
  })
47
- }), /* @__PURE__ */ u("div", {
48
+ }), /* @__PURE__ */ d("div", {
48
49
  className: "border border-border rounded-lg p-6 space-y-4",
49
- children: [/* @__PURE__ */ l("div", { className: "h-6 w-24 bg-muted animate-pulse rounded" }), /* @__PURE__ */ l("div", { className: "h-10 w-full bg-muted animate-pulse rounded" })]
50
+ children: [/* @__PURE__ */ u("div", { className: "h-6 w-24 bg-muted animate-pulse rounded" }), /* @__PURE__ */ u("div", { className: "h-10 w-full bg-muted animate-pulse rounded" })]
50
51
  })]
51
52
  })]
52
- }) : y && i(y) ? /* @__PURE__ */ l("div", {
53
+ }) : y && i(y) ? /* @__PURE__ */ u("div", {
53
54
  className: "p-6",
54
- children: /* @__PURE__ */ l(e, {
55
+ children: /* @__PURE__ */ u(e, {
55
56
  title: m("billing.plans.serverUnavailable", "Server Unavailable"),
56
57
  message: m("billing.plans.unableToLoadPlanDetails", "Unable to load plan details."),
57
58
  onRetry: () => b(),
58
59
  showRetry: !0
59
60
  })
60
- }) : _ ? /* @__PURE__ */ u("div", {
61
+ }) : _ ? /* @__PURE__ */ d("div", {
61
62
  className: "space-y-6 p-6",
62
- children: [/* @__PURE__ */ u("div", {
63
+ children: [/* @__PURE__ */ d("div", {
63
64
  className: "flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between",
64
- children: [/* @__PURE__ */ u("div", {
65
+ children: [/* @__PURE__ */ d("div", {
65
66
  className: "flex items-start gap-4",
66
- children: [/* @__PURE__ */ l("button", {
67
+ children: [/* @__PURE__ */ u("button", {
67
68
  type: "button",
68
69
  onClick: () => g("/plans"),
69
70
  className: "p-2 -ml-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors",
70
- children: /* @__PURE__ */ l("svg", {
71
+ children: /* @__PURE__ */ u("svg", {
71
72
  className: "size-5",
72
73
  fill: "none",
73
74
  viewBox: "0 0 24 24",
74
75
  stroke: "currentColor",
75
- children: /* @__PURE__ */ l("path", {
76
+ children: /* @__PURE__ */ u("path", {
76
77
  strokeLinecap: "round",
77
78
  strokeLinejoin: "round",
78
79
  strokeWidth: 2,
79
80
  d: "M15 19l-7-7 7-7"
80
81
  })
81
82
  })
82
- }), /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h1", {
83
+ }), /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("h1", {
83
84
  className: "text-2xl font-bold text-foreground",
84
85
  children: _.name
85
- }), /* @__PURE__ */ u("p", {
86
+ }), /* @__PURE__ */ d("p", {
86
87
  className: "text-sm text-muted-foreground mt-1",
87
88
  children: [r(_.duration), " billing plan"]
88
89
  })] })]
89
- }), /* @__PURE__ */ l("button", {
90
+ }), /* @__PURE__ */ u("button", {
90
91
  type: "button",
91
92
  onClick: () => g(`/checkout/${_.id}`),
92
93
  className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
93
94
  children: "Subscribe to Plan"
94
95
  })]
95
- }), /* @__PURE__ */ u("div", {
96
+ }), /* @__PURE__ */ d("div", {
96
97
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
97
- children: [/* @__PURE__ */ u("div", {
98
+ children: [/* @__PURE__ */ d("div", {
98
99
  className: "lg:col-span-2 space-y-6",
99
- children: [/* @__PURE__ */ u("div", {
100
+ children: [/* @__PURE__ */ d("div", {
100
101
  className: "border border-border rounded-lg bg-card overflow-hidden",
101
- children: [/* @__PURE__ */ l("div", {
102
+ children: [/* @__PURE__ */ u("div", {
102
103
  className: "p-6 border-b border-border bg-muted/30",
103
- children: /* @__PURE__ */ l("h2", {
104
+ children: /* @__PURE__ */ u("h2", {
104
105
  className: "text-lg font-semibold text-foreground",
105
106
  children: m("billing.plans.pricing", "Pricing")
106
107
  })
107
- }), /* @__PURE__ */ u("div", {
108
+ }), /* @__PURE__ */ d("div", {
108
109
  className: "p-6",
109
- children: [/* @__PURE__ */ u("div", {
110
+ children: [/* @__PURE__ */ d("div", {
110
111
  className: "flex items-baseline gap-1",
111
- children: [/* @__PURE__ */ l("span", {
112
+ children: [/* @__PURE__ */ u("span", {
112
113
  className: "text-4xl font-bold text-foreground",
113
114
  children: t(_.price, _.currency)
114
- }), /* @__PURE__ */ u("span", {
115
+ }), /* @__PURE__ */ d("span", {
115
116
  className: "text-lg text-muted-foreground",
116
117
  children: ["/ ", n(_.duration)]
117
118
  })]
118
- }), /* @__PURE__ */ u("p", {
119
+ }), /* @__PURE__ */ d("p", {
119
120
  className: "text-sm text-muted-foreground mt-2",
120
121
  children: [
121
122
  m("billing.plans.billed", "Billed"),
@@ -124,11 +125,11 @@ var p = () => {
124
125
  ]
125
126
  })]
126
127
  })]
127
- }), /* @__PURE__ */ u("div", {
128
+ }), /* @__PURE__ */ d("div", {
128
129
  className: "border border-border rounded-lg bg-card overflow-hidden",
129
- children: [/* @__PURE__ */ l("div", {
130
+ children: [/* @__PURE__ */ u("div", {
130
131
  className: "p-6 border-b border-border bg-muted/30",
131
- children: /* @__PURE__ */ u("h2", {
132
+ children: /* @__PURE__ */ d("h2", {
132
133
  className: "text-lg font-semibold text-foreground",
133
134
  children: [
134
135
  m("billing.plans.featuresAndQuotas", "Features & Quotas"),
@@ -137,46 +138,46 @@ var p = () => {
137
138
  ")"
138
139
  ]
139
140
  })
140
- }), /* @__PURE__ */ l("div", {
141
+ }), /* @__PURE__ */ u("div", {
141
142
  className: "p-6",
142
- children: x.length === 0 ? /* @__PURE__ */ l("p", {
143
+ children: x.length === 0 ? /* @__PURE__ */ u("p", {
143
144
  className: "text-sm text-muted-foreground",
144
145
  children: m("billing.plans.noFeatures", "No features configured for this plan.")
145
- }) : /* @__PURE__ */ l("ul", {
146
+ }) : /* @__PURE__ */ u("ul", {
146
147
  className: "space-y-3",
147
- children: x.map((e) => /* @__PURE__ */ u("li", {
148
+ children: x.map((e) => /* @__PURE__ */ d("li", {
148
149
  className: "flex items-start gap-3 p-3 rounded-lg bg-muted/30",
149
- children: [/* @__PURE__ */ l("div", {
150
+ children: [/* @__PURE__ */ u("div", {
150
151
  className: "size-8 rounded-full bg-status-success-bg-subtle flex items-center justify-center shrink-0",
151
- children: /* @__PURE__ */ l("svg", {
152
+ children: /* @__PURE__ */ u("svg", {
152
153
  className: "size-4 text-status-success-text",
153
154
  fill: "none",
154
155
  viewBox: "0 0 24 24",
155
156
  stroke: "currentColor",
156
- children: /* @__PURE__ */ l("path", {
157
+ children: /* @__PURE__ */ u("path", {
157
158
  strokeLinecap: "round",
158
159
  strokeLinejoin: "round",
159
160
  strokeWidth: 2,
160
161
  d: "M5 13l4 4L19 7"
161
162
  })
162
163
  })
163
- }), /* @__PURE__ */ u("div", {
164
+ }), /* @__PURE__ */ d("div", {
164
165
  className: "flex-1",
165
- children: [/* @__PURE__ */ u("div", {
166
+ children: [/* @__PURE__ */ d("div", {
166
167
  className: "flex items-center justify-between",
167
- children: [/* @__PURE__ */ l("p", {
168
+ children: [/* @__PURE__ */ u("p", {
168
169
  className: "font-medium text-foreground",
169
170
  children: e.name
170
- }), /* @__PURE__ */ l("span", {
171
+ }), /* @__PURE__ */ u("span", {
171
172
  className: "font-semibold text-foreground",
172
173
  children: e.totalValue.toLocaleString()
173
174
  })]
174
- }), /* @__PURE__ */ u("div", {
175
+ }), /* @__PURE__ */ d("div", {
175
176
  className: "flex items-center gap-2 mt-1",
176
- children: [/* @__PURE__ */ u("span", {
177
+ children: [/* @__PURE__ */ d("span", {
177
178
  className: "text-sm text-muted-foreground capitalize",
178
179
  children: ["Type: ", e.limitType]
179
- }), e.quotaType && /* @__PURE__ */ l("span", {
180
+ }), e.quotaType && /* @__PURE__ */ u("span", {
180
181
  className: "px-2 py-0.5 text-xs font-medium bg-muted text-muted-foreground rounded",
181
182
  children: e.quotaType
182
183
  })]
@@ -186,23 +187,23 @@ var p = () => {
186
187
  })
187
188
  })]
188
189
  })]
189
- }), /* @__PURE__ */ u("div", {
190
+ }), /* @__PURE__ */ d("div", {
190
191
  className: "space-y-6",
191
- children: [/* @__PURE__ */ u("div", {
192
+ children: [/* @__PURE__ */ d("div", {
192
193
  className: "border border-border rounded-lg bg-card overflow-hidden",
193
- children: [/* @__PURE__ */ l("div", {
194
+ children: [/* @__PURE__ */ u("div", {
194
195
  className: "p-6 border-b border-border bg-muted/30",
195
- children: /* @__PURE__ */ l("h2", {
196
+ children: /* @__PURE__ */ u("h2", {
196
197
  className: "text-lg font-semibold text-foreground",
197
198
  children: m("billing.plans.subscribeTitle", "Subscribe")
198
199
  })
199
- }), /* @__PURE__ */ u("div", {
200
+ }), /* @__PURE__ */ d("div", {
200
201
  className: "p-6 space-y-4",
201
202
  children: [
202
- /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
203
+ /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
203
204
  className: "text-sm text-muted-foreground",
204
205
  children: m("billing.plans.price", "Price")
205
- }), /* @__PURE__ */ u("p", {
206
+ }), /* @__PURE__ */ d("p", {
206
207
  className: "text-sm font-medium text-foreground mt-0.5",
207
208
  children: [
208
209
  t(_.price, _.currency),
@@ -210,21 +211,21 @@ var p = () => {
210
211
  n(_.duration)
211
212
  ]
212
213
  })] }),
213
- /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
214
+ /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
214
215
  className: "text-sm text-muted-foreground",
215
216
  children: m("billing.plans.billingPeriod", "Billing Period")
216
- }), /* @__PURE__ */ l("p", {
217
+ }), /* @__PURE__ */ u("p", {
217
218
  className: "text-sm text-foreground mt-0.5",
218
219
  children: r(_.duration)
219
220
  })] }),
220
- /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
221
+ /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
221
222
  className: "text-sm text-muted-foreground",
222
223
  children: m("billing.plans.quotasIncluded", "Quotas Included")
223
- }), /* @__PURE__ */ l("p", {
224
+ }), /* @__PURE__ */ u("p", {
224
225
  className: "text-sm font-medium text-foreground mt-0.5",
225
226
  children: x.length
226
227
  })] }),
227
- /* @__PURE__ */ l("button", {
228
+ /* @__PURE__ */ u("button", {
228
229
  type: "button",
229
230
  onClick: () => g(`/checkout/${_.id}`),
230
231
  className: "w-full px-4 py-2.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
@@ -232,7 +233,7 @@ var p = () => {
232
233
  })
233
234
  ]
234
235
  })]
235
- }), /* @__PURE__ */ l("button", {
236
+ }), /* @__PURE__ */ u("button", {
236
237
  type: "button",
237
238
  onClick: () => g("/plans"),
238
239
  className: "w-full px-4 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors",
@@ -240,20 +241,20 @@ var p = () => {
240
241
  })]
241
242
  })]
242
243
  })]
243
- }) : /* @__PURE__ */ l("div", {
244
+ }) : /* @__PURE__ */ u("div", {
244
245
  className: "flex items-center justify-center h-full min-h-[400px]",
245
- children: /* @__PURE__ */ u("div", {
246
+ children: /* @__PURE__ */ d("div", {
246
247
  className: "text-center space-y-4",
247
248
  children: [
248
- /* @__PURE__ */ l("h2", {
249
+ /* @__PURE__ */ u("h2", {
249
250
  className: "text-lg font-semibold text-foreground",
250
251
  children: m("billing.plans.planNotFound", "Plan not found")
251
252
  }),
252
- /* @__PURE__ */ l("p", {
253
+ /* @__PURE__ */ u("p", {
253
254
  className: "text-sm text-muted-foreground",
254
255
  children: y?.message || m("billing.plans.planNotFoundMessage", "The plan you are looking for does not exist.")
255
256
  }),
256
- /* @__PURE__ */ l("button", {
257
+ /* @__PURE__ */ u("button", {
257
258
  type: "button",
258
259
  onClick: () => g("/plans"),
259
260
  className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors",
@@ -264,6 +265,6 @@ var p = () => {
264
265
  });
265
266
  };
266
267
  //#endregion
267
- export { p as PlanBrowseDetailPage };
268
+ export { m as PlanBrowseDetailPage };
268
269
 
269
270
  //# sourceMappingURL=PlanBrowseDetailPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlanBrowseDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanBrowseDetailPage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Browse Detail Page (User-facing)\n * View plan details and subscribe. No edit/toggle actions.\n */\n\nimport { type FC, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlan } from '../hooks';\nimport { ServerError } from '../../../shared/components';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n isServerError,\n} from '../../../shared/utils';\nimport type { PlanFeature } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n quotaType: string;\n limitType: string;\n totalValue: number;\n}\n\nfunction combineQuotas(features: PlanFeature[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n for (const feature of features) {\n if (!feature.quota) continue;\n const quotaId = feature.quota.id;\n const limits = feature.quota.limits as { type?: string; value?: number } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = limits?.value ?? 0;\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n quotaType: feature.quota.quotaType ?? '',\n limitType,\n totalValue: limitValue,\n });\n }\n }\n return Array.from(quotaMap.values());\n}\n\nexport const PlanBrowseDetailPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { planId } = useParams<{ planId: string }>();\n const navigateTo = useBillingNavigate();\n const { plan, isLoading, error, refetch } = usePlan(planId);\n\n const combinedQuotas = useMemo(() => combineQuotas(plan?.features || []), [plan?.features]);\n\n if (isLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-muted animate-pulse rounded\" />\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <div className=\"border border-border rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-muted animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-muted animate-pulse rounded\" />\n </div>\n </div>\n <div className=\"border border-border rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-muted animate-pulse rounded\" />\n <div className=\"h-10 w-full bg-muted animate-pulse rounded\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.plans.serverUnavailable', 'Server Unavailable')}\n message={tr('billing.plans.unableToLoadPlanDetails', 'Unable to load plan details.')}\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n if (!plan) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.plans.planNotFound', 'Plan not found')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {error?.message ||\n tr(\n 'billing.plans.planNotFoundMessage',\n 'The plan you are looking for does not exist.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Back to Plans\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-4\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"p-2 -ml-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 19l-7-7 7-7\"\n />\n </svg>\n </button>\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">{plan.name}</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {formatPlanDurationLabel(plan.duration)} billing plan\n </p>\n </div>\n </div>\n\n <button\n type=\"button\"\n onClick={() => navigateTo(`/checkout/${plan.id}`)}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Subscribe to Plan\n </button>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Pricing Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.plans.pricing', 'Pricing')}\n </h2>\n </div>\n <div className=\"p-6\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold text-foreground\">\n {formatCurrency(plan.price, plan.currency)}\n </span>\n <span className=\"text-lg text-muted-foreground\">\n / {formatPlanDuration(plan.duration)}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground mt-2\">\n {tr('billing.plans.billed', 'Billed')}{' '}\n {plan.duration === 'monthly'\n ? tr('billing.plans.everyMonth', 'every month')\n : tr('billing.plans.annually', 'annually')}\n </p>\n </div>\n </div>\n\n {/* Features Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.plans.featuresAndQuotas', 'Features & Quotas')} (\n {combinedQuotas.length})\n </h2>\n </div>\n <div className=\"p-6\">\n {combinedQuotas.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.noFeatures', 'No features configured for this plan.')}\n </p>\n ) : (\n <ul className=\"space-y-3\">\n {combinedQuotas.map((quota) => (\n <li\n key={quota.quotaId}\n className=\"flex items-start gap-3 p-3 rounded-lg bg-muted/30\"\n >\n <div className=\"size-8 rounded-full bg-status-success-bg-subtle flex items-center justify-center shrink-0\">\n <svg\n className=\"size-4 text-status-success-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <p className=\"font-medium text-foreground\">{quota.name}</p>\n <span className=\"font-semibold text-foreground\">\n {quota.totalValue.toLocaleString()}\n </span>\n </div>\n <div className=\"flex items-center gap-2 mt-1\">\n <span className=\"text-sm text-muted-foreground capitalize\">\n Type: {quota.limitType}\n </span>\n {quota.quotaType && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-muted text-muted-foreground rounded\">\n {quota.quotaType}\n </span>\n )}\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Subscribe Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.plans.subscribeTitle', 'Subscribe')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.price', 'Price')}\n </p>\n <p className=\"text-sm font-medium text-foreground mt-0.5\">\n {formatCurrency(plan.price, plan.currency)} / {formatPlanDuration(plan.duration)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.billingPeriod', 'Billing Period')}\n </p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {formatPlanDurationLabel(plan.duration)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.quotasIncluded', 'Quotas Included')}\n </p>\n <p className=\"text-sm font-medium text-foreground mt-0.5\">\n {combinedQuotas.length}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/checkout/${plan.id}`)}\n className=\"w-full px-4 py-2.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Subscribe to Plan\n </button>\n </div>\n </div>\n\n {/* Back */}\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Back to Plans\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PlanBrowseDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanBrowseDetailPage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Browse Detail Page (User-facing)\n * View plan details and subscribe. No edit/toggle actions.\n */\n\nimport { type FC, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlan } from '../hooks';\nimport { ServerError } from '../../../shared/components';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n getPlanFeatureQuantity,\n isServerError,\n} from '../../../shared/utils';\nimport type { PlanFeature } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n quotaType: string;\n limitType: string;\n totalValue: number;\n}\n\nfunction combineQuotas(features: PlanFeature[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n for (const feature of features) {\n if (!feature.quota) continue;\n const quotaId = feature.quota.id;\n const limits = feature.quota.limits as { type?: string; value?: number } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = getPlanFeatureQuantity(feature);\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n quotaType: feature.quota.quotaType ?? '',\n limitType,\n totalValue: limitValue,\n });\n }\n }\n return Array.from(quotaMap.values());\n}\n\nexport const PlanBrowseDetailPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { planId } = useParams<{ planId: string }>();\n const navigateTo = useBillingNavigate();\n const { plan, isLoading, error, refetch } = usePlan(planId);\n\n const combinedQuotas = useMemo(() => combineQuotas(plan?.features || []), [plan?.features]);\n\n if (isLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-muted animate-pulse rounded\" />\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <div className=\"border border-border rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-muted animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-muted animate-pulse rounded\" />\n </div>\n </div>\n <div className=\"border border-border rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-muted animate-pulse rounded\" />\n <div className=\"h-10 w-full bg-muted animate-pulse rounded\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.plans.serverUnavailable', 'Server Unavailable')}\n message={tr('billing.plans.unableToLoadPlanDetails', 'Unable to load plan details.')}\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n if (!plan) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.plans.planNotFound', 'Plan not found')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {error?.message ||\n tr(\n 'billing.plans.planNotFoundMessage',\n 'The plan you are looking for does not exist.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Back to Plans\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-4\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"p-2 -ml-2 text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 19l-7-7 7-7\"\n />\n </svg>\n </button>\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">{plan.name}</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {formatPlanDurationLabel(plan.duration)} billing plan\n </p>\n </div>\n </div>\n\n <button\n type=\"button\"\n onClick={() => navigateTo(`/checkout/${plan.id}`)}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Subscribe to Plan\n </button>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Pricing Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.plans.pricing', 'Pricing')}\n </h2>\n </div>\n <div className=\"p-6\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold text-foreground\">\n {formatCurrency(plan.price, plan.currency)}\n </span>\n <span className=\"text-lg text-muted-foreground\">\n / {formatPlanDuration(plan.duration)}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground mt-2\">\n {tr('billing.plans.billed', 'Billed')}{' '}\n {plan.duration === 'monthly'\n ? tr('billing.plans.everyMonth', 'every month')\n : tr('billing.plans.annually', 'annually')}\n </p>\n </div>\n </div>\n\n {/* Features Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.plans.featuresAndQuotas', 'Features & Quotas')} (\n {combinedQuotas.length})\n </h2>\n </div>\n <div className=\"p-6\">\n {combinedQuotas.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.noFeatures', 'No features configured for this plan.')}\n </p>\n ) : (\n <ul className=\"space-y-3\">\n {combinedQuotas.map((quota) => (\n <li\n key={quota.quotaId}\n className=\"flex items-start gap-3 p-3 rounded-lg bg-muted/30\"\n >\n <div className=\"size-8 rounded-full bg-status-success-bg-subtle flex items-center justify-center shrink-0\">\n <svg\n className=\"size-4 text-status-success-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <p className=\"font-medium text-foreground\">{quota.name}</p>\n <span className=\"font-semibold text-foreground\">\n {quota.totalValue.toLocaleString()}\n </span>\n </div>\n <div className=\"flex items-center gap-2 mt-1\">\n <span className=\"text-sm text-muted-foreground capitalize\">\n Type: {quota.limitType}\n </span>\n {quota.quotaType && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-muted text-muted-foreground rounded\">\n {quota.quotaType}\n </span>\n )}\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Subscribe Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-6 border-b border-border bg-muted/30\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.plans.subscribeTitle', 'Subscribe')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.price', 'Price')}\n </p>\n <p className=\"text-sm font-medium text-foreground mt-0.5\">\n {formatCurrency(plan.price, plan.currency)} / {formatPlanDuration(plan.duration)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.billingPeriod', 'Billing Period')}\n </p>\n <p className=\"text-sm text-foreground mt-0.5\">\n {formatPlanDurationLabel(plan.duration)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.quotasIncluded', 'Quotas Included')}\n </p>\n <p className=\"text-sm font-medium text-foreground mt-0.5\">\n {combinedQuotas.length}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/checkout/${plan.id}`)}\n className=\"w-full px-4 py-2.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Subscribe to Plan\n </button>\n </div>\n </div>\n\n {/* Back */}\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Back to Plans\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}