@burdenoff/microfe-billing 2026.803.1 → 2026.805.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.
@@ -1,72 +1,72 @@
1
1
  import { formatCurrency as e, formatPlanDuration as t, formatPlanDurationLabel as n, formatQuotaValue as r } from "../../../shared/utils/format.js";
2
- import { getPlanFeatureQuantity as i, getPlanFeatureUnit as a } from "../../../shared/utils/planFeatureQuantity.js";
3
- import { useBillingPermissions as o } from "../../../hooks/useBillingPermissions.js";
4
- import { useBillingNavigate as s } from "../../../hooks/useBillingNavigate.js";
5
- import { usePlan as c, usePlanMutations as l } from "../hooks/usePlans.js";
2
+ import { getPlanFeatureQuantity as i, getPlanFeatureUnit as a, getQuotaDisplayName as o } from "../../../shared/utils/planFeatureQuantity.js";
3
+ import { useBillingPermissions as s } from "../../../hooks/useBillingPermissions.js";
4
+ import { useBillingNavigate as c } from "../../../hooks/useBillingNavigate.js";
5
+ import { usePlan as l, usePlanMutations as u } from "../hooks/usePlans.js";
6
6
  import "../hooks/index.js";
7
- import { useMemo as u } from "react";
8
- import { useParams as d } from "react-router";
9
- import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
10
- import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
11
- import { EmphasisPanel as g, PagePurpose as _ } from "@burdenoff/fe-libs/ui";
7
+ import { useMemo as d } from "react";
8
+ import { useParams as f } from "react-router";
9
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
10
+ import { useI18n as g } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
11
+ import { EmphasisPanel as _, PagePurpose as v } from "@burdenoff/fe-libs/ui";
12
12
  //#region src/billing/modules/plans/pages/PlanDetailPage.tsx
