@burdenoff/microfe-billing 2026.530.2 → 2026.530.3

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 (28) hide show
  1. package/dist/billing/BillingAdminRoutes.js +84 -71
  2. package/dist/billing/BillingAdminRoutes.js.map +1 -1
  3. package/dist/billing/BillingUserRoutes.js +103 -90
  4. package/dist/billing/BillingUserRoutes.js.map +1 -1
  5. package/dist/billing/modules/billing/index.js +2 -1
  6. package/dist/billing/modules/billing/pages/InvoicesListPage.js +135 -126
  7. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  8. package/dist/billing/modules/billing/pages/UserRefundsPage.js +417 -0
  9. package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -0
  10. package/dist/billing/modules/billing/pages/index.js +1 -0
  11. package/dist/billing/modules/plans/constants/featuredQuotas.js +37 -0
  12. package/dist/billing/modules/plans/constants/featuredQuotas.js.map +1 -0
  13. package/dist/billing/modules/plans/index.js +2 -1
  14. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +205 -0
  15. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -0
  16. package/dist/billing/modules/plans/pages/PlanCreatePage.js +51 -33
  17. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  18. package/dist/billing/modules/plans/pages/PlanEditPage.js +78 -60
  19. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  20. package/dist/billing/modules/plans/pages/PlansBrowsePage.js +246 -126
  21. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  22. package/dist/billing/modules/plans/pages/index.js +1 -0
  23. package/dist/billing/shared/types/graphql.js.map +1 -1
  24. package/dist/generated/global-operations.js +317 -287
  25. package/dist/generated/global-operations.js.map +1 -1
  26. package/dist/generated/global-types.js +3 -1
  27. package/dist/generated/global-types.js.map +1 -1
  28. package/package.json +1 -1
@@ -0,0 +1,37 @@
1
+ //#region src/billing/modules/plans/constants/featuredQuotas.ts
2
+ var e = [
3
+ "vibecontrols.agents.count",
4
+ "vibecontrols.sessions.active",
5
+ "vibecontrols.vibes.count",
6
+ "vibecontrols.sandbox.runtime_hours",
7
+ "vibecontrols.tunnels.custom_domains",
8
+ "vibecontrols.audit.retention_days",
9
+ "vibecontrols.actions.count",
10
+ "vibecontrols.vibedeck.boards",
11
+ "vibecontrols.scheduler.executions",
12
+ "vibecontrols.security.vaults",
13
+ "vibecontrols.catalogs.count",
14
+ "vibecontrols.docs.compilations",
15
+ "vibecontrols.organizations.count"
16
+ ], t = {
17
+ "vibecontrols.agents.count": "Agents",
18
+ "vibecontrols.sessions.active": "Concurrent Sessions",
19
+ "vibecontrols.vibes.count": "Vibes",
20
+ "vibecontrols.sandbox.runtime_hours": "Sandbox Minutes",
21
+ "vibecontrols.tunnels.custom_domains": "Custom Domains (VibeTunnels)",
22
+ "vibecontrols.audit.retention_days": "Audit Log Retention",
23
+ "vibecontrols.actions.count": "Actions",
24
+ "vibecontrols.vibedeck.boards": "Vibe Decks",
25
+ "vibecontrols.scheduler.executions": "Scheduled Task Executions",
26
+ "vibecontrols.security.vaults": "Vaults",
27
+ "vibecontrols.catalogs.count": "Catalogs",
28
+ "vibecontrols.docs.compilations": "Doc Builds",
29
+ "vibecontrols.organizations.count": "Organizations & Workspaces"
30
+ };
31
+ function n(e, t) {
32
+ return e === "vibecontrols.sandbox.runtime_hours" ? `${(t * 60).toLocaleString()} min` : e === "vibecontrols.audit.retention_days" ? `${t.toLocaleString()} days` : t.toLocaleString();
33
+ }
34
+ //#endregion
35
+ export { t as FEATURED_QUOTA_LABELS, e as FEATURED_QUOTA_NAMES, n as formatFeaturedQuotaValue };
36
+
37
+ //# sourceMappingURL=featuredQuotas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featuredQuotas.js","names":[],"sources":["../../../../../src/billing/modules/plans/constants/featuredQuotas.ts"],"sourcesContent":["/**\n * The subset of quotas shown prominently on plan cards.\n * Order here determines display order. All other quotas are shown only on\n * the plan detail page.\n */\nexport const FEATURED_QUOTA_NAMES = [\n 'vibecontrols.agents.count',\n 'vibecontrols.sessions.active',\n 'vibecontrols.vibes.count',\n 'vibecontrols.sandbox.runtime_hours',\n 'vibecontrols.tunnels.custom_domains',\n 'vibecontrols.audit.retention_days',\n 'vibecontrols.actions.count',\n 'vibecontrols.vibedeck.boards',\n 'vibecontrols.scheduler.executions',\n 'vibecontrols.security.vaults',\n 'vibecontrols.catalogs.count',\n 'vibecontrols.docs.compilations',\n 'vibecontrols.organizations.count',\n] as const;\n\nexport const FEATURED_QUOTA_LABELS: Record<string, string> = {\n 'vibecontrols.agents.count': 'Agents',\n 'vibecontrols.sessions.active': 'Concurrent Sessions',\n 'vibecontrols.vibes.count': 'Vibes',\n 'vibecontrols.sandbox.runtime_hours': 'Sandbox Minutes',\n 'vibecontrols.tunnels.custom_domains': 'Custom Domains (VibeTunnels)',\n 'vibecontrols.audit.retention_days': 'Audit Log Retention',\n 'vibecontrols.actions.count': 'Actions',\n 'vibecontrols.vibedeck.boards': 'Vibe Decks',\n 'vibecontrols.scheduler.executions': 'Scheduled Task Executions',\n 'vibecontrols.security.vaults': 'Vaults',\n 'vibecontrols.catalogs.count': 'Catalogs',\n 'vibecontrols.docs.compilations': 'Doc Builds',\n 'vibecontrols.organizations.count': 'Organizations & Workspaces',\n};\n\n/** Format a featured quota value for display (e.g. hours → minutes, days suffix). */\nexport function formatFeaturedQuotaValue(name: string, value: number): string {\n if (name === 'vibecontrols.sandbox.runtime_hours') {\n const minutes = value * 60;\n return `${minutes.toLocaleString()} min`;\n }\n if (name === 'vibecontrols.audit.retention_days') {\n return `${value.toLocaleString()} days`;\n }\n return value.toLocaleString();\n}\n"],"mappings":";AAKA,IAAa,IAAuB;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,EAEY,IAAgD;CAC3D,6BAA6B;CAC7B,gCAAgC;CAChC,4BAA4B;CAC5B,sCAAsC;CACtC,uCAAuC;CACvC,qCAAqC;CACrC,8BAA8B;CAC9B,gCAAgC;CAChC,qCAAqC;CACrC,gCAAgC;CAChC,+BAA+B;CAC/B,kCAAkC;CAClC,oCAAoC;CACrC;AAGD,SAAgB,EAAyB,GAAc,GAAuB;AAQ5E,QAPI,MAAS,uCAEJ,IADS,IAAQ,IACN,gBAAgB,CAAC,QAEjC,MAAS,sCACJ,GAAG,EAAM,gBAAgB,CAAC,SAE5B,EAAM,gBAAgB"}
@@ -7,5 +7,6 @@ import { PlanEditPage as n } from "./pages/PlanEditPage.js";
7
7
  import { PlanCreatePage as r } from "./pages/PlanCreatePage.js";
8
8
  import { PlansBrowsePage as i } from "./pages/PlansBrowsePage.js";
9
9
  import { PlanBrowseDetailPage as a } from "./pages/PlanBrowseDetailPage.js";
10
+ import { PlanAssignFreePage as o } from "./pages/PlanAssignFreePage.js";
10
11
  import "./pages/index.js";
11
- export { a as PlanBrowseDetailPage, r as PlanCreatePage, t as PlanDetailPage, n as PlanEditPage, i as PlansBrowsePage, e as PlansListPage };
12
+ export { o as PlanAssignFreePage, a as PlanBrowseDetailPage, r as PlanCreatePage, t as PlanDetailPage, n as PlanEditPage, i as PlansBrowsePage, e as PlansListPage };
@@ -0,0 +1,205 @@
1
+ import { AccessDenied as e } from "../../../shared/components/AccessDenied.js";
2
+ import { PageHeader as t } from "../../../shared/components/PageHeader.js";
3
+ import "../../../shared/components/index.js";
4
+ import { useBillingPermissions as n } from "../../../hooks/useBillingPermissions.js";
5
+ import { useAssignPlanFreeMutation as r } from "../../../../generated/global-operations.js";
6
+ import { useBillingNavigate as i } from "../../../hooks/useBillingNavigate.js";
7
+ import { usePlans as a } from "../hooks/usePlans.js";
8
+ import "../hooks/index.js";
9
+ import { useState as o } from "react";
10
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
11
+ import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
+ //#region src/billing/modules/plans/pages/PlanAssignFreePage.tsx
13
+ var u = () => {
14
+ let { t: u } = l(), d = (e, t) => {
15
+ let n = u(e);
16
+ return n === e ? t : n;
17
+ }, f = i(), p = n(), [m, h] = o(""), [g, _] = o(""), [v, y] = o(""), [b, x] = o(null), { plans: S, isLoading: C } = a({ includeFree: !0 }), [w, { loading: T }] = r();
18
+ if (!p.canManagePlans) return /* @__PURE__ */ s(e, { message: d("billing.assignFree.noPermission", "You don't have permission to access this page.") });
19
+ let E = async (e) => {
20
+ if (e.preventDefault(), x(null), !m.trim() || !g.trim() || !v) {
21
+ x({
22
+ success: !1,
23
+ message: "All fields are required."
24
+ });
25
+ return;
26
+ }
27
+ try {
28
+ let { data: e } = await w({ variables: {
29
+ secret: m.trim(),
30
+ billingAccountId: g.trim(),
31
+ planId: v
32
+ } });
33
+ if (e?.assignPlanFree) {
34
+ let t = e.assignPlanFree;
35
+ x({
36
+ success: !0,
37
+ message: `Plan assigned. Subscription ID: ${t.id} · Status: ${t.status} · Valid: ${new Date(t.startDate).toLocaleDateString()} → ${new Date(t.endDate).toLocaleDateString()}`
38
+ }), h(""), _(""), y("");
39
+ }
40
+ } catch (e) {
41
+ x({
42
+ success: !1,
43
+ message: e instanceof Error ? e.message : "Failed to assign plan."
44
+ });
45
+ }
46
+ }, D = S.find((e) => e.id === v);
47
+ return /* @__PURE__ */ c("div", {
48
+ className: "space-y-6 p-6 max-w-2xl",
49
+ children: [
50
+ /* @__PURE__ */ s(t, {
51
+ title: d("billing.assignFree.title", "Assign Plan for Free"),
52
+ description: d("billing.assignFree.description", "Internally assign any plan to a billing account at no charge. Requires the team secret."),
53
+ actions: /* @__PURE__ */ s("button", {
54
+ type: "button",
55
+ onClick: () => f("/plans"),
56
+ className: "px-3 py-2 text-sm border border-border rounded-md text-foreground hover:bg-muted transition-colors",
57
+ children: "← Back to Plans"
58
+ })
59
+ }),
60
+ /* @__PURE__ */ c("div", {
61
+ className: "flex items-start gap-3 p-4 rounded-lg border border-status-warning-border bg-status-warning-bg/10",
62
+ children: [/* @__PURE__ */ s("svg", {
63
+ className: "size-5 text-status-warning-text shrink-0 mt-0.5",
64
+ fill: "none",
65
+ viewBox: "0 0 24 24",
66
+ stroke: "currentColor",
67
+ children: /* @__PURE__ */ s("path", {
68
+ strokeLinecap: "round",
69
+ strokeLinejoin: "round",
70
+ strokeWidth: 2,
71
+ d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.962-.833-2.732 0L3.072 16.5c-.77.833.192 2.5 1.732 2.5z"
72
+ })
73
+ }), /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("p", {
74
+ className: "text-sm font-medium text-status-warning-text",
75
+ children: "Internal admin tool"
76
+ }), /* @__PURE__ */ s("p", {
77
+ className: "text-xs text-status-warning-text/80 mt-0.5",
78
+ children: "This bypasses all payment flows. The plan is assigned immediately with no charge. Use only for enterprise deals or internal accounts."
79
+ })] })]
80
+ }),
81
+ /* @__PURE__ */ c("form", {
82
+ onSubmit: E,
83
+ className: "border border-border rounded-lg bg-card overflow-hidden",
84
+ children: [
85
+ /* @__PURE__ */ s("div", {
86
+ className: "p-4 border-b border-border bg-muted/30",
87
+ children: /* @__PURE__ */ s("h2", {
88
+ className: "font-semibold text-foreground",
89
+ children: "Assignment Details"
90
+ })
91
+ }),
92
+ /* @__PURE__ */ c("div", {
93
+ className: "p-4 space-y-4",
94
+ children: [
95
+ /* @__PURE__ */ c("div", { children: [
96
+ /* @__PURE__ */ c("label", {
97
+ htmlFor: "secret",
98
+ className: "block text-sm font-medium text-foreground mb-1",
99
+ children: ["Team Secret ", /* @__PURE__ */ s("span", {
100
+ className: "text-status-error-text",
101
+ children: "*"
102
+ })]
103
+ }),
104
+ /* @__PURE__ */ s("input", {
105
+ id: "secret",
106
+ type: "password",
107
+ value: m,
108
+ onChange: (e) => h(e.target.value),
109
+ autoComplete: "off",
110
+ className: "w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary font-mono",
111
+ placeholder: "Enter PLAN_ASSIGN_SECRET",
112
+ required: !0
113
+ }),
114
+ /* @__PURE__ */ s("p", {
115
+ className: "text-xs text-muted-foreground mt-1",
116
+ children: "The secret key shared within the team. Never expose this to users."
117
+ })
118
+ ] }),
119
+ /* @__PURE__ */ c("div", { children: [
120
+ /* @__PURE__ */ c("label", {
121
+ htmlFor: "billingAccountId",
122
+ className: "block text-sm font-medium text-foreground mb-1",
123
+ children: ["Billing Account ID ", /* @__PURE__ */ s("span", {
124
+ className: "text-status-error-text",
125
+ children: "*"
126
+ })]
127
+ }),
128
+ /* @__PURE__ */ s("input", {
129
+ id: "billingAccountId",
130
+ type: "text",
131
+ value: g,
132
+ onChange: (e) => _(e.target.value),
133
+ className: "w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary font-mono text-sm",
134
+ placeholder: "e.g. ba_01jk3n2...",
135
+ required: !0
136
+ }),
137
+ /* @__PURE__ */ s("p", {
138
+ className: "text-xs text-muted-foreground mt-1",
139
+ children: "Find this in the user's billing account settings or via the billing accounts list."
140
+ })
141
+ ] }),
142
+ /* @__PURE__ */ c("div", { children: [
143
+ /* @__PURE__ */ c("label", {
144
+ htmlFor: "planId",
145
+ className: "block text-sm font-medium text-foreground mb-1",
146
+ children: ["Plan ", /* @__PURE__ */ s("span", {
147
+ className: "text-status-error-text",
148
+ children: "*"
149
+ })]
150
+ }),
151
+ C ? /* @__PURE__ */ s("div", { className: "h-10 bg-muted animate-pulse rounded-md" }) : /* @__PURE__ */ c("select", {
152
+ id: "planId",
153
+ value: v,
154
+ onChange: (e) => y(e.target.value),
155
+ className: "w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary",
156
+ required: !0,
157
+ children: [/* @__PURE__ */ s("option", {
158
+ value: "",
159
+ disabled: !0,
160
+ children: "Select a plan…"
161
+ }), S.map((e) => /* @__PURE__ */ c("option", {
162
+ value: e.id,
163
+ children: [
164
+ e.name,
165
+ e.isPurchasable ? "" : " (internal)",
166
+ e.price != null && e.price > 0 ? ` — ${e.currency ?? "USD"} ${e.price}` : ""
167
+ ]
168
+ }, e.id))]
169
+ }),
170
+ D && /* @__PURE__ */ c("p", {
171
+ className: "text-xs text-muted-foreground mt-1",
172
+ children: [D.isPurchasable ? "Public plan" : "Internal / non-purchasable plan", D.productID ? ` · Product: ${D.productID}` : ""]
173
+ })
174
+ ] })
175
+ ]
176
+ }),
177
+ b && /* @__PURE__ */ s("div", {
178
+ className: `mx-4 mb-4 p-3 rounded-lg border text-sm ${b.success ? "border-status-success-border bg-status-success-bg/10 text-status-success-text" : "border-status-error-border bg-status-error-bg/10 text-status-error-text"}`,
179
+ children: b.message
180
+ }),
181
+ /* @__PURE__ */ c("div", {
182
+ className: "px-4 pb-4 flex items-center gap-3",
183
+ children: [/* @__PURE__ */ s("button", {
184
+ type: "submit",
185
+ disabled: T || !m || !g || !v,
186
+ className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
187
+ children: T ? "Assigning…" : "Assign Plan for Free"
188
+ }), /* @__PURE__ */ s("button", {
189
+ type: "button",
190
+ onClick: () => {
191
+ h(""), _(""), y(""), x(null);
192
+ },
193
+ className: "px-4 py-2 text-sm border border-border rounded-md text-foreground hover:bg-muted transition-colors",
194
+ children: "Clear"
195
+ })]
196
+ })
197
+ ]
198
+ })
199
+ ]
200
+ });
201
+ };
202
+ //#endregion
203
+ export { u as PlanAssignFreePage };
204
+
205
+ //# sourceMappingURL=PlanAssignFreePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PlanAssignFreePage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanAssignFreePage.tsx"],"sourcesContent":["/**\n * Plans Module - Assign Plan Free (Admin only)\n *\n * Internal tool for assigning any plan to a billing account at no charge.\n * Protected by PLAN_ASSIGN_SECRET — only team members who know the secret\n * can use this page. Never expose to end users.\n *\n * Route: /billing/plans/assign-free\n */\n\nimport { useState, type FC, type FormEvent } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useAssignPlanFreeMutation } from '../../../../generated/global-operations';\nimport { usePlans } from '../hooks';\n\nexport const PlanAssignFreePage: 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\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n\n const [secret, setSecret] = useState('');\n const [billingAccountId, setBillingAccountId] = useState('');\n const [planId, setPlanId] = useState('');\n const [result, setResult] = useState<{ success: boolean; message: string } | null>(null);\n\n // Fetch all plans (including non-purchasable internal ones) for the dropdown\n const { plans, isLoading: isPlansLoading } = usePlans({ includeFree: true });\n\n const [assignPlanFree, { loading: isAssigning }] = useAssignPlanFreeMutation();\n\n if (!permissions.canManagePlans) {\n return (\n <AccessDenied\n message={tr(\n 'billing.assignFree.noPermission',\n \"You don't have permission to access this page.\"\n )}\n />\n );\n }\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setResult(null);\n\n if (!secret.trim() || !billingAccountId.trim() || !planId) {\n setResult({ success: false, message: 'All fields are required.' });\n return;\n }\n\n try {\n const { data } = await assignPlanFree({\n variables: {\n secret: secret.trim(),\n billingAccountId: billingAccountId.trim(),\n planId,\n },\n });\n\n if (data?.assignPlanFree) {\n const sub = data.assignPlanFree;\n setResult({\n success: true,\n message: `Plan assigned. Subscription ID: ${sub.id} · Status: ${sub.status} · Valid: ${new Date(sub.startDate).toLocaleDateString()} → ${new Date(sub.endDate).toLocaleDateString()}`,\n });\n // Clear sensitive fields\n setSecret('');\n setBillingAccountId('');\n setPlanId('');\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : 'Failed to assign plan.';\n setResult({ success: false, message: msg });\n }\n };\n\n const selectedPlan = plans.find((p) => p.id === planId);\n\n return (\n <div className=\"space-y-6 p-6 max-w-2xl\">\n <PageHeader\n title={tr('billing.assignFree.title', 'Assign Plan for Free')}\n description={tr(\n 'billing.assignFree.description',\n 'Internally assign any plan to a billing account at no charge. Requires the team secret.'\n )}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"px-3 py-2 text-sm border border-border rounded-md text-foreground hover:bg-muted transition-colors\"\n >\n ← Back to Plans\n </button>\n }\n />\n\n {/* Warning banner */}\n <div className=\"flex items-start gap-3 p-4 rounded-lg border border-status-warning-border bg-status-warning-bg/10\">\n <svg\n className=\"size-5 text-status-warning-text shrink-0 mt-0.5\"\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-2.5L13.732 4c-.77-.833-1.962-.833-2.732 0L3.072 16.5c-.77.833.192 2.5 1.732 2.5z\"\n />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-status-warning-text\">Internal admin tool</p>\n <p className=\"text-xs text-status-warning-text/80 mt-0.5\">\n This bypasses all payment flows. The plan is assigned immediately with no charge. Use\n only for enterprise deals or internal accounts.\n </p>\n </div>\n </div>\n\n <form\n onSubmit={handleSubmit}\n className=\"border border-border rounded-lg bg-card overflow-hidden\"\n >\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">Assignment Details</h2>\n </div>\n\n <div className=\"p-4 space-y-4\">\n {/* Secret */}\n <div>\n <label htmlFor=\"secret\" className=\"block text-sm font-medium text-foreground mb-1\">\n Team Secret <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"secret\"\n type=\"password\"\n value={secret}\n onChange={(e) => setSecret(e.target.value)}\n autoComplete=\"off\"\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary font-mono\"\n placeholder=\"Enter PLAN_ASSIGN_SECRET\"\n required\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n The secret key shared within the team. Never expose this to users.\n </p>\n </div>\n\n {/* Billing Account ID */}\n <div>\n <label\n htmlFor=\"billingAccountId\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n Billing Account ID <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"billingAccountId\"\n type=\"text\"\n value={billingAccountId}\n onChange={(e) => setBillingAccountId(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary font-mono text-sm\"\n placeholder=\"e.g. ba_01jk3n2...\"\n required\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n Find this in the user's billing account settings or via the billing accounts list.\n </p>\n </div>\n\n {/* Plan selector */}\n <div>\n <label htmlFor=\"planId\" className=\"block text-sm font-medium text-foreground mb-1\">\n Plan <span className=\"text-status-error-text\">*</span>\n </label>\n {isPlansLoading ? (\n <div className=\"h-10 bg-muted animate-pulse rounded-md\" />\n ) : (\n <select\n id=\"planId\"\n value={planId}\n onChange={(e) => setPlanId(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n required\n >\n <option value=\"\" disabled>\n Select a plan…\n </option>\n {plans.map((plan) => (\n <option key={plan.id} value={plan.id}>\n {plan.name}\n {!plan.isPurchasable ? ' (internal)' : ''}\n {plan.price != null && plan.price > 0\n ? ` — ${plan.currency ?? 'USD'} ${plan.price}`\n : ''}\n </option>\n ))}\n </select>\n )}\n {selectedPlan && (\n <p className=\"text-xs text-muted-foreground mt-1\">\n {selectedPlan.isPurchasable ? 'Public plan' : 'Internal / non-purchasable plan'}\n {selectedPlan.productID ? ` · Product: ${selectedPlan.productID}` : ''}\n </p>\n )}\n </div>\n </div>\n\n {/* Result banner */}\n {result && (\n <div\n className={`mx-4 mb-4 p-3 rounded-lg border text-sm ${\n result.success\n ? 'border-status-success-border bg-status-success-bg/10 text-status-success-text'\n : 'border-status-error-border bg-status-error-bg/10 text-status-error-text'\n }`}\n >\n {result.message}\n </div>\n )}\n\n <div className=\"px-4 pb-4 flex items-center gap-3\">\n <button\n type=\"submit\"\n disabled={isAssigning || !secret || !billingAccountId || !planId}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isAssigning ? 'Assigning…' : 'Assign Plan for Free'}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setSecret('');\n setBillingAccountId('');\n setPlanId('');\n setResult(null);\n }}\n className=\"px-4 py-2 text-sm border border-border rounded-md text-foreground hover:bg-muted transition-colors\"\n >\n Clear\n </button>\n </div>\n </form>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -16,7 +16,7 @@ var p = () => {
16
16
  let { t: p } = f(), m = (e, t) => {
17
17
  let n = p(e);
18
18
  return n === e ? t : n;
19
- }, h = a(), g = i(), { quotas: _, isLoading: v } = s({ onlyActive: !0 }), { createPlan: y, isCreating: b } = o(), [x, S] = c(""), [C, w] = c(""), [T, E] = c("USD"), [D, O] = c(r.MONTHLY), [k, A] = c(""), [j, M] = c([]), [N, P] = c([]), [F, I] = c(null), [L, R] = c(!1);
19
+ }, h = a(), g = i(), { quotas: _, isLoading: v } = s({ onlyActive: !0 }), { createPlan: y, isCreating: b } = o(), [x, S] = c(""), [C, w] = c(""), [T, E] = c("USD"), [D, O] = c(r.MONTHLY), [k, A] = c(""), [j, M] = c(!0), [N, P] = c([]), [F, I] = c([]), [L, R] = c(null), [z, B] = c(!1);
20
20
  if (!g.canManagePlans) return /* @__PURE__ */ u(e, { message: m("billing.plans.noCreatePermission", "You don't have permission to create plans.") });