13
- function v(e) {
13
+ function y(e) {
14
14
  let t = /* @__PURE__ */ new Map();
15
15
  for (let n of e) {
16
16
  if (!n.quota) continue;
17
- let e = n.quota.id, r = n.quota.limits?.type ?? "unknown", o = i(n), s = t.get(e);
18
- s ? (s.totalValue += o, s.count += 1) : t.set(e, {
17
+ let e = n.quota.id, r = n.quota.limits?.type ?? "unknown", s = i(n), c = t.get(e);
18
+ c ? (c.totalValue += s, c.count += 1) : t.set(e, {
19
19
  quotaId: e,
20
- name: n.quota.name ?? "Unknown Quota",
20
+ name: o(n),
21
21
  quotaType: n.quota.quotaType ?? "",
22
22
  limitType: r,
23
- totalValue: o,
23
+ totalValue: s,
24
24
  count: 1,
25
25
  unit: a(n)
26
26
  });
27
27
  }
28
28
  return Array.from(t.values());
29
29
  }
30
- var y = () => {
31
- let { t: i } = h(), a = (e, t) => {
30
+ var b = () => {
31
+ let { t: i } = g(), a = (e, t) => {
32
32
  let n = i(e);
33
33
  return n === e ? t : n;
34
- }, { planId: y } = d(), b = s(), x = o(), { plan: S, isLoading: C, error: w, refetch: T } = c(y), { togglePlan: E, isToggling: D } = l(), O = u(() => v(S?.features || []), [S?.features]);
35
- return x.canViewPlans ? C ? /* @__PURE__ */ m("div", {
34
+ }, { planId: o } = f(), b = c(), x = s(), { plan: S, isLoading: C, error: w, refetch: T } = l(o), { togglePlan: E, isToggling: D } = u(), O = d(() => y(S?.features || []), [S?.features]);
35
+ return x.canViewPlans ? C ? /* @__PURE__ */ h("div", {
36
36
  className: "space-y-6 p-6",
37
- children: [/* @__PURE__ */ m("div", {
37
+ children: [/* @__PURE__ */ h("div", {
38
38
  className: "flex items-center gap-4",
39
- children: [/* @__PURE__ */ p("div", { className: "size-10 bg-bg-sunken animate-pulse rounded" }), /* @__PURE__ */ p("div", { className: "h-8 w-48 bg-bg-sunken animate-pulse rounded" })]
40
- }), /* @__PURE__ */ m("div", {
39
+ children: [/* @__PURE__ */ m("div", { className: "size-10 bg-bg-sunken animate-pulse rounded" }), /* @__PURE__ */ m("div", { className: "h-8 w-48 bg-bg-sunken animate-pulse rounded" })]
40
+ }), /* @__PURE__ */ h("div", {
41
41
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
42
- children: [/* @__PURE__ */ p("div", {
42
+ children: [/* @__PURE__ */ m("div", {
43
43
  className: "lg:col-span-2 space-y-6",
44
- children: /* @__PURE__ */ m("div", {
44
+ children: /* @__PURE__ */ h("div", {
45
45
  className: "border border-border-subtle rounded-lg p-6 space-y-4",
46
46
  children: [
47
- /* @__PURE__ */ p("div", { className: "h-6 w-32 bg-bg-sunken animate-pulse rounded" }),
48
- /* @__PURE__ */ p("div", { className: "h-4 w-full bg-bg-sunken animate-pulse rounded" }),
49
- /* @__PURE__ */ p("div", { className: "h-4 w-3/4 bg-bg-sunken animate-pulse rounded" })
47
+ /* @__PURE__ */ m("div", { className: "h-6 w-32 bg-bg-sunken animate-pulse rounded" }),
48
+ /* @__PURE__ */ m("div", { className: "h-4 w-full bg-bg-sunken animate-pulse rounded" }),
49
+ /* @__PURE__ */ m("div", { className: "h-4 w-3/4 bg-bg-sunken animate-pulse rounded" })
50
50
  ]
51
51
  })
52
- }), /* @__PURE__ */ m("div", {
52
+ }), /* @__PURE__ */ h("div", {
53
53
  className: "border border-border-subtle rounded-lg p-6 space-y-4",
54
- children: [/* @__PURE__ */ p("div", { className: "h-6 w-24 bg-bg-sunken animate-pulse rounded" }), /* @__PURE__ */ p("div", { className: "h-10 w-full bg-bg-sunken animate-pulse rounded" })]
54
+ children: [/* @__PURE__ */ m("div", { className: "h-6 w-24 bg-bg-sunken animate-pulse rounded" }), /* @__PURE__ */ m("div", { className: "h-10 w-full bg-bg-sunken animate-pulse rounded" })]
55
55
  })]
56
56
  })]
57
- }) : w || !S ? /* @__PURE__ */ p("div", {
57
+ }) : w || !S ? /* @__PURE__ */ m("div", {
58
58
  className: "flex items-center justify-center h-full min-h-[400px]",
59
- children: /* @__PURE__ */ m("div", {
59
+ children: /* @__PURE__ */ h("div", {
60
60
  className: "text-center space-y-4",
61
61
  children: [
62
- /* @__PURE__ */ p("div", {
62
+ /* @__PURE__ */ m("div", {
63
63
  className: "size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center",
64
- children: /* @__PURE__ */ p("svg", {
64
+ children: /* @__PURE__ */ m("svg", {
65
65
  className: "size-6 text-status-error-text",
66
66
  fill: "none",
67
67
  viewBox: "0 0 24 24",
68
68
  stroke: "currentColor",
69
- children: /* @__PURE__ */ p("path", {
69
+ children: /* @__PURE__ */ m("path", {
70
70
  strokeLinecap: "round",
71
71
  strokeLinejoin: "round",
72
72
  strokeWidth: 2,
@@ -74,15 +74,15 @@ var y = () => {
74
74
  })
75
75
  })
76
76
  }),
77
- /* @__PURE__ */ p("h2", {
77
+ /* @__PURE__ */ m("h2", {
78
78
  className: "text-lg font-semibold text-text-primary",
79
79
  children: a("billing.plans.planNotFound", "Plan not found")
80
80
  }),
81
- /* @__PURE__ */ p("p", {
81
+ /* @__PURE__ */ m("p", {
82
82
  className: "text-sm text-text-muted",
83
83
  children: w?.message || a("billing.plans.planNotFoundMessage", "The plan you are looking for does not exist.")
84
84
  }),
85
- /* @__PURE__ */ p("button", {
85
+ /* @__PURE__ */ m("button", {
86
86
  type: "button",
87
87
  onClick: () => b("/plans"),
88
88
  className: "px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors",
@@ -90,61 +90,61 @@ var y = () => {
90
90
  })
91
91
  ]
92
92
  })
93
- }) : /* @__PURE__ */ m("div", {
93
+ }) : /* @__PURE__ */ h("div", {
94
94
  className: "space-y-6 p-6",
95
- children: [/* @__PURE__ */ m("div", {
95
+ children: [/* @__PURE__ */ h("div", {
96
96
  className: "flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between",
97
- children: [/* @__PURE__ */ m("div", {
97
+ children: [/* @__PURE__ */ h("div", {
98
98
  className: "flex items-start gap-4",
99
- children: [/* @__PURE__ */ p("button", {
99
+ children: [/* @__PURE__ */ m("button", {
100
100
  type: "button",
101
101
  onClick: () => b("/plans"),
102
102
  className: "p-2 -ml-2 text-text-muted hover:text-text-primary hover:bg-bg-sunken rounded-md transition-colors",
103
- children: /* @__PURE__ */ p("svg", {
103
+ children: /* @__PURE__ */ m("svg", {
104
104
  className: "size-5",
105
105
  fill: "none",
106
106
  viewBox: "0 0 24 24",
107
107
  stroke: "currentColor",
108
- children: /* @__PURE__ */ p("path", {
108
+ children: /* @__PURE__ */ m("path", {
109
109
  strokeLinecap: "round",
110
110
  strokeLinejoin: "round",
111
111
  strokeWidth: 2,
112
112
  d: "M15 19l-7-7 7-7"
113
113
  })
114
114
  })
115
- }), /* @__PURE__ */ m("div", { children: [
116
- /* @__PURE__ */ m("div", {
115
+ }), /* @__PURE__ */ h("div", { children: [
116
+ /* @__PURE__ */ h("div", {
117
117
  className: "flex items-center gap-3",
118
- children: [/* @__PURE__ */ p("h1", {
118
+ children: [/* @__PURE__ */ m("h1", {
119
119
  className: "text-2xl md:text-3xl font-semibold tracking-tight text-text-primary",
120
120
  children: S.name
121
- }), /* @__PURE__ */ p("span", {
121
+ }), /* @__PURE__ */ m("span", {
122
122
  className: `px-2 py-1 text-xs font-medium rounded ${S.isActive ? "bg-status-success-bg-subtle text-status-success-text" : "bg-bg-sunken text-text-muted"}`,
123
123
  children: S.isActive ? "Active" : "Inactive"
124
124
  })]
125
125
  }),
126
- /* @__PURE__ */ m("p", {
126
+ /* @__PURE__ */ h("p", {
127
127
  className: "text-sm text-text-muted mt-1",
128
128
  children: [n(S.duration), " billing plan"]
129
129
  }),
130
- /* @__PURE__ */ p(_, {
130
+ /* @__PURE__ */ m(v, {
131
131
  className: "mt-3",
132
132
  children: a("billing.plans.detailPurpose", "A full breakdown of this plan — its price, billing period and every quota and feature it grants. Use it to decide whether the plan fits before subscribing, or (as an admin) to edit or activate it.")
133
133
  })
134
134
  ] })]
135
- }), /* @__PURE__ */ m("div", {
135
+ }), /* @__PURE__ */ h("div", {
136
136
  className: "flex items-center gap-2",
137
- children: [x.canCreateSubscription && S.isActive && /* @__PURE__ */ p("button", {
137
+ children: [x.canCreateSubscription && S.isActive && /* @__PURE__ */ m("button", {
138
138
  type: "button",
139
139
  onClick: () => b(`/checkout/${S.id}`),
140
140
  className: "px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors",
141
141
  children: "Subscribe to Plan"
142
- }), x.canManagePlans && /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p("button", {
142
+ }), x.canManagePlans && /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m("button", {
143
143
  type: "button",
144
144
  onClick: () => b(`/plans/${S.id}/edit`),
145
145
  className: "px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors",
146
146
  children: "Edit Plan"
147
- }), /* @__PURE__ */ p("button", {
147
+ }), /* @__PURE__ */ m("button", {
148
148
  type: "button",
149
149
  onClick: async () => {
150
150
  if (x.canManagePlans) try {
@@ -158,28 +158,28 @@ var y = () => {
158
158
  children: D ? a("billing.plans.processing", "Processing...") : S.isActive ? a("billing.plans.deactivate", "Deactivate") : a("billing.plans.activate", "Activate")
159
159
  })] })]
160
160
  })]
161
- }), /* @__PURE__ */ m("div", {
161
+ }), /* @__PURE__ */ h("div", {
162
162
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
163
- children: [/* @__PURE__ */ m("div", {
163
+ children: [/* @__PURE__ */ h("div", {
164
164
  className: "lg:col-span-2 space-y-6",
165
- children: [/* @__PURE__ */ m(g, {
165
+ children: [/* @__PURE__ */ h(_, {
166
166
  className: "p-6",
167
167
  children: [
168
- /* @__PURE__ */ p("h2", {
168
+ /* @__PURE__ */ m("h2", {
169
169
  className: "text-lg font-semibold text-text-primary mb-4",
170
170
  children: a("billing.plans.pricing", "Pricing")
171
171
  }),
172
- /* @__PURE__ */ m("div", {
172
+ /* @__PURE__ */ h("div", {
173
173
  className: "flex items-baseline gap-1",
174
- children: [/* @__PURE__ */ p("span", {
174
+ children: [/* @__PURE__ */ m("span", {
175
175
  className: "text-4xl font-bold text-text-primary",
176
176
  children: e(S.price, S.currency)
177
- }), /* @__PURE__ */ m("span", {
177
+ }), /* @__PURE__ */ h("span", {
178
178
  className: "text-lg text-text-muted",
179
179
  children: ["/ ", t(S.duration)]
180
180
  })]
181
181
  }),
182
- /* @__PURE__ */ m("p", {
182
+ /* @__PURE__ */ h("p", {
183
183
  className: "text-sm text-text-muted mt-2",
184
184
  children: [
185
185
  a("billing.plans.billed", "Billed"),
@@ -188,11 +188,11 @@ var y = () => {
188
188
  ]
189
189
  })
190
190
  ]
191
- }), /* @__PURE__ */ m("div", {
191
+ }), /* @__PURE__ */ h("div", {
192
192
  className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
193
- children: [/* @__PURE__ */ p("div", {
193
+ children: [/* @__PURE__ */ m("div", {
194
194
  className: "p-6 border-b border-border-subtle bg-bg-sunken/30",
195
- children: /* @__PURE__ */ m("h2", {
195
+ children: /* @__PURE__ */ h("h2", {
196
196
  className: "text-lg font-semibold text-text-primary",
197
197
  children: [
198
198
  a("billing.plans.featuresAndQuotas", "Features & Quotas"),
@@ -201,46 +201,46 @@ var y = () => {
201
201
  ")"
202
202
  ]
203
203
  })
204
- }), /* @__PURE__ */ p("div", {
204
+ }), /* @__PURE__ */ m("div", {
205
205
  className: "p-6",
206
- children: O.length === 0 ? /* @__PURE__ */ p("p", {
206
+ children: O.length === 0 ? /* @__PURE__ */ m("p", {
207
207
  className: "text-sm text-text-muted",
208
208
  children: a("billing.plans.noFeatures", "No features configured for this plan.")
209
- }) : /* @__PURE__ */ p("ul", {
209
+ }) : /* @__PURE__ */ m("ul", {
210
210
  className: "space-y-3",
211
- children: O.map((e) => /* @__PURE__ */ m("li", {
211
+ children: O.map((e) => /* @__PURE__ */ h("li", {
212
212
  className: "flex items-start gap-3 p-3 rounded-lg bg-bg-sunken/30",
213
- children: [/* @__PURE__ */ p("div", {
213
+ children: [/* @__PURE__ */ m("div", {
214
214
  className: "size-8 rounded-full bg-status-success-bg-subtle flex items-center justify-center shrink-0",
215
- children: /* @__PURE__ */ p("svg", {
215
+ children: /* @__PURE__ */ m("svg", {
216
216
  className: "size-4 text-status-success-text",
217
217
  fill: "none",
218
218
  viewBox: "0 0 24 24",
219
219
  stroke: "currentColor",
220
- children: /* @__PURE__ */ p("path", {
220
+ children: /* @__PURE__ */ m("path", {
221
221
  strokeLinecap: "round",
222
222
  strokeLinejoin: "round",
223
223
  strokeWidth: 2,
224
224
  d: "M5 13l4 4L19 7"
225
225
  })
226
226
  })
227
- }), /* @__PURE__ */ m("div", {
227
+ }), /* @__PURE__ */ h("div", {
228
228
  className: "flex-1",
229
- children: [/* @__PURE__ */ m("div", {
229
+ children: [/* @__PURE__ */ h("div", {
230
230
  className: "flex items-center justify-between",
231
- children: [/* @__PURE__ */ p("p", {
231
+ children: [/* @__PURE__ */ m("p", {
232
232
  className: "font-medium text-text-primary",
233
233
  children: e.name
234
- }), /* @__PURE__ */ p("span", {
234
+ }), /* @__PURE__ */ m("span", {
235
235
  className: "font-semibold text-text-primary",
236
236
  children: r(e.totalValue, e.unit)
237
237
  })]
238
- }), /* @__PURE__ */ m("div", {
238
+ }), /* @__PURE__ */ h("div", {
239
239
  className: "flex items-center gap-2 mt-1",
240
- children: [/* @__PURE__ */ m("span", {
240
+ children: [/* @__PURE__ */ h("span", {
241
241
  className: "text-sm text-text-muted capitalize",
242
242
  children: ["Type: ", e.limitType]
243
- }), e.quotaType && /* @__PURE__ */ p("span", {
243
+ }), e.quotaType && /* @__PURE__ */ m("span", {
244
244
  className: "px-2 py-0.5 text-xs font-medium bg-bg-sunken text-text-muted rounded",
245
245
  children: e.quotaType
246
246
  })]
@@ -250,40 +250,40 @@ var y = () => {
250
250
  })
251
251
  })]
252
252
  })]
253
- }), /* @__PURE__ */ m("div", {
253
+ }), /* @__PURE__ */ h("div", {
254
254
  className: "space-y-6",
255
- children: [/* @__PURE__ */ m("div", {
255
+ children: [/* @__PURE__ */ h("div", {
256
256
  className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
257
- children: [/* @__PURE__ */ p("div", {
257
+ children: [/* @__PURE__ */ m("div", {
258
258
  className: "p-6 border-b border-border-subtle bg-bg-sunken/30",
259
- children: /* @__PURE__ */ p("h2", {
259
+ children: /* @__PURE__ */ m("h2", {
260
260
  className: "text-lg font-semibold text-text-primary",
261
261
  children: a("billing.plans.planDetails", "Plan Details")
262
262
  })
263
- }), /* @__PURE__ */ m("div", {
263
+ }), /* @__PURE__ */ h("div", {
264
264
  className: "p-6 space-y-4",
265
265
  children: [
266
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
266
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
267
267
  className: "text-sm text-text-muted",
268
268
  children: a("billing.plans.planName", "Plan Name")
269
- }), /* @__PURE__ */ p("p", {
269
+ }), /* @__PURE__ */ m("p", {
270
270
  className: "text-sm font-medium text-text-primary mt-0.5",
271
271
  children: S.name
272
272
  })] }),
273
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
273
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
274
274
  className: "text-sm text-text-muted",
275
275
  children: a("billing.plans.status", "Status")
276
- }), /* @__PURE__ */ p("p", {
276
+ }), /* @__PURE__ */ m("p", {
277
277
  className: "text-sm text-text-primary mt-0.5",
278
- children: /* @__PURE__ */ p("span", {
278
+ children: /* @__PURE__ */ m("span", {
279
279
  className: `inline-flex items-center px-2 py-0.5 text-xs font-medium rounded ${S.isActive ? "bg-status-success-bg-subtle text-status-success-text" : "bg-bg-sunken text-text-muted"}`,
280
280
  children: S.isActive ? "Active" : "Inactive"
281
281
  })
282
282
  })] }),
283
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
283
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
284
284
  className: "text-sm text-text-muted",
285
285
  children: a("billing.plans.price", "Price")
286
- }), /* @__PURE__ */ m("p", {
286
+ }), /* @__PURE__ */ h("p", {
287
287
  className: "text-sm font-medium text-text-primary mt-0.5",
288
288
  children: [
289
289
  e(S.price, S.currency),
@@ -291,38 +291,38 @@ var y = () => {
291
291
  t(S.duration)
292
292
  ]
293
293
  })] }),
294
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
294
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
295
295
  className: "text-sm text-text-muted",
296
296
  children: a("billing.plans.currency", "Currency")
297
- }), /* @__PURE__ */ p("p", {
297
+ }), /* @__PURE__ */ m("p", {
298
298
  className: "text-sm text-text-primary mt-0.5",
299
299
  children: S.currency
300
300
  })] }),
301
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
301
+ /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
302
302
  className: "text-sm text-text-muted",
303
303
  children: a("billing.plans.billingPeriod", "Billing Period")
304
- }), /* @__PURE__ */ p("p", {
304
+ }), /* @__PURE__ */ m("p", {
305
305
  className: "text-sm text-text-primary mt-0.5",
306
306
  children: n(S.duration)
307
307
  })] })
308
308
  ]
309
309
  })]
310
- }), /* @__PURE__ */ m("div", {
310
+ }), /* @__PURE__ */ h("div", {
311
311
  className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
312
- children: [/* @__PURE__ */ p("div", {
312
+ children: [/* @__PURE__ */ m("div", {
313
313
  className: "p-6 border-b border-border-subtle bg-bg-sunken/30",
314
- children: /* @__PURE__ */ p("h2", {
314
+ children: /* @__PURE__ */ m("h2", {
315
315
  className: "text-lg font-semibold text-text-primary",
316
316
  children: a("billing.plans.statistics", "Statistics")
317
317
  })
318
- }), /* @__PURE__ */ p("div", {
318
+ }), /* @__PURE__ */ m("div", {
319
319
  className: "p-6 space-y-4",
320
- children: /* @__PURE__ */ m("div", {
320
+ children: /* @__PURE__ */ h("div", {
321
321
  className: "flex items-center justify-between",
322
- children: [/* @__PURE__ */ p("p", {
322
+ children: [/* @__PURE__ */ m("p", {
323
323
  className: "text-sm text-text-muted",
324
324
  children: a("billing.plans.uniqueQuotas", "Unique Quotas")
325
- }), /* @__PURE__ */ p("p", {
325
+ }), /* @__PURE__ */ m("p", {
326
326
  className: "font-semibold text-text-primary",
327
327
  children: O.length
328
328
  })]
@@ -331,19 +331,19 @@ var y = () => {
331
331
  })]
332
332
  })]
333
333
  })]
334
- }) : /* @__PURE__ */ p("div", {
334
+ }) : /* @__PURE__ */ m("div", {
335
335
  className: "flex items-center justify-center h-full min-h-[400px]",
336
- children: /* @__PURE__ */ m("div", {
336
+ children: /* @__PURE__ */ h("div", {
337
337
  className: "text-center space-y-2",
338
338
  children: [
339
- /* @__PURE__ */ p("div", {
339
+ /* @__PURE__ */ m("div", {
340
340
  className: "size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center",
341
- children: /* @__PURE__ */ p("svg", {
341
+ children: /* @__PURE__ */ m("svg", {
342
342
  className: "size-6 text-text-muted",
343
343
  fill: "none",
344
344
  viewBox: "0 0 24 24",
345
345
  stroke: "currentColor",
346
- children: /* @__PURE__ */ p("path", {
346
+ children: /* @__PURE__ */ m("path", {
347
347
  strokeLinecap: "round",
348
348
  strokeLinejoin: "round",
349
349
  strokeWidth: 2,
@@ -351,11 +351,11 @@ var y = () => {
351
351
  })
352
352
  })
353
353
  }),
354
- /* @__PURE__ */ p("h2", {
354
+ /* @__PURE__ */ m("h2", {
355
355
  className: "text-lg font-semibold text-text-primary",
356
356
  children: a("billing.accessDenied.title", "Access Denied")
357
357
  }),
358
- /* @__PURE__ */ p("p", {
358
+ /* @__PURE__ */ m("p", {
359
359
  className: "text-sm text-text-muted max-w-sm",
360
360
  children: a("billing.plans.noViewPlanPermission", "You don't have permission to view this plan.")
361
361
  })
@@ -364,6 +364,6 @@ var y = () => {
364
364
  });
365
365
  };
366
366
  //#endregion
367
- export { y as PlanDetailPage };
367
+ export { b as PlanDetailPage };
368
368
 
369
369
  //# sourceMappingURL=PlanDetailPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlanDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanDetailPage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Detail Page\n * Displays detailed information about a single plan\n */\n\nimport { type FC, useMemo } from 'react';\nimport { useParams } from 'react-router';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlan, usePlanMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n formatQuotaValue,\n} from '../../../shared/utils/format';\nimport {\n getPlanFeatureQuantity,\n getPlanFeatureUnit,\n} from '../../../shared/utils/planFeatureQuantity';\nimport type { PlanFeature } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PagePurpose, EmphasisPanel } from '@burdenoff/fe-libs/ui';\n\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n quotaType: string;\n limitType: string;\n totalValue: number;\n count: number;\n unit?: string;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n */\nfunction combineQuotas(features: PlanFeature[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n\n for (const feature of features) {\n if (!feature.quota) continue;\n\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\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n existing.count += 1;\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 count: 1,\n unit: getPlanFeatureUnit(feature),\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\nexport const PlanDetailPage: 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 permissions = useBillingPermissions();\n const { plan, isLoading, error, refetch } = usePlan(planId);\n const { togglePlan, isToggling } = usePlanMutations();\n\n // Combine duplicate quotas for cleaner display (must be before any returns)\n const combinedQuotas = useMemo(() => combineQuotas(plan?.features || []), [plan?.features]);\n\n // Permission check\n if (!permissions.canViewPlans) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-muted\"\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=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.accessDenied.title', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-muted max-w-sm\">\n {tr(\n 'billing.plans.noViewPlanPermission',\n \"You don't have permission to view this plan.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\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-bg-sunken animate-pulse rounded\" />\n <div className=\"h-8 w-48 bg-bg-sunken 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-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-3/4 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-10 w-full bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || !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 <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.planNotFound', 'Plan not found')}\n </h2>\n <p className=\"text-sm text-text-muted\">\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-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Back to Plans\n </button>\n </div>\n </div>\n );\n }\n\n const handleToggle = async () => {\n if (!permissions.canManagePlans) return;\n try {\n await togglePlan(plan.id, !plan.isActive);\n await refetch();\n } catch (err) {\n console.error('Failed to toggle plan:', err);\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-text-muted hover:text-text-primary hover:bg-bg-sunken 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 <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {plan.name}\n </h1>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${\n plan.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {plan.isActive ? 'Active' : 'Inactive'}\n </span>\n </div>\n <p className=\"text-sm text-text-muted mt-1\">\n {formatPlanDurationLabel(plan.duration)} billing plan\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.plans.detailPurpose',\n 'A full breakdown of this plan — its price, billing period and every quota and feature it grants. Use it to decide whether the plan fits before subscribing, or (as an admin) to edit or activate it.'\n )}\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {permissions.canCreateSubscription && plan.isActive && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/checkout/${plan.id}`)}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Subscribe to Plan\n </button>\n )}\n {permissions.canManagePlans && (\n <>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/plans/${plan.id}/edit`)}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Edit Plan\n </button>\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={isToggling}\n className={`px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors ${\n plan.isActive\n ? 'text-status-error-text hover:bg-status-error-bg-subtle'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {isToggling\n ? tr('billing.plans.processing', 'Processing...')\n : plan.isActive\n ? tr('billing.plans.deactivate', 'Deactivate')\n : tr('billing.plans.activate', 'Activate')}\n </button>\n </>\n )}\n </div>\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 — primary emphasis zone */}\n <EmphasisPanel className=\"p-6\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('billing.plans.pricing', 'Pricing')}\n </h2>\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold text-text-primary\">\n {formatCurrency(plan.price, plan.currency)}\n </span>\n <span className=\"text-lg text-text-muted\">/ {formatPlanDuration(plan.duration)}</span>\n </div>\n <p className=\"text-sm text-text-muted 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 </EmphasisPanel>\n\n {/* Features Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\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-text-muted\">\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-bg-sunken/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-text-primary\">{quota.name}</p>\n <span className=\"font-semibold text-text-primary\">\n {formatQuotaValue(quota.totalValue, quota.unit)}\n </span>\n </div>\n <div className=\"flex items-center gap-2 mt-1\">\n <span className=\"text-sm text-text-muted capitalize\">\n Type: {quota.limitType}\n </span>\n {quota.quotaType && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-bg-sunken text-text-muted 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 {/* Plan Details Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.planDetails', 'Plan Details')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.planName', 'Plan Name')}\n </p>\n <p className=\"text-sm font-medium text-text-primary mt-0.5\">{plan.name}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">{tr('billing.plans.status', 'Status')}</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n <span\n className={`inline-flex items-center px-2 py-0.5 text-xs font-medium rounded ${\n plan.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {plan.isActive ? 'Active' : 'Inactive'}\n </span>\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">{tr('billing.plans.price', 'Price')}</p>\n <p className=\"text-sm font-medium text-text-primary mt-0.5\">\n {formatCurrency(plan.price, plan.currency)} / {formatPlanDuration(plan.duration)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.currency', 'Currency')}\n </p>\n <p className=\"text-sm text-text-primary mt-0.5\">{plan.currency}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.billingPeriod', 'Billing Period')}\n </p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatPlanDurationLabel(plan.duration)}\n </p>\n </div>\n </div>\n </div>\n\n {/* Quick Stats Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.statistics', 'Statistics')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.uniqueQuotas', 'Unique Quotas')}\n </p>\n <p className=\"font-semibold text-text-primary\">{combinedQuotas.length}</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PlanDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanDetailPage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Detail Page\n * Displays detailed information about a single plan\n */\n\nimport { type FC, useMemo } from 'react';\nimport { useParams } from 'react-router';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlan, usePlanMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n formatQuotaValue,\n} from '../../../shared/utils/format';\nimport {\n getPlanFeatureQuantity,\n getPlanFeatureUnit,\n getQuotaDisplayName,\n} from '../../../shared/utils/planFeatureQuantity';\nimport type { PlanFeature } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PagePurpose, EmphasisPanel } from '@burdenoff/fe-libs/ui';\n\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n quotaType: string;\n limitType: string;\n totalValue: number;\n count: number;\n unit?: string;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n */\nfunction combineQuotas(features: PlanFeature[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n\n for (const feature of features) {\n if (!feature.quota) continue;\n\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\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n existing.count += 1;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: getQuotaDisplayName(feature),\n quotaType: feature.quota.quotaType ?? '',\n limitType,\n totalValue: limitValue,\n count: 1,\n unit: getPlanFeatureUnit(feature),\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\nexport const PlanDetailPage: 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 permissions = useBillingPermissions();\n const { plan, isLoading, error, refetch } = usePlan(planId);\n const { togglePlan, isToggling } = usePlanMutations();\n\n // Combine duplicate quotas for cleaner display (must be before any returns)\n const combinedQuotas = useMemo(() => combineQuotas(plan?.features || []), [plan?.features]);\n\n // Permission check\n if (!permissions.canViewPlans) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-muted\"\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=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.accessDenied.title', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-muted max-w-sm\">\n {tr(\n 'billing.plans.noViewPlanPermission',\n \"You don't have permission to view this plan.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\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-bg-sunken animate-pulse rounded\" />\n <div className=\"h-8 w-48 bg-bg-sunken 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-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-3/4 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-10 w-full bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || !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 <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-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=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.planNotFound', 'Plan not found')}\n </h2>\n <p className=\"text-sm text-text-muted\">\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-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Back to Plans\n </button>\n </div>\n </div>\n );\n }\n\n const handleToggle = async () => {\n if (!permissions.canManagePlans) return;\n try {\n await togglePlan(plan.id, !plan.isActive);\n await refetch();\n } catch (err) {\n console.error('Failed to toggle plan:', err);\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-text-muted hover:text-text-primary hover:bg-bg-sunken 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 <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {plan.name}\n </h1>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${\n plan.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {plan.isActive ? 'Active' : 'Inactive'}\n </span>\n </div>\n <p className=\"text-sm text-text-muted mt-1\">\n {formatPlanDurationLabel(plan.duration)} billing plan\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.plans.detailPurpose',\n 'A full breakdown of this plan — its price, billing period and every quota and feature it grants. Use it to decide whether the plan fits before subscribing, or (as an admin) to edit or activate it.'\n )}\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {permissions.canCreateSubscription && plan.isActive && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/checkout/${plan.id}`)}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Subscribe to Plan\n </button>\n )}\n {permissions.canManagePlans && (\n <>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/plans/${plan.id}/edit`)}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Edit Plan\n </button>\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={isToggling}\n className={`px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors ${\n plan.isActive\n ? 'text-status-error-text hover:bg-status-error-bg-subtle'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {isToggling\n ? tr('billing.plans.processing', 'Processing...')\n : plan.isActive\n ? tr('billing.plans.deactivate', 'Deactivate')\n : tr('billing.plans.activate', 'Activate')}\n </button>\n </>\n )}\n </div>\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 — primary emphasis zone */}\n <EmphasisPanel className=\"p-6\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('billing.plans.pricing', 'Pricing')}\n </h2>\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold text-text-primary\">\n {formatCurrency(plan.price, plan.currency)}\n </span>\n <span className=\"text-lg text-text-muted\">/ {formatPlanDuration(plan.duration)}</span>\n </div>\n <p className=\"text-sm text-text-muted 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 </EmphasisPanel>\n\n {/* Features Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\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-text-muted\">\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-bg-sunken/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-text-primary\">{quota.name}</p>\n <span className=\"font-semibold text-text-primary\">\n {formatQuotaValue(quota.totalValue, quota.unit)}\n </span>\n </div>\n <div className=\"flex items-center gap-2 mt-1\">\n <span className=\"text-sm text-text-muted capitalize\">\n Type: {quota.limitType}\n </span>\n {quota.quotaType && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-bg-sunken text-text-muted 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 {/* Plan Details Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.planDetails', 'Plan Details')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.planName', 'Plan Name')}\n </p>\n <p className=\"text-sm font-medium text-text-primary mt-0.5\">{plan.name}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">{tr('billing.plans.status', 'Status')}</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n <span\n className={`inline-flex items-center px-2 py-0.5 text-xs font-medium rounded ${\n plan.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {plan.isActive ? 'Active' : 'Inactive'}\n </span>\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">{tr('billing.plans.price', 'Price')}</p>\n <p className=\"text-sm font-medium text-text-primary mt-0.5\">\n {formatCurrency(plan.price, plan.currency)} / {formatPlanDuration(plan.duration)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.currency', 'Currency')}\n </p>\n <p className=\"text-sm text-text-primary mt-0.5\">{plan.currency}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.billingPeriod', 'Billing Period')}\n </p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatPlanDurationLabel(plan.duration)}\n </p>\n </div>\n </div>\n </div>\n\n {/* Quick Stats Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.statistics', 'Statistics')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.uniqueQuotas', 'Unique Quotas')}\n </p>\n <p className=\"font-semibold text-text-primary\">{combinedQuotas.length}</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -9,7 +9,27 @@ function t(e) {
9
9
  let t = e.context;
10
10
  return typeof t?.unit == "string" ? t.unit : void 0;
11
11
  }
12
+ var n = new Set([
13
+ "count",
14
+ "bytes",
15
+ "days",
16
+ "seconds",
17
+ "hours",
18
+ "monthly",
19
+ "daily",
20
+ "concurrent"
21
+ ]);
22
+ function r(e) {
23
+ let t = e.split(".").filter(Boolean), r = t.length > 1 ? t.slice(1) : t, i = r[r.length - 1], a = (r.length > 1 && i && n.has(i) ? r.slice(0, -1) : r).join(" ").replace(/_/g, " ").trim().split(/\s+/).filter(Boolean);
24
+ return a.length === 0 ? e : a.map((e) => e.charAt(0).toUpperCase() + e.slice(1)).join(" ");
25
+ }
26
+ function i(e) {
27
+ let t = e.quota?.context;
28
+ if (t && typeof t.displayName == "string" && t.displayName) return t.displayName;
29
+ let n = e.quota?.name;
30
+ return n ? r(n) : "Unknown Quota";
31
+ }
12
32
  //#endregion
13
- export { e as getPlanFeatureQuantity, t as getPlanFeatureUnit };
33
+ export { e as getPlanFeatureQuantity, t as getPlanFeatureUnit, i as getQuotaDisplayName, r as humanizeQuotaName };
14
34
 
15
35
  //# sourceMappingURL=planFeatureQuantity.js.map