21
21
  if (v) return /* @__PURE__ */ d("div", {
22
22
  className: "space-y-6 p-6",
@@ -31,16 +31,16 @@ var p = () => {
31
31
  ].map((e) => /* @__PURE__ */ u("div", { className: "h-16 bg-muted animate-pulse rounded" }, e))
32
32
  })]
33
33
  });
34
- let z = () => {
35
- M([{
34
+ let V = () => {
35
+ P([{
36
36
  quotaId: "",
37
37
  quantity: 1
38
- }, ...j]);
39
- }, B = (e) => {
40
- M(j.filter((t, n) => n !== e));
41
- }, V = (e, t, n) => {
42
- let r = [...j];
43
- t === "quotaId" ? r[e].quotaId = n : r[e].quantity = Number(n), M(r);
38
+ }, ...N]);
39
+ }, H = (e) => {
40
+ P(N.filter((t, n) => n !== e));
41
+ }, U = (e, t, n) => {
42
+ let r = [...N];
43
+ t === "quotaId" ? r[e].quotaId = n : r[e].quantity = Number(n), P(r);
44
44
  };
45
45
  return /* @__PURE__ */ d("div", {
46
46
  className: "space-y-6 p-6",
@@ -49,24 +49,24 @@ var p = () => {
49
49
  description: m("billing.plans.createNewPlanDesc", "Set up a new pricing plan with quotas")
50
50
  }), /* @__PURE__ */ d("form", {
51
51
  onSubmit: async (e) => {
52
- if (e.preventDefault(), I(null), R(!1), !x.trim()) {
53
- I("Plan name is required");
52
+ if (e.preventDefault(), R(null), B(!1), !x.trim()) {
53
+ R("Plan name is required");
54
54
  return;
55
55
  }
56
56
  if (!C || isNaN(Number(C)) || Number(C) < 0) {
57
- I("Valid price is required");
57
+ R("Valid price is required");
58
58
  return;
59
59
  }
60
60
  if (!k.trim()) {
61
- I("Product ID is required");
61
+ R("Product ID is required");
62
62
  return;
63
63
  }
64
- if (j.length === 0) {
65
- I("At least one quota is required");
64
+ if (N.length === 0) {
65
+ R("At least one quota is required");
66
66
  return;
67
67
  }
68
- if (j.filter((e) => e.quotaId === "").length > 0) {
69
- I("Please select a quota for all allocations");
68
+ if (N.filter((e) => e.quotaId === "").length > 0) {
69
+ R("Please select a quota for all allocations");
70
70
  return;
71
71
  }
72
72
  let t = {
@@ -75,28 +75,29 @@ var p = () => {
75
75
  currency: T,
76
76
  duration: D,
77
77
  productId: k.trim(),
78
- quotas: j.map((e) => ({
78
+ isPurchasable: j,
79
+ quotas: N.map((e) => ({
79
80
  quotaId: e.quotaId,
80
81
  quantity: e.quantity
81
82
  })),
82
- ...N.length > 0 ? { currencyPriceOverrides: N } : {}
83
+ ...F.length > 0 ? { currencyPriceOverrides: F } : {}
83
84
  };
84
85
  try {
85
- await y(t), R(!0), setTimeout(() => h("/plans"), 1500);
86
+ await y(t), B(!0), setTimeout(() => h("/plans"), 1500);
86
87
  } catch (e) {
87
- I(e instanceof Error ? e.message : "Failed to create plan");
88
+ R(e instanceof Error ? e.message : "Failed to create plan");
88
89
  }
89
90
  },
90
91
  className: "space-y-6",
91
92
  children: [
92
- F && /* @__PURE__ */ u("div", {
93
+ L && /* @__PURE__ */ u("div", {
93
94
  className: "p-4 bg-status-error-bg-subtle border border-border-subtle rounded-lg",
94
95
  children: /* @__PURE__ */ u("p", {
95
96
  className: "text-sm text-status-error-text",
96
- children: F
97
+ children: L
97
98
  })
98
99
  }),
99
- L && /* @__PURE__ */ u("div", {
100
+ z && /* @__PURE__ */ u("div", {
100
101
  className: "p-4 bg-status-success-bg-subtle border border-border-subtle rounded-lg",
101
102
  children: /* @__PURE__ */ u("p", {
102
103
  className: "text-sm text-status-success-text",
@@ -226,7 +227,24 @@ var p = () => {
226
227
  value: r.YEARLY,
227
228
  children: "Yearly"
228
229
  })]
229
- })] })
230
+ })] }),
231
+ /* @__PURE__ */ d("div", {
232
+ className: "flex items-start justify-between gap-4 pt-1",
233
+ children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
234
+ className: "text-sm font-medium text-foreground",
235
+ children: m("billing.plans.isPurchasableLabel", "Publicly purchasable")
236
+ }), /* @__PURE__ */ u("p", {
237
+ className: "text-xs text-muted-foreground mt-0.5",
238
+ children: j ? m("billing.plans.isPurchasableOn", "Visible on pricing page — users can subscribe directly.") : m("billing.plans.isPurchasableOff", "Internal / custom plan — hidden from pricing page, assign via API only.")
239
+ })] }), /* @__PURE__ */ u("button", {
240
+ type: "button",
241
+ role: "switch",
242
+ "aria-checked": j,
243
+ onClick: () => M((e) => !e),
244
+ className: `relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${j ? "bg-primary" : "bg-muted-foreground/30"}`,
245
+ children: /* @__PURE__ */ u("span", { className: `inline-block h-5 w-5 transform rounded-full bg-white shadow transition-transform ${j ? "translate-x-5" : "translate-x-0"}` })
246
+ })]
247
+ })
230
248
  ]
231
249
  })]
232
250
  }), /* @__PURE__ */ d("div", {
@@ -238,7 +256,7 @@ var p = () => {
238
256
  children: [m("billing.plans.quotaAllocations", "Quota Allocations"), " *"]
239
257
  }), /* @__PURE__ */ u("button", {
240
258
  type: "button",
241
- onClick: z,
259
+ onClick: V,
242
260
  disabled: _.length === 0,
243
261
  className: "px-3 py-1 text-sm font-medium text-primary hover:bg-primary/10 rounded-md transition-colors disabled:opacity-50",
244
262
  children: "+ Add Quota"
@@ -248,10 +266,10 @@ var p = () => {
248
266
  children: _.length === 0 ? /* @__PURE__ */ u("p", {
249
267
  className: "text-sm text-muted-foreground",
250
268
  children: m("billing.plans.noQuotasAvailable", "No quotas available. Please create quotas first.")
251
- }) : j.length === 0 ? /* @__PURE__ */ u("p", {
269
+ }) : N.length === 0 ? /* @__PURE__ */ u("p", {
252
270
  className: "text-sm text-muted-foreground",
253
271
  children: m("billing.plans.noQuotasAllocated", "No quotas allocated. Click \"Add Quota\" to add one.")
254
- }) : j.map((e, t) => {
272
+ }) : N.map((e, t) => {
255
273
  let n = _.find((t) => t.id === e.quotaId), r = n?.limits, i = r?.value ?? 0, a = r?.type ?? "unknown", o = i * e.quantity;
256
274
  return /* @__PURE__ */ d("div", {
257
275
  className: "p-3 border border-border rounded-lg bg-muted/20 space-y-3",
@@ -259,7 +277,7 @@ var p = () => {
259
277
  className: "flex items-center gap-3",
260
278
  children: [/* @__PURE__ */ d("select", {
261
279
  value: e.quotaId,
262
- onChange: (e) => V(t, "quotaId", e.target.value),
280
+ onChange: (e) => U(t, "quotaId", e.target.value),
263
281
  className: `flex-1 px-3 py-2 border border-border rounded-md bg-background focus:outline-none focus:ring-2 focus:ring-primary ${e.quotaId === "" ? "text-muted-foreground" : "text-foreground"}`,
264
282
  children: [/* @__PURE__ */ u("option", {
265
283
  value: "",
@@ -271,7 +289,7 @@ var p = () => {
271
289
  }, e.id))]
272
290
  }), /* @__PURE__ */ u("button", {
273
291
  type: "button",
274
- onClick: () => B(t),
292
+ onClick: () => H(t),
275
293
  className: "p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-md transition-colors",
276
294
  title: "Remove quota",
277
295
  children: /* @__PURE__ */ u("svg", {
@@ -321,7 +339,7 @@ var p = () => {
321
339
  type: "number",
322
340
  min: "1",
323
341
  value: e.quantity,
324
- onChange: (e) => V(t, "quantity", e.target.value),
342
+ onChange: (e) => U(t, "quantity", e.target.value),
325
343
  className: "w-24 px-3 py-1.5 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary text-center"
326
344
  })]
327
345
  }), /* @__PURE__ */ d("div", {
@@ -341,8 +359,8 @@ var p = () => {
341
359
  })]
342
360
  }),
343
361
  /* @__PURE__ */ u(n, {
344
- overrides: N,
345
- onChange: P
362
+ overrides: F,
363
+ onChange: I
346
364
  }),
347
365
  /* @__PURE__ */ d("div", {
348
366
  className: "flex items-center gap-3 pt-4",
@@ -1 +1 @@
1
- {"version":3,"file":"PlanCreatePage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanCreatePage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Create Page\n * Form to create a new plan\n */\n\nimport { useState, type FC, type FormEvent } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlanMutations, useQuotas } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, CurrencyPriceOverridesEditor, PageHeader } from '../../../shared/components';\nimport {\n PlanDuration,\n type CreatePlanInput,\n type CurrencyPriceOverrideInput,\n} from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface QuotaAllocation {\n quotaId: string;\n quantity: number;\n}\n\nexport const PlanCreatePage: 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 navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { quotas, isLoading: isQuotasLoading } = useQuotas({ onlyActive: true });\n const { createPlan, isCreating } = usePlanMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [price, setPrice] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [duration, setDuration] = useState<PlanDuration>(PlanDuration.MONTHLY);\n const [productId, setProductId] = useState('');\n const [quotaAllocations, setQuotaAllocations] = useState<QuotaAllocation[]>([]);\n const [currencyPriceOverrides, setCurrencyPriceOverrides] = useState<\n CurrencyPriceOverrideInput[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Permission check\n if (!permissions.canManagePlans) {\n return (\n <AccessDenied\n message={tr(\n 'billing.plans.noCreatePermission',\n \"You don't have permission to create plans.\"\n )}\n />\n );\n }\n\n // Loading state\n if (isQuotasLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"max-w-2xl space-y-4\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-16 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n const handleAddQuota = () => {\n // Add new quota at the top with empty quotaId (placeholder)\n setQuotaAllocations([{ quotaId: '', quantity: 1 }, ...quotaAllocations]);\n };\n\n const handleRemoveQuota = (index: number) => {\n setQuotaAllocations(quotaAllocations.filter((_, i) => i !== index));\n };\n\n const handleQuotaChange = (\n index: number,\n field: 'quotaId' | 'quantity',\n value: string | number\n ) => {\n const updated = [...quotaAllocations];\n if (field === 'quotaId') {\n updated[index].quotaId = value as string;\n } else {\n updated[index].quantity = Number(value);\n }\n setQuotaAllocations(updated);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n setSuccess(false);\n\n // Validation\n if (!name.trim()) {\n setError('Plan name is required');\n return;\n }\n if (!price || isNaN(Number(price)) || Number(price) < 0) {\n setError('Valid price is required');\n return;\n }\n if (!productId.trim()) {\n setError('Product ID is required');\n return;\n }\n if (quotaAllocations.length === 0) {\n setError('At least one quota is required');\n return;\n }\n // Check for unselected quotas\n const unselectedQuotas = quotaAllocations.filter((a) => a.quotaId === '');\n if (unselectedQuotas.length > 0) {\n setError('Please select a quota for all allocations');\n return;\n }\n\n const input: CreatePlanInput = {\n name: name.trim(),\n price: Number(price),\n currency,\n duration,\n productId: productId.trim(),\n quotas: quotaAllocations.map((a) => ({\n quotaId: a.quotaId,\n quantity: a.quantity,\n })),\n ...(currencyPriceOverrides.length > 0 ? { currencyPriceOverrides } : {}),\n };\n\n try {\n await createPlan(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo('/plans'), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create plan');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.plans.createNewPlan', 'Create New Plan')}\n description={tr('billing.plans.createNewPlanDesc', 'Set up a new pricing plan with quotas')}\n />\n\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Error/Success Messages */}\n {error && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-error-text\">{error}</p>\n </div>\n )}\n {success && (\n <div className=\"p-4 bg-status-success-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-success-text\">\n {tr('billing.plans.createSuccess', 'Plan created successfully! Redirecting...')}\n </p>\n </div>\n )}\n\n {/* Two-column layout on desktop */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Basic Info */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden h-fit\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.plans.basicInfo', 'Basic Information')}\n </h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Name */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-foreground mb-1\">\n {tr('billing.plans.planNameLabel', 'Plan Name')} *\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder={tr('billing.plans.planNamePlaceholder', 'e.g., Pro Plan')}\n required\n />\n </div>\n\n {/* Product ID */}\n <div>\n <label\n htmlFor=\"productId\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n {tr('billing.plans.productId', 'Product ID')} *\n </label>\n <input\n id=\"productId\"\n type=\"text\"\n value={productId}\n onChange={(e) => setProductId(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder={tr('billing.plans.productIdPlaceholder', 'e.g., prod_abc123')}\n required\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n {tr('billing.plans.productIdDesc', 'The product this plan belongs to')}\n </p>\n </div>\n\n {/* Price & Currency */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label htmlFor=\"price\" className=\"block text-sm font-medium text-foreground mb-1\">\n {tr('billing.plans.priceLabel', 'Price')} *\n </label>\n <input\n id=\"price\"\n type=\"number\"\n step=\"0.01\"\n min=\"0\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"49.99\"\n required\n />\n </div>\n <div>\n <label\n htmlFor=\"currency\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n {tr('billing.plans.currencyLabel', 'Currency')} *\n </label>\n <select\n id=\"currency\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"USD\">USD ($)</option>\n <option value=\"EUR\">EUR</option>\n <option value=\"GBP\">GBP</option>\n <option value=\"INR\">INR</option>\n <option value=\"AUD\">AUD</option>\n <option value=\"CAD\">CAD</option>\n <option value=\"JPY\">JPY</option>\n <option value=\"SGD\">SGD</option>\n </select>\n </div>\n </div>\n\n {/* Duration */}\n <div>\n <label\n htmlFor=\"duration\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n {tr('billing.plans.billingPeriodLabel', 'Billing Period')} *\n </label>\n <select\n id=\"duration\"\n value={duration}\n onChange={(e) => setDuration(e.target.value as PlanDuration)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={PlanDuration.MONTHLY}>Monthly</option>\n <option value={PlanDuration.YEARLY}>Yearly</option>\n </select>\n </div>\n </div>\n </div>\n\n {/* Quota Allocations */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden h-fit\">\n <div className=\"p-4 border-b border-border bg-muted/30 flex items-center justify-between\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.plans.quotaAllocations', 'Quota Allocations')} *\n </h2>\n <button\n type=\"button\"\n onClick={handleAddQuota}\n disabled={quotas.length === 0}\n className=\"px-3 py-1 text-sm font-medium text-primary hover:bg-primary/10 rounded-md transition-colors disabled:opacity-50\"\n >\n + Add Quota\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'billing.plans.noQuotasAvailable',\n 'No quotas available. Please create quotas first.'\n )}\n </p>\n ) : quotaAllocations.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'billing.plans.noQuotasAllocated',\n 'No quotas allocated. Click \"Add Quota\" to add one.'\n )}\n </p>\n ) : (\n quotaAllocations.map((allocation, index) => {\n const selectedQuota = quotas.find((q) => q.id === allocation.quotaId);\n const limits = selectedQuota?.limits as { type?: string; value?: number } | null;\n const limitValue = limits?.value ?? 0;\n const limitType = limits?.type ?? 'unknown';\n const totalValue = limitValue * allocation.quantity;\n\n return (\n <div\n key={index}\n className=\"p-3 border border-border rounded-lg bg-muted/20 space-y-3\"\n >\n <div className=\"flex items-center gap-3\">\n <select\n value={allocation.quotaId}\n onChange={(e) => handleQuotaChange(index, 'quotaId', e.target.value)}\n className={`flex-1 px-3 py-2 border border-border rounded-md bg-background focus:outline-none focus:ring-2 focus:ring-primary ${\n allocation.quotaId === '' ? 'text-muted-foreground' : 'text-foreground'\n }`}\n >\n <option value=\"\" disabled>\n {tr('billing.plans.selectQuota', 'Select a quota...')}\n </option>\n {quotas.map((quota) => (\n <option key={quota.id} value={quota.id}>\n {quota.name}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n onClick={() => handleRemoveQuota(index)}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-md transition-colors\"\n title=\"Remove quota\"\n >\n <svg\n className=\"size-5\"\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=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n </button>\n </div>\n\n {/* Quota Info - only show when quota is selected */}\n {selectedQuota && (\n <>\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"text-muted-foreground\">\n <span className=\"capitalize\">Type: {limitType}</span>\n <span className=\"mx-2\">•</span>\n <span>\n Value per unit:{' '}\n <span className=\"font-medium text-foreground\">\n {limitValue.toLocaleString()}\n </span>\n </span>\n </div>\n </div>\n\n {/* Quantity & Total */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.quantity', 'Quantity')}:\n </label>\n <input\n type=\"number\"\n min=\"1\"\n value={allocation.quantity}\n onChange={(e) =>\n handleQuotaChange(index, 'quantity', e.target.value)\n }\n className=\"w-24 px-3 py-1.5 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary text-center\"\n />\n </div>\n <div className=\"flex items-center gap-2 text-sm\">\n <span className=\"text-muted-foreground\">=</span>\n <span className=\"font-semibold text-status-success-text\">\n {totalValue.toLocaleString()} total\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n <CurrencyPriceOverridesEditor\n overrides={currencyPriceOverrides}\n onChange={setCurrencyPriceOverrides}\n />\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4\">\n <button\n type=\"submit\"\n disabled={isCreating || quotas.length === 0}\n className=\"px-6 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isCreating\n ? tr('billing.plans.creating', 'Creating...')\n : tr('billing.plans.createPlanBtn', 'Create Plan')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"px-6 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PlanCreatePage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanCreatePage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Create Page\n * Form to create a new plan\n */\n\nimport { useState, type FC, type FormEvent } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlanMutations, useQuotas } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, CurrencyPriceOverridesEditor, PageHeader } from '../../../shared/components';\nimport {\n PlanDuration,\n type CreatePlanInput,\n type CurrencyPriceOverrideInput,\n} from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface QuotaAllocation {\n quotaId: string;\n quantity: number;\n}\n\nexport const PlanCreatePage: 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 navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { quotas, isLoading: isQuotasLoading } = useQuotas({ onlyActive: true });\n const { createPlan, isCreating } = usePlanMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [price, setPrice] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [duration, setDuration] = useState<PlanDuration>(PlanDuration.MONTHLY);\n const [productId, setProductId] = useState('');\n const [isPurchasable, setIsPurchasable] = useState(true);\n const [quotaAllocations, setQuotaAllocations] = useState<QuotaAllocation[]>([]);\n const [currencyPriceOverrides, setCurrencyPriceOverrides] = useState<\n CurrencyPriceOverrideInput[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Permission check\n if (!permissions.canManagePlans) {\n return (\n <AccessDenied\n message={tr(\n 'billing.plans.noCreatePermission',\n \"You don't have permission to create plans.\"\n )}\n />\n );\n }\n\n // Loading state\n if (isQuotasLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"max-w-2xl space-y-4\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-16 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n const handleAddQuota = () => {\n // Add new quota at the top with empty quotaId (placeholder)\n setQuotaAllocations([{ quotaId: '', quantity: 1 }, ...quotaAllocations]);\n };\n\n const handleRemoveQuota = (index: number) => {\n setQuotaAllocations(quotaAllocations.filter((_, i) => i !== index));\n };\n\n const handleQuotaChange = (\n index: number,\n field: 'quotaId' | 'quantity',\n value: string | number\n ) => {\n const updated = [...quotaAllocations];\n if (field === 'quotaId') {\n updated[index].quotaId = value as string;\n } else {\n updated[index].quantity = Number(value);\n }\n setQuotaAllocations(updated);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n setSuccess(false);\n\n // Validation\n if (!name.trim()) {\n setError('Plan name is required');\n return;\n }\n if (!price || isNaN(Number(price)) || Number(price) < 0) {\n setError('Valid price is required');\n return;\n }\n if (!productId.trim()) {\n setError('Product ID is required');\n return;\n }\n if (quotaAllocations.length === 0) {\n setError('At least one quota is required');\n return;\n }\n // Check for unselected quotas\n const unselectedQuotas = quotaAllocations.filter((a) => a.quotaId === '');\n if (unselectedQuotas.length > 0) {\n setError('Please select a quota for all allocations');\n return;\n }\n\n const input: CreatePlanInput = {\n name: name.trim(),\n price: Number(price),\n currency,\n duration,\n productId: productId.trim(),\n isPurchasable,\n quotas: quotaAllocations.map((a) => ({\n quotaId: a.quotaId,\n quantity: a.quantity,\n })),\n ...(currencyPriceOverrides.length > 0 ? { currencyPriceOverrides } : {}),\n };\n\n try {\n await createPlan(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo('/plans'), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create plan');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.plans.createNewPlan', 'Create New Plan')}\n description={tr('billing.plans.createNewPlanDesc', 'Set up a new pricing plan with quotas')}\n />\n\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Error/Success Messages */}\n {error && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-error-text\">{error}</p>\n </div>\n )}\n {success && (\n <div className=\"p-4 bg-status-success-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-success-text\">\n {tr('billing.plans.createSuccess', 'Plan created successfully! Redirecting...')}\n </p>\n </div>\n )}\n\n {/* Two-column layout on desktop */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Basic Info */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden h-fit\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.plans.basicInfo', 'Basic Information')}\n </h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Name */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-foreground mb-1\">\n {tr('billing.plans.planNameLabel', 'Plan Name')} *\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder={tr('billing.plans.planNamePlaceholder', 'e.g., Pro Plan')}\n required\n />\n </div>\n\n {/* Product ID */}\n <div>\n <label\n htmlFor=\"productId\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n {tr('billing.plans.productId', 'Product ID')} *\n </label>\n <input\n id=\"productId\"\n type=\"text\"\n value={productId}\n onChange={(e) => setProductId(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder={tr('billing.plans.productIdPlaceholder', 'e.g., prod_abc123')}\n required\n />\n <p className=\"text-xs text-muted-foreground mt-1\">\n {tr('billing.plans.productIdDesc', 'The product this plan belongs to')}\n </p>\n </div>\n\n {/* Price & Currency */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label htmlFor=\"price\" className=\"block text-sm font-medium text-foreground mb-1\">\n {tr('billing.plans.priceLabel', 'Price')} *\n </label>\n <input\n id=\"price\"\n type=\"number\"\n step=\"0.01\"\n min=\"0\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"49.99\"\n required\n />\n </div>\n <div>\n <label\n htmlFor=\"currency\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n {tr('billing.plans.currencyLabel', 'Currency')} *\n </label>\n <select\n id=\"currency\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"USD\">USD ($)</option>\n <option value=\"EUR\">EUR</option>\n <option value=\"GBP\">GBP</option>\n <option value=\"INR\">INR</option>\n <option value=\"AUD\">AUD</option>\n <option value=\"CAD\">CAD</option>\n <option value=\"JPY\">JPY</option>\n <option value=\"SGD\">SGD</option>\n </select>\n </div>\n </div>\n\n {/* Duration */}\n <div>\n <label\n htmlFor=\"duration\"\n className=\"block text-sm font-medium text-foreground mb-1\"\n >\n {tr('billing.plans.billingPeriodLabel', 'Billing Period')} *\n </label>\n <select\n id=\"duration\"\n value={duration}\n onChange={(e) => setDuration(e.target.value as PlanDuration)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value={PlanDuration.MONTHLY}>Monthly</option>\n <option value={PlanDuration.YEARLY}>Yearly</option>\n </select>\n </div>\n\n {/* Purchasable toggle */}\n <div className=\"flex items-start justify-between gap-4 pt-1\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {tr('billing.plans.isPurchasableLabel', 'Publicly purchasable')}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {isPurchasable\n ? tr(\n 'billing.plans.isPurchasableOn',\n 'Visible on pricing page — users can subscribe directly.'\n )\n : tr(\n 'billing.plans.isPurchasableOff',\n 'Internal / custom plan — hidden from pricing page, assign via API only.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isPurchasable}\n onClick={() => setIsPurchasable((v) => !v)}\n className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 ${\n isPurchasable ? 'bg-primary' : 'bg-muted-foreground/30'\n }`}\n >\n <span\n className={`inline-block h-5 w-5 transform rounded-full bg-white shadow transition-transform ${\n isPurchasable ? 'translate-x-5' : 'translate-x-0'\n }`}\n />\n </button>\n </div>\n </div>\n </div>\n\n {/* Quota Allocations */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden h-fit\">\n <div className=\"p-4 border-b border-border bg-muted/30 flex items-center justify-between\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.plans.quotaAllocations', 'Quota Allocations')} *\n </h2>\n <button\n type=\"button\"\n onClick={handleAddQuota}\n disabled={quotas.length === 0}\n className=\"px-3 py-1 text-sm font-medium text-primary hover:bg-primary/10 rounded-md transition-colors disabled:opacity-50\"\n >\n + Add Quota\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'billing.plans.noQuotasAvailable',\n 'No quotas available. Please create quotas first.'\n )}\n </p>\n ) : quotaAllocations.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'billing.plans.noQuotasAllocated',\n 'No quotas allocated. Click \"Add Quota\" to add one.'\n )}\n </p>\n ) : (\n quotaAllocations.map((allocation, index) => {\n const selectedQuota = quotas.find((q) => q.id === allocation.quotaId);\n const limits = selectedQuota?.limits as { type?: string; value?: number } | null;\n const limitValue = limits?.value ?? 0;\n const limitType = limits?.type ?? 'unknown';\n const totalValue = limitValue * allocation.quantity;\n\n return (\n <div\n key={index}\n className=\"p-3 border border-border rounded-lg bg-muted/20 space-y-3\"\n >\n <div className=\"flex items-center gap-3\">\n <select\n value={allocation.quotaId}\n onChange={(e) => handleQuotaChange(index, 'quotaId', e.target.value)}\n className={`flex-1 px-3 py-2 border border-border rounded-md bg-background focus:outline-none focus:ring-2 focus:ring-primary ${\n allocation.quotaId === '' ? 'text-muted-foreground' : 'text-foreground'\n }`}\n >\n <option value=\"\" disabled>\n {tr('billing.plans.selectQuota', 'Select a quota...')}\n </option>\n {quotas.map((quota) => (\n <option key={quota.id} value={quota.id}>\n {quota.name}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n onClick={() => handleRemoveQuota(index)}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-md transition-colors\"\n title=\"Remove quota\"\n >\n <svg\n className=\"size-5\"\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=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n </button>\n </div>\n\n {/* Quota Info - only show when quota is selected */}\n {selectedQuota && (\n <>\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"text-muted-foreground\">\n <span className=\"capitalize\">Type: {limitType}</span>\n <span className=\"mx-2\">•</span>\n <span>\n Value per unit:{' '}\n <span className=\"font-medium text-foreground\">\n {limitValue.toLocaleString()}\n </span>\n </span>\n </div>\n </div>\n\n {/* Quantity & Total */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-muted-foreground\">\n {tr('billing.plans.quantity', 'Quantity')}:\n </label>\n <input\n type=\"number\"\n min=\"1\"\n value={allocation.quantity}\n onChange={(e) =>\n handleQuotaChange(index, 'quantity', e.target.value)\n }\n className=\"w-24 px-3 py-1.5 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary text-center\"\n />\n </div>\n <div className=\"flex items-center gap-2 text-sm\">\n <span className=\"text-muted-foreground\">=</span>\n <span className=\"font-semibold text-status-success-text\">\n {totalValue.toLocaleString()} total\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n <CurrencyPriceOverridesEditor\n overrides={currencyPriceOverrides}\n onChange={setCurrencyPriceOverrides}\n />\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4\">\n <button\n type=\"submit\"\n disabled={isCreating || quotas.length === 0}\n className=\"px-6 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n {isCreating\n ? tr('billing.plans.creating', 'Creating...')\n : tr('billing.plans.createPlanBtn', 'Create Plan')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"px-6 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n );\n};\n"],"mappings":"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"}