@burdenoff/microfe-billing 2026.528.1 → 2026.530.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +27 -25
  2. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
  3. package/dist/billing/modules/dashboard/components/BillingAccountCard.js +9 -8
  4. package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
  5. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +7 -6
  6. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
  7. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +9 -7
  8. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
  9. package/dist/billing/modules/dashboard/pages/OverviewPage.js +7 -0
  10. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  11. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +82 -81
  12. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  13. package/dist/billing/modules/plans/pages/PlanDetailPage.js +105 -104
  14. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  15. package/dist/billing/modules/plans/pages/PlanEditPage.js +162 -161
  16. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  17. package/dist/billing/modules/plans/pages/PlansBrowsePage.js +85 -84
  18. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  19. package/dist/billing/modules/plans/pages/PlansListPage.js +115 -114
  20. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  21. package/dist/billing/shared/utils/index.js +1 -0
  22. package/dist/billing/shared/utils/planFeatureQuantity.js +11 -0
  23. package/dist/billing/shared/utils/planFeatureQuantity.js.map +1 -0
  24. package/dist/generated/global-operations.js +1 -0
  25. package/dist/generated/global-operations.js.map +1 -1
  26. package/package.json +1 -1
@@ -4,24 +4,25 @@ import { AlertTriangle as r, ArrowRight as i, Calendar as a, Clock as o, Coins a
4
4
  import { jsx as f, jsxs as p } from "react/jsx-runtime";
5
5
  import { useI18n as m } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
6
6
  //#region src/billing/modules/dashboard/components/ActiveSubscriptionCard.tsx
7
- var h = ({ subscription: h, onViewDetails: g, onUpgrade: _, onManage: v, onPurchasePlan: y, className: b = "" }) => {
8
- let { t: x } = m(), S = (e, t) => {
9
- let n = x(e);
7
+ var h = ({ subscription: h, onViewDetails: g, onUpgrade: _, onManage: v, onPurchasePlan: y, className: b = "", dataTour: x }) => {
8
+ let { t: S } = m(), C = (e, t) => {
9
+ let n = S(e);
10
10
  return n === e ? t : n;
11
- }, C = (e) => e ? new Date(e).toLocaleDateString("en-US", {
11
+ }, w = (e) => e ? new Date(e).toLocaleDateString("en-US", {
12
12
  year: "numeric",
13
13
  month: "long",
14
14
  day: "numeric"
15
- }) : "N/A", w = (e, t) => new Intl.NumberFormat("en-US", {
15
+ }) : "N/A", T = (e, t) => new Intl.NumberFormat("en-US", {
16
16
  style: "currency",
17
17
  currency: t || "USD",
18
18
  minimumFractionDigits: 2
19
- }).format(e), T = n(() => {
19
+ }).format(e), E = n(() => {
20
20
  if (!h?.endDate) return null;
21
21
  let e = new Date(h.endDate), t = /* @__PURE__ */ new Date();
22
22
  return Math.ceil((e.getTime() - t.getTime()) / (1e3 * 60 * 60 * 24));
23
- }, [h?.endDate]), E = T !== null && T <= 7 && T > 0, D = T !== null && T <= 0, O = () => D ? `${t.error.text} ${t.error.bg}` : E ? `${t.warning.text} ${t.warning.bg}` : `${t.success.text} ${t.success.bg}`, k = () => D ? "Expired" : E ? "Expiring Soon" : "Active";
23
+ }, [h?.endDate]), D = E !== null && E <= 7 && E > 0, O = E !== null && E <= 0, k = () => O ? `${t.error.text} ${t.error.bg}` : D ? `${t.warning.text} ${t.warning.bg}` : `${t.success.text} ${t.success.bg}`, A = () => O ? "Expired" : D ? "Expiring Soon" : "Active";
24
24
  if (!h) return /* @__PURE__ */ f("section", {
25
+ "data-tour": x,
25
26
  className: `border border-border rounded-lg bg-card p-6 ${b}`,
26
27
  children: /* @__PURE__ */ p("div", {
27
28
  className: "flex flex-col items-center text-center py-4",
@@ -32,7 +33,7 @@ var h = ({ subscription: h, onViewDetails: g, onUpgrade: _, onManage: v, onPurch
32
33
  }),
33
34
  /* @__PURE__ */ f("h3", {
34
35
  className: "text-lg font-semibold text-foreground mb-2",
35
- children: S("billing.overview.noActiveSubscription", "No Active Subscription")
36
+ children: C("billing.overview.noActiveSubscription", "No Active Subscription")
36
37
  }),
37
38
  /* @__PURE__ */ f("p", {
38
39
  className: "text-sm text-muted-foreground mb-6 max-w-sm",
@@ -47,11 +48,12 @@ var h = ({ subscription: h, onViewDetails: g, onUpgrade: _, onManage: v, onPurch
47
48
  ]
48
49
  })
49
50
  });
50
- let A = h.plan;
51
+ let j = h.plan;
51
52
  return /* @__PURE__ */ p("section", {
53
+ "data-tour": x,
52
54
  className: `border border-border rounded-lg bg-card overflow-hidden ${b}`,
53
55
  children: [
54
- E && /* @__PURE__ */ f("div", {
56
+ D && /* @__PURE__ */ f("div", {
55
57
  className: `px-4 py-2 ${e.warning.container} border-b`,
56
58
  children: /* @__PURE__ */ p("div", {
57
59
  className: `flex items-center gap-2 ${e.warning.text}`,
@@ -59,9 +61,9 @@ var h = ({ subscription: h, onViewDetails: g, onUpgrade: _, onManage: v, onPurch
59
61
  className: "text-sm font-medium",
60
62
  children: [
61
63
  "Your subscription expires in ",
62
- T,
64
+ E,
63
65
  " day",
64
- T === 1 ? "" : "s"
66
+ E === 1 ? "" : "s"
65
67
  ]
66
68
  })]
67
69
  })
@@ -79,17 +81,17 @@ var h = ({ subscription: h, onViewDetails: g, onUpgrade: _, onManage: v, onPurch
79
81
  className: "flex items-center gap-2 flex-wrap",
80
82
  children: [/* @__PURE__ */ f("h3", {
81
83
  className: "text-lg font-semibold text-foreground",
82
- children: A?.name || "Subscription"
84
+ children: j?.name || "Subscription"
83
85
  }), /* @__PURE__ */ f("span", {
84
- className: `inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${O()}`,
85
- children: k()
86
+ className: `inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${k()}`,
87
+ children: A()
86
88
  })]
87
89
  }), /* @__PURE__ */ p("p", {
88
90
  className: "text-sm text-muted-foreground mt-1",
89
91
  children: [
90
- A && w(A.price, A.currency),
92
+ j && T(j.price, j.currency),
91
93
  " / ",
92
- A?.duration || "month"
94
+ j?.duration || "month"
93
95
  ]
94
96
  })] })]
95
97
  }), /* @__PURE__ */ p("div", {
@@ -99,7 +101,7 @@ var h = ({ subscription: h, onViewDetails: g, onUpgrade: _, onManage: v, onPurch
99
101
  onClick: v,
100
102
  className: "px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors",
101
103
  children: "Manage"
102
- }), _ && !D && /* @__PURE__ */ f("button", {
104
+ }), _ && !O && /* @__PURE__ */ f("button", {
103
105
  type: "button",
104
106
  onClick: _,
105
107
  className: "px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors",
@@ -120,17 +122,17 @@ var h = ({ subscription: h, onViewDetails: g, onUpgrade: _, onManage: v, onPurch
120
122
  children: "Started"
121
123
  }), /* @__PURE__ */ p("p", {
122
124
  className: "text-sm font-medium text-foreground flex items-center gap-2",
123
- children: [/* @__PURE__ */ f(a, { className: "size-4 text-muted-foreground" }), C(h.startDate)]
125
+ children: [/* @__PURE__ */ f(a, { className: "size-4 text-muted-foreground" }), w(h.startDate)]
124
126
  })]
125
127
  }),
126
128
  /* @__PURE__ */ p("div", {
127
129
  className: "space-y-1.5",
128
130
  children: [/* @__PURE__ */ f("p", {
129
131
  className: "text-xs font-medium text-muted-foreground uppercase tracking-wide",
130
- children: D ? "Expired" : "Ends"
132
+ children: O ? "Expired" : "Ends"
131
133
  }), /* @__PURE__ */ p("p", {
132
- className: `text-sm font-medium flex items-center gap-2 ${D ? t.error.text : E ? t.warning.text : "text-foreground"}`,
133
- children: [/* @__PURE__ */ f(o, { className: "size-4" }), C(h.endDate)]
134
+ className: `text-sm font-medium flex items-center gap-2 ${O ? t.error.text : D ? t.warning.text : "text-foreground"}`,
135
+ children: [/* @__PURE__ */ f(o, { className: "size-4" }), w(h.endDate)]
134
136
  })]
135
137
  }),
136
138
  /* @__PURE__ */ p("div", {
@@ -154,19 +156,19 @@ var h = ({ subscription: h, onViewDetails: g, onUpgrade: _, onManage: v, onPurch
154
156
  })]
155
157
  })
156
158
  ]
157
- }), h.nextBillingDate && h.autoRenewal && !D && /* @__PURE__ */ f("div", {
159
+ }), h.nextBillingDate && h.autoRenewal && !O && /* @__PURE__ */ f("div", {
158
160
  className: "mt-6 p-4 rounded-lg bg-muted/50 border border-border",
159
161
  children: /* @__PURE__ */ p("div", {
160
162
  className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2",
161
163
  children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("p", {
162
164
  className: "text-sm font-medium text-foreground",
163
- children: S("billing.overview.nextBillingDate", "Next billing date")
165
+ children: C("billing.overview.nextBillingDate", "Next billing date")
164
166
  }), /* @__PURE__ */ f("p", {
165
167
  className: "text-xs text-muted-foreground",
166
168
  children: "You'll be charged automatically"
167
169
  })] }), /* @__PURE__ */ f("p", {
168
170
  className: "text-sm font-semibold text-foreground",
169
- children: C(h.nextBillingDate)
171
+ children: w(h.nextBillingDate)
170
172
  })]
171
173
  })
172
174
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"ActiveSubscriptionCard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/ActiveSubscriptionCard.tsx"],"sourcesContent":["/**\n * Active Subscription Card Component\n * Displays the active subscription details with renewal info\n */\n\nimport { type FC, useMemo } from 'react';\nimport {\n Crown,\n Calendar,\n ArrowRight,\n RefreshCw,\n Clock,\n CreditCard,\n Zap,\n AlertTriangle,\n Coins,\n} from 'lucide-react';\nimport type { BillingSubscription } from '../../../shared/types';\nimport { statusTokens, alertTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface ActiveSubscriptionCardProps {\n subscription: BillingSubscription | null;\n onViewDetails?: () => void;\n onUpgrade?: () => void;\n onManage?: () => void;\n onPurchasePlan?: () => void;\n className?: string;\n}\n\nexport const ActiveSubscriptionCard: FC<ActiveSubscriptionCardProps> = ({\n subscription,\n onViewDetails,\n onUpgrade,\n onManage,\n onPurchasePlan,\n className = '',\n}) => {\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 formatDate = (dateString: string | undefined) => {\n if (!dateString) return 'N/A';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n };\n\n const formatCurrency = (amount: number, currency: string) => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n minimumFractionDigits: 2,\n }).format(amount);\n };\n\n const daysUntilEnd = useMemo(() => {\n if (!subscription?.endDate) return null;\n const end = new Date(subscription.endDate);\n const now = new Date();\n const diff = Math.ceil((end.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));\n return diff;\n }, [subscription?.endDate]);\n\n const isExpiringSoon = daysUntilEnd !== null && daysUntilEnd <= 7 && daysUntilEnd > 0;\n const isExpired = daysUntilEnd !== null && daysUntilEnd <= 0;\n\n const getStatusColor = () => {\n if (isExpired) return `${statusTokens.error.text} ${statusTokens.error.bg}`;\n if (isExpiringSoon) return `${statusTokens.warning.text} ${statusTokens.warning.bg}`;\n return `${statusTokens.success.text} ${statusTokens.success.bg}`;\n };\n\n const getStatusText = () => {\n if (isExpired) return 'Expired';\n if (isExpiringSoon) return 'Expiring Soon';\n return 'Active';\n };\n\n // No subscription state\n if (!subscription) {\n return (\n <section className={`border border-border rounded-lg bg-card p-6 ${className}`}>\n <div className=\"flex flex-col items-center text-center py-4\">\n <div className=\"size-16 rounded-full bg-gradient-to-br from-primary/20 to-primary/5 flex items-center justify-center mb-4\">\n <Zap className=\"size-8 text-primary\" />\n </div>\n <h3 className=\"text-lg font-semibold text-foreground mb-2\">\n {tr('billing.overview.noActiveSubscription', 'No Active Subscription')}\n </h3>\n <p className=\"text-sm text-muted-foreground mb-6 max-w-sm\">\n Subscribe to a plan to unlock premium features and get the most out of your account\n </p>\n {onPurchasePlan && (\n <button\n type=\"button\"\n onClick={onPurchasePlan}\n className=\"inline-flex items-center gap-2 px-6 py-2.5 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Browse Plans\n <ArrowRight className=\"size-4\" />\n </button>\n )}\n </div>\n </section>\n );\n }\n\n const plan = subscription.plan;\n\n return (\n <section className={`border border-border rounded-lg bg-card overflow-hidden ${className}`}>\n {/* Expiring Soon Banner */}\n {isExpiringSoon && (\n <div className={`px-4 py-2 ${alertTokens.warning.container} border-b`}>\n <div className={`flex items-center gap-2 ${alertTokens.warning.text}`}>\n <AlertTriangle className=\"size-4\" />\n <span className=\"text-sm font-medium\">\n Your subscription expires in {daysUntilEnd} day{daysUntilEnd !== 1 ? 's' : ''}\n </span>\n </div>\n </div>\n )}\n\n {/* Header */}\n <header className=\"p-4 sm:p-6 border-b border-border\">\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4\">\n <div className=\"flex items-start gap-4\">\n <div className=\"size-12 rounded-lg bg-gradient-to-br from-primary to-primary/60 flex items-center justify-center flex-shrink-0\">\n <Crown className=\"size-6 text-primary-foreground\" />\n </div>\n <div>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"text-lg font-semibold text-foreground\">\n {plan?.name || 'Subscription'}\n </h3>\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${getStatusColor()}`}\n >\n {getStatusText()}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {plan && formatCurrency(plan.price, plan.currency)} / {plan?.duration || 'month'}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {onManage && (\n <button\n type=\"button\"\n onClick={onManage}\n className=\"px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors\"\n >\n Manage\n </button>\n )}\n {onUpgrade && !isExpired && (\n <button\n type=\"button\"\n onClick={onUpgrade}\n className=\"px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Upgrade\n </button>\n )}\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 sm:p-6\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {/* Start Date */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Started\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n <Calendar className=\"size-4 text-muted-foreground\" />\n {formatDate(subscription.startDate)}\n </p>\n </div>\n\n {/* End Date */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n {isExpired ? 'Expired' : 'Ends'}\n </p>\n <p\n className={`text-sm font-medium flex items-center gap-2 ${isExpired ? statusTokens.error.text : isExpiringSoon ? statusTokens.warning.text : 'text-foreground'}`}\n >\n <Clock className=\"size-4\" />\n {formatDate(subscription.endDate)}\n </p>\n </div>\n\n {/* Auto Renewal */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Auto Renewal\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n <RefreshCw\n className={`size-4 ${subscription.autoRenewal ? statusTokens.success.icon : 'text-muted-foreground'}`}\n />\n {subscription.autoRenewal ? 'Enabled' : 'Disabled'}\n </p>\n </div>\n\n {/* Payment Gateway */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Payment Via\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n {subscription.paymentGateway === 'CREDITS' ? (\n <Coins className={`size-4 ${statusTokens.warning.icon}`} />\n ) : (\n <CreditCard className=\"size-4 text-muted-foreground\" />\n )}\n {subscription.paymentGateway === 'CREDITS'\n ? 'Credits'\n : subscription.paymentGateway === 'STRIPE'\n ? 'Stripe'\n : subscription.paymentGateway === 'RAZORPAY'\n ? 'Razorpay'\n : subscription.paymentGateway || 'N/A'}\n </p>\n </div>\n </div>\n\n {/* Next Billing */}\n {subscription.nextBillingDate && subscription.autoRenewal && !isExpired && (\n <div className=\"mt-6 p-4 rounded-lg bg-muted/50 border border-border\">\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {tr('billing.overview.nextBillingDate', 'Next billing date')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n You&apos;ll be charged automatically\n </p>\n </div>\n <p className=\"text-sm font-semibold text-foreground\">\n {formatDate(subscription.nextBillingDate)}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {/* Footer */}\n {onViewDetails && (\n <footer className=\"px-4 sm:px-6 py-3 border-t border-border bg-muted/30\">\n <button\n type=\"button\"\n onClick={onViewDetails}\n className=\"w-full flex items-center justify-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n View Subscription Details\n <ArrowRight className=\"size-4\" />\n </button>\n </footer>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ActiveSubscriptionCard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/ActiveSubscriptionCard.tsx"],"sourcesContent":["/**\n * Active Subscription Card Component\n * Displays the active subscription details with renewal info\n */\n\nimport { type FC, useMemo } from 'react';\nimport {\n Crown,\n Calendar,\n ArrowRight,\n RefreshCw,\n Clock,\n CreditCard,\n Zap,\n AlertTriangle,\n Coins,\n} from 'lucide-react';\nimport type { BillingSubscription } from '../../../shared/types';\nimport { statusTokens, alertTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface ActiveSubscriptionCardProps {\n subscription: BillingSubscription | null;\n onViewDetails?: () => void;\n onUpgrade?: () => void;\n onManage?: () => void;\n onPurchasePlan?: () => void;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\nexport const ActiveSubscriptionCard: FC<ActiveSubscriptionCardProps> = ({\n subscription,\n onViewDetails,\n onUpgrade,\n onManage,\n onPurchasePlan,\n className = '',\n dataTour,\n}) => {\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 formatDate = (dateString: string | undefined) => {\n if (!dateString) return 'N/A';\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n });\n };\n\n const formatCurrency = (amount: number, currency: string) => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n minimumFractionDigits: 2,\n }).format(amount);\n };\n\n const daysUntilEnd = useMemo(() => {\n if (!subscription?.endDate) return null;\n const end = new Date(subscription.endDate);\n const now = new Date();\n const diff = Math.ceil((end.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));\n return diff;\n }, [subscription?.endDate]);\n\n const isExpiringSoon = daysUntilEnd !== null && daysUntilEnd <= 7 && daysUntilEnd > 0;\n const isExpired = daysUntilEnd !== null && daysUntilEnd <= 0;\n\n const getStatusColor = () => {\n if (isExpired) return `${statusTokens.error.text} ${statusTokens.error.bg}`;\n if (isExpiringSoon) return `${statusTokens.warning.text} ${statusTokens.warning.bg}`;\n return `${statusTokens.success.text} ${statusTokens.success.bg}`;\n };\n\n const getStatusText = () => {\n if (isExpired) return 'Expired';\n if (isExpiringSoon) return 'Expiring Soon';\n return 'Active';\n };\n\n // No subscription state\n if (!subscription) {\n return (\n <section\n data-tour={dataTour}\n className={`border border-border rounded-lg bg-card p-6 ${className}`}\n >\n <div className=\"flex flex-col items-center text-center py-4\">\n <div className=\"size-16 rounded-full bg-gradient-to-br from-primary/20 to-primary/5 flex items-center justify-center mb-4\">\n <Zap className=\"size-8 text-primary\" />\n </div>\n <h3 className=\"text-lg font-semibold text-foreground mb-2\">\n {tr('billing.overview.noActiveSubscription', 'No Active Subscription')}\n </h3>\n <p className=\"text-sm text-muted-foreground mb-6 max-w-sm\">\n Subscribe to a plan to unlock premium features and get the most out of your account\n </p>\n {onPurchasePlan && (\n <button\n type=\"button\"\n onClick={onPurchasePlan}\n className=\"inline-flex items-center gap-2 px-6 py-2.5 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Browse Plans\n <ArrowRight className=\"size-4\" />\n </button>\n )}\n </div>\n </section>\n );\n }\n\n const plan = subscription.plan;\n\n return (\n <section\n data-tour={dataTour}\n className={`border border-border rounded-lg bg-card overflow-hidden ${className}`}\n >\n {/* Expiring Soon Banner */}\n {isExpiringSoon && (\n <div className={`px-4 py-2 ${alertTokens.warning.container} border-b`}>\n <div className={`flex items-center gap-2 ${alertTokens.warning.text}`}>\n <AlertTriangle className=\"size-4\" />\n <span className=\"text-sm font-medium\">\n Your subscription expires in {daysUntilEnd} day{daysUntilEnd !== 1 ? 's' : ''}\n </span>\n </div>\n </div>\n )}\n\n {/* Header */}\n <header className=\"p-4 sm:p-6 border-b border-border\">\n <div className=\"flex flex-col sm:flex-row sm:items-start sm:justify-between gap-4\">\n <div className=\"flex items-start gap-4\">\n <div className=\"size-12 rounded-lg bg-gradient-to-br from-primary to-primary/60 flex items-center justify-center flex-shrink-0\">\n <Crown className=\"size-6 text-primary-foreground\" />\n </div>\n <div>\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"text-lg font-semibold text-foreground\">\n {plan?.name || 'Subscription'}\n </h3>\n <span\n className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${getStatusColor()}`}\n >\n {getStatusText()}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {plan && formatCurrency(plan.price, plan.currency)} / {plan?.duration || 'month'}\n </p>\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n {onManage && (\n <button\n type=\"button\"\n onClick={onManage}\n className=\"px-4 py-2 text-sm font-medium text-foreground border border-border rounded-lg hover:bg-muted transition-colors\"\n >\n Manage\n </button>\n )}\n {onUpgrade && !isExpired && (\n <button\n type=\"button\"\n onClick={onUpgrade}\n className=\"px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n Upgrade\n </button>\n )}\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 sm:p-6\">\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n {/* Start Date */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Started\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n <Calendar className=\"size-4 text-muted-foreground\" />\n {formatDate(subscription.startDate)}\n </p>\n </div>\n\n {/* End Date */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n {isExpired ? 'Expired' : 'Ends'}\n </p>\n <p\n className={`text-sm font-medium flex items-center gap-2 ${isExpired ? statusTokens.error.text : isExpiringSoon ? statusTokens.warning.text : 'text-foreground'}`}\n >\n <Clock className=\"size-4\" />\n {formatDate(subscription.endDate)}\n </p>\n </div>\n\n {/* Auto Renewal */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Auto Renewal\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n <RefreshCw\n className={`size-4 ${subscription.autoRenewal ? statusTokens.success.icon : 'text-muted-foreground'}`}\n />\n {subscription.autoRenewal ? 'Enabled' : 'Disabled'}\n </p>\n </div>\n\n {/* Payment Gateway */}\n <div className=\"space-y-1.5\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Payment Via\n </p>\n <p className=\"text-sm font-medium text-foreground flex items-center gap-2\">\n {subscription.paymentGateway === 'CREDITS' ? (\n <Coins className={`size-4 ${statusTokens.warning.icon}`} />\n ) : (\n <CreditCard className=\"size-4 text-muted-foreground\" />\n )}\n {subscription.paymentGateway === 'CREDITS'\n ? 'Credits'\n : subscription.paymentGateway === 'STRIPE'\n ? 'Stripe'\n : subscription.paymentGateway === 'RAZORPAY'\n ? 'Razorpay'\n : subscription.paymentGateway || 'N/A'}\n </p>\n </div>\n </div>\n\n {/* Next Billing */}\n {subscription.nextBillingDate && subscription.autoRenewal && !isExpired && (\n <div className=\"mt-6 p-4 rounded-lg bg-muted/50 border border-border\">\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {tr('billing.overview.nextBillingDate', 'Next billing date')}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n You&apos;ll be charged automatically\n </p>\n </div>\n <p className=\"text-sm font-semibold text-foreground\">\n {formatDate(subscription.nextBillingDate)}\n </p>\n </div>\n </div>\n )}\n </div>\n\n {/* Footer */}\n {onViewDetails && (\n <footer className=\"px-4 sm:px-6 py-3 border-t border-border bg-muted/30\">\n <button\n type=\"button\"\n onClick={onViewDetails}\n className=\"w-full flex items-center justify-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n View Subscription Details\n <ArrowRight className=\"size-4\" />\n </button>\n </footer>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
@@ -4,21 +4,22 @@ import { Building2 as n, Calendar as r, Check as i, ChevronRight as a, Coins as
4
4
  import { jsx as u, jsxs as d } from "react/jsx-runtime";
5
5
  import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
6
6
  //#region src/billing/modules/dashboard/components/BillingAccountCard.tsx
7
- var p = ({ account: p, isDefault: m = !1, isSelected: h = !1, onSelect: g, onEdit: _, onViewTransactions: v, onViewCreditTransactions: y, className: b = "" }) => {
8
- let { t: x } = f(), S = (e, t) => {
9
- let n = x(e);
7
+ var p = ({ account: p, isDefault: m = !1, isSelected: h = !1, onSelect: g, onEdit: _, onViewTransactions: v, onViewCreditTransactions: y, className: b = "", dataTour: x }) => {
8
+ let { t: S } = f(), C = (e, t) => {
9
+ let n = S(e);
10
10
  return n === e ? t : n;
11
- }, [C, w] = t(!1);
11
+ }, [w, T] = t(!1);
12
12
  return /* @__PURE__ */ d("article", {
13
+ "data-tour": x,
13
14
  className: `
14
15
  relative border rounded-lg bg-card transition-all duration-200
15
16
  ${h ? "border-primary ring-2 ring-primary/20" : "border-border"}
16
- ${C ? "shadow-md" : "shadow-sm"}
17
+ ${w ? "shadow-md" : "shadow-sm"}
17
18
  ${g ? "cursor-pointer" : ""}
18
19
  ${b}
19
20
  `,
20
- onMouseEnter: () => w(!0),
21
- onMouseLeave: () => w(!1),
21
+ onMouseEnter: () => T(!0),
22
+ onMouseLeave: () => T(!1),
22
23
  onClick: g,
23
24
  role: g ? "button" : "article",
24
25
  tabIndex: g ? 0 : void 0,
@@ -46,7 +47,7 @@ var p = ({ account: p, isDefault: m = !1, isSelected: h = !1, onSelect: g, onEdi
46
47
  }),
47
48
  m && /* @__PURE__ */ u("span", {
48
49
  className: "inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-primary/10 text-primary",
49
- children: S("billing.overview.default", "Default")
50
+ children: C("billing.overview.default", "Default")
50
51
  }),
51
52
  h && /* @__PURE__ */ u("span", {
52
53
  className: "inline-flex items-center justify-center size-5 rounded-full bg-primary",
@@ -1 +1 @@
1
- {"version":3,"file":"BillingAccountCard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/BillingAccountCard.tsx"],"sourcesContent":["/**\n * Billing Account Card Component\n * Displays billing account details with actions\n */\n\nimport { type FC, useState } from 'react';\nimport {\n Building2,\n Mail,\n CreditCard,\n Edit2,\n ChevronRight,\n Coins,\n Check,\n Calendar,\n} from 'lucide-react';\nimport type { BillingAccount } from '../../../shared/types';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface BillingAccountCardProps {\n account: BillingAccount;\n isDefault?: boolean;\n isSelected?: boolean;\n onSelect?: () => void;\n onEdit?: () => void;\n onViewTransactions?: () => void;\n onViewCreditTransactions?: () => void;\n className?: string;\n}\n\nexport const BillingAccountCard: FC<BillingAccountCardProps> = ({\n account,\n isDefault = false,\n isSelected = false,\n onSelect,\n onEdit,\n onViewTransactions,\n onViewCreditTransactions,\n className = '',\n}) => {\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 [isHovered, setIsHovered] = useState(false);\n\n const formatDate = (dateString: string) => {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n };\n\n return (\n <article\n className={`\n relative border rounded-lg bg-card transition-all duration-200\n ${isSelected ? 'border-primary ring-2 ring-primary/20' : 'border-border'}\n ${isHovered ? 'shadow-md' : 'shadow-sm'}\n ${onSelect ? 'cursor-pointer' : ''}\n ${className}\n `}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onClick={onSelect}\n role={onSelect ? 'button' : 'article'}\n tabIndex={onSelect ? 0 : undefined}\n onKeyDown={(e) => {\n if (onSelect && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onSelect();\n }\n }}\n >\n {/* Header */}\n <header className=\"p-4 border-b border-border\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <Building2 className=\"size-5 text-primary\" />\n </div>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-foreground truncate\">{account.name}</h3>\n {isDefault && (\n <span className=\"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-primary/10 text-primary\">\n {tr('billing.overview.default', 'Default')}\n </span>\n )}\n {isSelected && (\n <span className=\"inline-flex items-center justify-center size-5 rounded-full bg-primary\">\n <Check className=\"size-3 text-primary-foreground\" />\n </span>\n )}\n </div>\n <p className=\"text-sm text-muted-foreground truncate flex items-center gap-1\">\n <Mail className=\"size-3.5\" />\n {account.email}\n </p>\n </div>\n </div>\n {onEdit && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onEdit();\n }}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n aria-label=\"Edit billing account\"\n >\n <Edit2 className=\"size-4\" />\n </button>\n )}\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 space-y-4\">\n {/* Stats Grid */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Credit Balance\n </p>\n <p className=\"text-lg font-semibold text-foreground flex items-center gap-1.5\">\n <Coins className={`size-4 ${statusTokens.warning.icon}`} />\n {(account.creditAmount || 0).toLocaleString()} Credits\n </p>\n </div>\n <div className=\"space-y-1\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Payment Currency\n </p>\n <p className=\"text-lg font-semibold text-foreground flex items-center gap-1.5\">\n <CreditCard className=\"size-4 text-primary\" />\n {account.currency || 'USD'}\n </p>\n </div>\n </div>\n\n {/* Meta Info */}\n <div className=\"pt-3 border-t border-border\">\n <p className=\"text-xs text-muted-foreground flex items-center gap-1\">\n <Calendar className=\"size-3.5\" />\n Created {formatDate(account.createdAt)}\n </p>\n </div>\n </div>\n\n {/* Footer Actions */}\n {(onViewTransactions || onViewCreditTransactions) && (\n <footer className=\"px-4 py-3 border-t border-border bg-muted/30 rounded-b-lg\">\n <div className=\"flex items-center gap-2\">\n {onViewTransactions && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onViewTransactions();\n }}\n className=\"flex-1 flex items-center justify-center gap-1.5 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n Transactions\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n {onViewCreditTransactions && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onViewCreditTransactions();\n }}\n className=\"flex-1 flex items-center justify-center gap-1.5 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n Credit History\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n </div>\n </footer>\n )}\n\n {/* Status indicator */}\n {!account.isActive && (\n <div className=\"absolute inset-0 bg-background/60 backdrop-blur-sm rounded-lg flex items-center justify-center\">\n <span className=\"px-3 py-1.5 rounded-full bg-destructive/10 text-destructive text-sm font-medium\">\n Inactive\n </span>\n </div>\n )}\n </article>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"BillingAccountCard.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/BillingAccountCard.tsx"],"sourcesContent":["/**\n * Billing Account Card Component\n * Displays billing account details with actions\n */\n\nimport { type FC, useState } from 'react';\nimport {\n Building2,\n Mail,\n CreditCard,\n Edit2,\n ChevronRight,\n Coins,\n Check,\n Calendar,\n} from 'lucide-react';\nimport type { BillingAccount } from '../../../shared/types';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface BillingAccountCardProps {\n account: BillingAccount;\n isDefault?: boolean;\n isSelected?: boolean;\n onSelect?: () => void;\n onEdit?: () => void;\n onViewTransactions?: () => void;\n onViewCreditTransactions?: () => void;\n className?: string;\n /** Optional tour anchor applied to the card root (for onboarding tours). */\n dataTour?: string;\n}\n\nexport const BillingAccountCard: FC<BillingAccountCardProps> = ({\n account,\n isDefault = false,\n isSelected = false,\n onSelect,\n onEdit,\n onViewTransactions,\n onViewCreditTransactions,\n className = '',\n dataTour,\n}) => {\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 [isHovered, setIsHovered] = useState(false);\n\n const formatDate = (dateString: string) => {\n return new Date(dateString).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'short',\n day: 'numeric',\n });\n };\n\n return (\n <article\n data-tour={dataTour}\n className={`\n relative border rounded-lg bg-card transition-all duration-200\n ${isSelected ? 'border-primary ring-2 ring-primary/20' : 'border-border'}\n ${isHovered ? 'shadow-md' : 'shadow-sm'}\n ${onSelect ? 'cursor-pointer' : ''}\n ${className}\n `}\n onMouseEnter={() => setIsHovered(true)}\n onMouseLeave={() => setIsHovered(false)}\n onClick={onSelect}\n role={onSelect ? 'button' : 'article'}\n tabIndex={onSelect ? 0 : undefined}\n onKeyDown={(e) => {\n if (onSelect && (e.key === 'Enter' || e.key === ' ')) {\n e.preventDefault();\n onSelect();\n }\n }}\n >\n {/* Header */}\n <header className=\"p-4 border-b border-border\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <div className=\"size-10 rounded-lg bg-primary/10 flex items-center justify-center flex-shrink-0\">\n <Building2 className=\"size-5 text-primary\" />\n </div>\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"font-semibold text-foreground truncate\">{account.name}</h3>\n {isDefault && (\n <span className=\"inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-primary/10 text-primary\">\n {tr('billing.overview.default', 'Default')}\n </span>\n )}\n {isSelected && (\n <span className=\"inline-flex items-center justify-center size-5 rounded-full bg-primary\">\n <Check className=\"size-3 text-primary-foreground\" />\n </span>\n )}\n </div>\n <p className=\"text-sm text-muted-foreground truncate flex items-center gap-1\">\n <Mail className=\"size-3.5\" />\n {account.email}\n </p>\n </div>\n </div>\n {onEdit && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onEdit();\n }}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n aria-label=\"Edit billing account\"\n >\n <Edit2 className=\"size-4\" />\n </button>\n )}\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 space-y-4\">\n {/* Stats Grid */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div className=\"space-y-1\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Credit Balance\n </p>\n <p className=\"text-lg font-semibold text-foreground flex items-center gap-1.5\">\n <Coins className={`size-4 ${statusTokens.warning.icon}`} />\n {(account.creditAmount || 0).toLocaleString()} Credits\n </p>\n </div>\n <div className=\"space-y-1\">\n <p className=\"text-xs font-medium text-muted-foreground uppercase tracking-wide\">\n Payment Currency\n </p>\n <p className=\"text-lg font-semibold text-foreground flex items-center gap-1.5\">\n <CreditCard className=\"size-4 text-primary\" />\n {account.currency || 'USD'}\n </p>\n </div>\n </div>\n\n {/* Meta Info */}\n <div className=\"pt-3 border-t border-border\">\n <p className=\"text-xs text-muted-foreground flex items-center gap-1\">\n <Calendar className=\"size-3.5\" />\n Created {formatDate(account.createdAt)}\n </p>\n </div>\n </div>\n\n {/* Footer Actions */}\n {(onViewTransactions || onViewCreditTransactions) && (\n <footer className=\"px-4 py-3 border-t border-border bg-muted/30 rounded-b-lg\">\n <div className=\"flex items-center gap-2\">\n {onViewTransactions && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onViewTransactions();\n }}\n className=\"flex-1 flex items-center justify-center gap-1.5 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n Transactions\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n {onViewCreditTransactions && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n onViewCreditTransactions();\n }}\n className=\"flex-1 flex items-center justify-center gap-1.5 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n Credit History\n <ChevronRight className=\"size-4\" />\n </button>\n )}\n </div>\n </footer>\n )}\n\n {/* Status indicator */}\n {!account.isActive && (\n <div className=\"absolute inset-0 bg-background/60 backdrop-blur-sm rounded-lg flex items-center justify-center\">\n <span className=\"px-3 py-1.5 rounded-full bg-destructive/10 text-destructive text-sm font-medium\">\n Inactive\n </span>\n </div>\n )}\n </article>\n );\n};\n"],"mappings":"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"}
@@ -164,18 +164,19 @@ var h = ({ brand: e, className: t }) => {
164
164
  })]
165
165
  })]
166
166
  });
167
- }, _ = ({ paymentMethods: e, billingAccountId: t, onAddPaymentMethod: n, onSetDefault: r, onDelete: i, isLoading: s = !1, className: l = "" }) => {
168
- let { t: u } = m(), d = (e, t) => {
169
- let n = u(e);
167
+ }, _ = ({ paymentMethods: e, billingAccountId: t, onAddPaymentMethod: n, onSetDefault: r, onDelete: i, isLoading: s = !1, className: l = "", dataTour: u }) => {
168
+ let { t: d } = m(), h = (e, t) => {
169
+ let n = d(e);
170
170
  return n === e ? t : n;
171
- }, h = e.length > 0;
171
+ }, _ = e.length > 0;
172
172
  return /* @__PURE__ */ p("section", {
173
173
  className: `space-y-4 ${l}`,
174
+ "data-tour": u,
174
175
  children: [/* @__PURE__ */ p("header", {
175
176
  className: "flex items-center justify-between",
176
177
  children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h3", {
177
178
  className: "text-lg font-semibold text-foreground",
178
- children: d("billing.overview.paymentMethods", "Payment Methods")
179
+ children: h("billing.overview.paymentMethods", "Payment Methods")
179
180
  }), /* @__PURE__ */ f("p", {
180
181
  className: "text-sm text-muted-foreground",
181
182
  children: "Manage your payment methods for subscriptions and purchases"
@@ -188,7 +189,7 @@ var h = ({ brand: e, className: t }) => {
188
189
  }), s ? /* @__PURE__ */ f("div", {
189
190
  className: "flex items-center justify-center py-8",
190
191
  children: /* @__PURE__ */ f(o, { className: "size-6 animate-spin text-muted-foreground" })
191
- }) : h ? /* @__PURE__ */ f("div", {
192
+ }) : _ ? /* @__PURE__ */ f("div", {
192
193
  className: "space-y-3 overflow-visible",
193
194
  children: e.filter((e) => e != null).map((e) => /* @__PURE__ */ f(g, {
194
195
  method: e,
@@ -1 +1 @@
1
- {"version":3,"file":"PaymentMethodsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/PaymentMethodsSection.tsx"],"sourcesContent":["/**\n * Payment Methods Section Component\n * Displays and manages payment methods\n * Note: Provider config is fetched in the modal, not here\n */\n\nimport { type FC, useState } from 'react';\nimport { CreditCard, Plus, MoreVertical, Trash2, Star, Loader2, CheckCircle2 } from 'lucide-react';\nimport type { PaymentMethod, PaymentMethodType } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\ninterface PaymentMethodsSectionProps {\n paymentMethods: PaymentMethod[];\n billingAccountId: string;\n onAddPaymentMethod: () => void;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n isLoading?: boolean;\n className?: string;\n}\n\n// Card brand icons mapping\nconst CardBrandIcon: FC<{ brand?: string; className?: string }> = ({ brand, className }) => {\n const brandLower = brand?.toLowerCase() || '';\n\n // In a real app, you'd use actual brand SVGs/images\n const brandColors: Record<string, string> = {\n visa: 'text-accent-blue',\n mastercard: 'text-accent-orange',\n amex: 'text-accent-blue',\n discover: 'text-accent-orange',\n default: 'text-muted-foreground',\n };\n\n const color = brandColors[brandLower] || brandColors.default;\n\n return (\n <div className={`w-10 h-6 rounded bg-muted flex items-center justify-center ${className}`}>\n <CreditCard className={`size-5 ${color}`} />\n </div>\n );\n};\n\nconst PaymentMethodCard: FC<{\n method: PaymentMethod;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n}> = ({ method, onSetDefault, onDelete }) => {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [isSettingDefault, setIsSettingDefault] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);\n\n const handleSetDefault = async () => {\n setIsMenuOpen(false);\n setIsSettingDefault(true);\n try {\n await onSetDefault(method.id);\n } finally {\n setIsSettingDefault(false);\n }\n };\n\n const handleDeleteRequest = async () => {\n setIsMenuOpen(false);\n void nativeImpact('medium');\n const confirmed = await nativeConfirm({\n title: 'Delete payment method',\n message: 'Are you sure you want to remove this payment method?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n if (confirmed === true) {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n return;\n }\n if (confirmed === null) {\n setIsConfirmingDelete(true);\n }\n };\n\n const handleDelete = async () => {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setIsConfirmingDelete(false);\n }\n };\n\n const getMethodTypeLabel = (type: PaymentMethodType) => {\n const labels: Record<PaymentMethodType, string> = {\n CREDIT_CARD: 'Credit Card',\n DEBIT_CARD: 'Debit Card',\n BANK_TRANSFER: 'UPI',\n PAYPAL: 'PayPal',\n CRYPTO: 'Crypto',\n WALLET: 'Wallet',\n NETBANKING: 'Netbanking',\n };\n return labels[type] || type;\n };\n\n const getExpiryText = () => {\n if (method.expiryMonth && method.expiryYear) {\n const monthStr = String(method.expiryMonth).padStart(2, '0');\n const yearStr = String(method.expiryYear).slice(-2);\n return `${monthStr}/${yearStr}`;\n }\n return null;\n };\n\n const isExpired = () => {\n if (!method.expiryMonth || !method.expiryYear) return false;\n const now = new Date();\n const expiry = new Date(method.expiryYear, method.expiryMonth, 0);\n return expiry < now;\n };\n\n const getProviderBadge = () => {\n if (method.stripePaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-accent-violet-subtle text-accent-violet\">\n Stripe\n </span>\n );\n }\n if (method.razorpayPaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-status-info-bg-subtle text-status-info-text\">\n Razorpay\n </span>\n );\n }\n return null;\n };\n\n return (\n <div\n className={`\n relative p-4 border rounded-lg transition-all overflow-visible\n ${method.isDefault ? 'border-primary bg-primary/5' : 'border-border bg-card'}\n ${isDeleting ? 'opacity-50' : ''}\n `}\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <CardBrandIcon brand={method.brand} />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className=\"font-medium text-foreground\">\n {method.brand || getMethodTypeLabel(method.type)} •••• {method.last4 || '****'}\n </p>\n {method.isDefault && (\n <span className=\"inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium bg-primary/10 text-primary\">\n <Star className=\"size-2.5 fill-current\" />\n Default\n </span>\n )}\n {getProviderBadge()}\n </div>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n {getExpiryText() && (\n <>\n <span className={isExpired() ? 'text-destructive' : ''}>\n Expires {getExpiryText()}\n </span>\n {isExpired() && <span className=\"text-destructive text-xs\">(Expired)</span>}\n </>\n )}\n {method.isVerified && (\n <span className=\"flex items-center gap-1 text-status-success-text\">\n <CheckCircle2 className=\"size-3\" />\n Verified\n </span>\n )}\n </div>\n </div>\n </div>\n\n {/* Actions Menu */}\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsMenuOpen(!isMenuOpen)}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n disabled={isSettingDefault || isDeleting}\n aria-label=\"Payment method options\"\n >\n {isSettingDefault || isDeleting ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <MoreVertical className=\"size-4\" />\n )}\n </button>\n\n {isMenuOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsMenuOpen(false)} />\n <div className=\"absolute top-full right-0 mt-1 min-w-[140px] rounded-lg border border-border bg-popover shadow-lg z-20 overflow-hidden whitespace-nowrap\">\n {!method.isDefault && (\n <button\n type=\"button\"\n onClick={handleSetDefault}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-foreground hover:bg-muted transition-colors text-left\"\n >\n <Star className=\"size-4 flex-shrink-0\" />\n Set as default\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleDeleteRequest}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-destructive hover:bg-destructive/10 transition-colors text-left\"\n >\n <Trash2 className=\"size-4 flex-shrink-0\" />\n Remove\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n\n {isConfirmingDelete && (\n <div className=\"mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3\">\n <p className=\"text-sm font-medium text-status-error-text\">\n Remove this payment method from your billing account?\n </p>\n <div className=\"mt-3 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsConfirmingDelete(false)}\n className=\"rounded-lg border border-border bg-background px-3 py-2 text-sm font-medium text-foreground transition-colors hover:bg-muted\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"rounded-lg bg-destructive px-3 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-destructive/90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isDeleting ? 'Removing…' : 'Remove'}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport const PaymentMethodsSection: FC<PaymentMethodsSectionProps> = ({\n paymentMethods,\n billingAccountId: _billingAccountId,\n onAddPaymentMethod,\n onSetDefault,\n onDelete,\n isLoading = false,\n className = '',\n}) => {\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 // billingAccountId is available for future use\n void _billingAccountId;\n\n const hasPaymentMethods = paymentMethods.length > 0;\n\n return (\n <section className={`space-y-4 ${className}`}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.overview.paymentMethods', 'Payment Methods')}\n </h3>\n <p className=\"text-sm text-muted-foreground\">\n Manage your payment methods for subscriptions and purchases\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onAddPaymentMethod}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"size-4\" />\n Add Method\n </button>\n </header>\n\n {/* Content */}\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n </div>\n ) : hasPaymentMethods ? (\n <div className=\"space-y-3 overflow-visible\">\n {paymentMethods\n .filter((method): method is PaymentMethod => method !== null && method !== undefined)\n .map((method) => (\n <PaymentMethodCard\n key={method.id}\n method={method}\n onSetDefault={onSetDefault}\n onDelete={onDelete}\n />\n ))}\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center py-8 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <CreditCard className=\"size-6 text-muted-foreground\" />\n </div>\n <h4 className=\"text-base font-medium text-foreground\">No payment methods</h4>\n <p className=\"text-sm text-muted-foreground mt-1 text-center max-w-sm\">\n Add a payment method to enable automatic payments for your subscriptions\n </p>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PaymentMethodsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/PaymentMethodsSection.tsx"],"sourcesContent":["/**\n * Payment Methods Section Component\n * Displays and manages payment methods\n * Note: Provider config is fetched in the modal, not here\n */\n\nimport { type FC, useState } from 'react';\nimport { CreditCard, Plus, MoreVertical, Trash2, Star, Loader2, CheckCircle2 } from 'lucide-react';\nimport type { PaymentMethod, PaymentMethodType } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { nativeConfirm, nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\ninterface PaymentMethodsSectionProps {\n paymentMethods: PaymentMethod[];\n billingAccountId: string;\n onAddPaymentMethod: () => void;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\n// Card brand icons mapping\nconst CardBrandIcon: FC<{ brand?: string; className?: string }> = ({ brand, className }) => {\n const brandLower = brand?.toLowerCase() || '';\n\n // In a real app, you'd use actual brand SVGs/images\n const brandColors: Record<string, string> = {\n visa: 'text-accent-blue',\n mastercard: 'text-accent-orange',\n amex: 'text-accent-blue',\n discover: 'text-accent-orange',\n default: 'text-muted-foreground',\n };\n\n const color = brandColors[brandLower] || brandColors.default;\n\n return (\n <div className={`w-10 h-6 rounded bg-muted flex items-center justify-center ${className}`}>\n <CreditCard className={`size-5 ${color}`} />\n </div>\n );\n};\n\nconst PaymentMethodCard: FC<{\n method: PaymentMethod;\n onSetDefault: (id: string) => Promise<void>;\n onDelete: (id: string) => Promise<void>;\n}> = ({ method, onSetDefault, onDelete }) => {\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [isSettingDefault, setIsSettingDefault] = useState(false);\n const [isDeleting, setIsDeleting] = useState(false);\n const [isConfirmingDelete, setIsConfirmingDelete] = useState(false);\n\n const handleSetDefault = async () => {\n setIsMenuOpen(false);\n setIsSettingDefault(true);\n try {\n await onSetDefault(method.id);\n } finally {\n setIsSettingDefault(false);\n }\n };\n\n const handleDeleteRequest = async () => {\n setIsMenuOpen(false);\n void nativeImpact('medium');\n const confirmed = await nativeConfirm({\n title: 'Delete payment method',\n message: 'Are you sure you want to remove this payment method?',\n okButtonTitle: 'Delete',\n cancelButtonTitle: 'Cancel',\n });\n if (confirmed === true) {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n }\n return;\n }\n if (confirmed === null) {\n setIsConfirmingDelete(true);\n }\n };\n\n const handleDelete = async () => {\n setIsDeleting(true);\n try {\n await onDelete(method.id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n } finally {\n setIsDeleting(false);\n setIsConfirmingDelete(false);\n }\n };\n\n const getMethodTypeLabel = (type: PaymentMethodType) => {\n const labels: Record<PaymentMethodType, string> = {\n CREDIT_CARD: 'Credit Card',\n DEBIT_CARD: 'Debit Card',\n BANK_TRANSFER: 'UPI',\n PAYPAL: 'PayPal',\n CRYPTO: 'Crypto',\n WALLET: 'Wallet',\n NETBANKING: 'Netbanking',\n };\n return labels[type] || type;\n };\n\n const getExpiryText = () => {\n if (method.expiryMonth && method.expiryYear) {\n const monthStr = String(method.expiryMonth).padStart(2, '0');\n const yearStr = String(method.expiryYear).slice(-2);\n return `${monthStr}/${yearStr}`;\n }\n return null;\n };\n\n const isExpired = () => {\n if (!method.expiryMonth || !method.expiryYear) return false;\n const now = new Date();\n const expiry = new Date(method.expiryYear, method.expiryMonth, 0);\n return expiry < now;\n };\n\n const getProviderBadge = () => {\n if (method.stripePaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-accent-violet-subtle text-accent-violet\">\n Stripe\n </span>\n );\n }\n if (method.razorpayPaymentMethodId) {\n return (\n <span className=\"inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-status-info-bg-subtle text-status-info-text\">\n Razorpay\n </span>\n );\n }\n return null;\n };\n\n return (\n <div\n className={`\n relative p-4 border rounded-lg transition-all overflow-visible\n ${method.isDefault ? 'border-primary bg-primary/5' : 'border-border bg-card'}\n ${isDeleting ? 'opacity-50' : ''}\n `}\n >\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <CardBrandIcon brand={method.brand} />\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <p className=\"font-medium text-foreground\">\n {method.brand || getMethodTypeLabel(method.type)} •••• {method.last4 || '****'}\n </p>\n {method.isDefault && (\n <span className=\"inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium bg-primary/10 text-primary\">\n <Star className=\"size-2.5 fill-current\" />\n Default\n </span>\n )}\n {getProviderBadge()}\n </div>\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n {getExpiryText() && (\n <>\n <span className={isExpired() ? 'text-destructive' : ''}>\n Expires {getExpiryText()}\n </span>\n {isExpired() && <span className=\"text-destructive text-xs\">(Expired)</span>}\n </>\n )}\n {method.isVerified && (\n <span className=\"flex items-center gap-1 text-status-success-text\">\n <CheckCircle2 className=\"size-3\" />\n Verified\n </span>\n )}\n </div>\n </div>\n </div>\n\n {/* Actions Menu */}\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setIsMenuOpen(!isMenuOpen)}\n className=\"p-2 rounded-lg hover:bg-muted text-muted-foreground hover:text-foreground transition-colors\"\n disabled={isSettingDefault || isDeleting}\n aria-label=\"Payment method options\"\n >\n {isSettingDefault || isDeleting ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <MoreVertical className=\"size-4\" />\n )}\n </button>\n\n {isMenuOpen && (\n <>\n <div className=\"fixed inset-0 z-10\" onClick={() => setIsMenuOpen(false)} />\n <div className=\"absolute top-full right-0 mt-1 min-w-[140px] rounded-lg border border-border bg-popover shadow-lg z-20 overflow-hidden whitespace-nowrap\">\n {!method.isDefault && (\n <button\n type=\"button\"\n onClick={handleSetDefault}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-foreground hover:bg-muted transition-colors text-left\"\n >\n <Star className=\"size-4 flex-shrink-0\" />\n Set as default\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleDeleteRequest}\n className=\"w-full flex items-center gap-2 px-3 py-2.5 text-sm text-destructive hover:bg-destructive/10 transition-colors text-left\"\n >\n <Trash2 className=\"size-4 flex-shrink-0\" />\n Remove\n </button>\n </div>\n </>\n )}\n </div>\n </div>\n\n {isConfirmingDelete && (\n <div className=\"mt-4 rounded-lg border border-status-error-border bg-status-error-bg-subtle p-3\">\n <p className=\"text-sm font-medium text-status-error-text\">\n Remove this payment method from your billing account?\n </p>\n <div className=\"mt-3 flex justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsConfirmingDelete(false)}\n className=\"rounded-lg border border-border bg-background px-3 py-2 text-sm font-medium text-foreground transition-colors hover:bg-muted\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"rounded-lg bg-destructive px-3 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-destructive/90 disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {isDeleting ? 'Removing…' : 'Remove'}\n </button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport const PaymentMethodsSection: FC<PaymentMethodsSectionProps> = ({\n paymentMethods,\n billingAccountId: _billingAccountId,\n onAddPaymentMethod,\n onSetDefault,\n onDelete,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\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 // billingAccountId is available for future use\n void _billingAccountId;\n\n const hasPaymentMethods = paymentMethods.length > 0;\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div>\n <h3 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.overview.paymentMethods', 'Payment Methods')}\n </h3>\n <p className=\"text-sm text-muted-foreground\">\n Manage your payment methods for subscriptions and purchases\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onAddPaymentMethod}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"size-4\" />\n Add Method\n </button>\n </header>\n\n {/* Content */}\n {isLoading ? (\n <div className=\"flex items-center justify-center py-8\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n </div>\n ) : hasPaymentMethods ? (\n <div className=\"space-y-3 overflow-visible\">\n {paymentMethods\n .filter((method): method is PaymentMethod => method !== null && method !== undefined)\n .map((method) => (\n <PaymentMethodCard\n key={method.id}\n method={method}\n onSetDefault={onSetDefault}\n onDelete={onDelete}\n />\n ))}\n </div>\n ) : (\n <div className=\"flex flex-col items-center justify-center py-8 border border-dashed border-border rounded-lg\">\n <div className=\"size-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <CreditCard className=\"size-6 text-muted-foreground\" />\n </div>\n <h4 className=\"text-base font-medium text-foreground\">No payment methods</h4>\n <p className=\"text-sm text-muted-foreground mt-1 text-center max-w-sm\">\n Add a payment method to enable automatic payments for your subscriptions\n </p>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
@@ -124,18 +124,19 @@ var _ = ({ addon: n, onView: i, onPurchase: a, onAddToCart: u, isInCart: h = !1,
124
124
  })
125
125
  ]
126
126
  });
127
- }, v = ({ addons: e, hasSubscription: t, onViewAddon: r, onPurchaseAddon: o, onViewAllAddons: s, onAddToCart: c, isInCart: l, getCartQuantity: d, onUpdateQuantity: f, onRemoveFromCart: g, maxAddons: v = 3, isLoading: y = !1, className: b = "" }) => {
128
- let { t: x } = h(), S = (e, t) => {
129
- let n = x(e);
127
+ }, v = ({ addons: e, hasSubscription: t, onViewAddon: r, onPurchaseAddon: o, onViewAllAddons: s, onAddToCart: c, isInCart: l, getCartQuantity: d, onUpdateQuantity: f, onRemoveFromCart: g, maxAddons: v = 3, isLoading: y = !1, className: b = "", dataTour: x }) => {
128
+ let { t: S } = h(), C = (e, t) => {
129
+ let n = S(e);
130
130
  return n === e ? t : n;
131
131
  };
132
132
  if (!t) return null;
133
- let C = e.slice(0, v);
133
+ let w = e.slice(0, v);
134
134
  return y ? /* @__PURE__ */ m("section", {
135
135
  className: `space-y-4 ${b}`,
136
+ "data-tour": x,
136
137
  children: [/* @__PURE__ */ m("header", { children: [/* @__PURE__ */ p("h3", {
137
138
  className: "text-lg font-semibold text-foreground",
138
- children: S("billing.overview.recommendedAddons", "Recommended Add-ons")
139
+ children: C("billing.overview.recommendedAddons", "Recommended Add-ons")
139
140
  }), /* @__PURE__ */ p("p", {
140
141
  className: "text-sm text-muted-foreground",
141
142
  children: "Enhance your subscription with these popular add-ons"
@@ -143,8 +144,9 @@ var _ = ({ addon: n, onView: i, onPurchase: a, onAddToCart: u, isInCart: h = !1,
143
144
  className: "flex items-center justify-center py-12",
144
145
  children: /* @__PURE__ */ p(a, { className: "size-6 animate-spin text-muted-foreground" })
145
146
  })]
146
- }) : C.length === 0 ? null : /* @__PURE__ */ m("section", {
147
+ }) : w.length === 0 ? null : /* @__PURE__ */ m("section", {
147
148
  className: `space-y-4 ${b}`,
149
+ "data-tour": x,
148
150
  children: [
149
151
  /* @__PURE__ */ m("header", {
150
152
  className: "flex items-center justify-between",
@@ -166,7 +168,7 @@ var _ = ({ addon: n, onView: i, onPurchase: a, onAddToCart: u, isInCart: h = !1,
166
168
  }),
167
169
  /* @__PURE__ */ p("div", {
168
170
  className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4",
169
- children: C.map((e) => /* @__PURE__ */ p(_, {
171
+ children: w.map((e) => /* @__PURE__ */ p(_, {
170
172
  addon: e,
171
173
  onView: () => r(e.id),
172
174
  onPurchase: () => o(e.id),
@@ -1 +1 @@
1
- {"version":3,"file":"RecommendedAddonsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/RecommendedAddonsSection.tsx"],"sourcesContent":["/**\n * Recommended Addons Section Component\n * Displays recommended addons that users might want to purchase\n * Supports add to cart functionality\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport {\n Package,\n ArrowRight,\n Sparkles,\n ChevronRight,\n Loader2,\n ShoppingCart,\n Check,\n Plus,\n Minus,\n Trash2,\n} from 'lucide-react';\nimport type { Addon, PlanDuration, SubscriptionFeatures } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n * e.g., 10 bots + 10 bots = 20 bots (shown as one feature)\n */\nfunction combineQuotas(features: SubscriptionFeatures[]): 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 { value?: number } | null;\n const limitValue = limits?.value ?? 0;\n\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n totalValue: limitValue,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\ninterface RecommendedAddonsSectionProps {\n addons: Addon[];\n hasSubscription: boolean;\n onViewAddon: (addonId: string) => void;\n onPurchaseAddon: (addonId: string) => void;\n onViewAllAddons: () => void;\n /** Handler for adding addon to cart */\n onAddToCart?: (addon: Addon) => void;\n /** Check if addon is in cart */\n isInCart?: (addonId: string) => boolean;\n /** Get cart quantity for addon */\n getCartQuantity?: (addonId: string) => number;\n /** Update quantity in cart */\n onUpdateQuantity?: (addonId: string, quantity: number) => void;\n /** Remove from cart */\n onRemoveFromCart?: (addonId: string) => void;\n /** Maximum number of addons to show (default: 3) */\n maxAddons?: number;\n isLoading?: boolean;\n className?: string;\n}\n\nconst AddonCard: FC<{\n addon: Addon;\n onView: () => void;\n onPurchase: () => void;\n onAddToCart?: () => void;\n isInCart?: boolean;\n cartQuantity?: number;\n onUpdateQuantity?: (quantity: number) => void;\n onRemoveFromCart?: () => void;\n}> = ({\n addon,\n onView,\n onPurchase,\n onAddToCart,\n isInCart = false,\n cartQuantity = 0,\n onUpdateQuantity,\n onRemoveFromCart,\n}) => {\n const [justAdded, setJustAdded] = useState(false);\n\n const handleAddToCart = () => {\n if (onAddToCart) {\n onAddToCart();\n setJustAdded(true);\n setTimeout(() => setJustAdded(false), 2000);\n } else {\n onPurchase();\n }\n };\n\n const formatCurrency = (amount: number, currency: string) => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(amount);\n };\n\n const getDurationLabel = (duration: PlanDuration) => {\n return duration === 'yearly' ? 'year' : 'month';\n };\n\n // Combine duplicate quotas (e.g., 10 bots + 10 bots = 20 bots)\n const combinedQuotas = useMemo(() => {\n const features = addon.features || [];\n return combineQuotas(features as SubscriptionFeatures[]);\n }, [addon.features]);\n\n return (\n <article className=\"flex flex-col border border-border rounded-lg bg-card overflow-hidden hover:shadow-md transition-shadow\">\n {/* Header */}\n <header className=\"p-4 border-b border-border\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-status-success-bg-subtle flex items-center justify-center\">\n <Package className=\"size-5 text-status-success-text\" />\n </div>\n <div>\n <h4 className=\"font-semibold text-foreground\">{addon.name}</h4>\n <p className=\"text-sm text-muted-foreground\">\n {formatCurrency(addon.price, addon.currency)}/{getDurationLabel(addon.duration)}\n </p>\n </div>\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 flex-1\">\n {combinedQuotas.length > 0 ? (\n <ul className=\"space-y-2\">\n {combinedQuotas.slice(0, 2).map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"size-1.5 rounded-full bg-status-success-bg\" />\n <span className=\"text-muted-foreground\">{quota.name}</span>\n </div>\n <span className=\"font-medium text-foreground\">\n {quota.totalValue.toLocaleString()}\n </span>\n </li>\n ))}\n {combinedQuotas.length > 2 && (\n <li className=\"text-xs text-muted-foreground\">\n +{combinedQuotas.length - 2} more quotas\n </li>\n )}\n </ul>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n Enhance your subscription with additional features\n </p>\n )}\n </div>\n\n {/* Footer */}\n <footer className=\"p-4 border-t border-border bg-muted/30 flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={onView}\n className=\"flex-1 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors text-center\"\n >\n View Details\n </button>\n {isInCart && onUpdateQuantity ? (\n // Quantity controls when in cart\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={onRemoveFromCart}\n className=\"p-2 text-destructive hover:bg-destructive/10 rounded-lg transition-colors\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity - 1)}\n className=\"p-2 text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-8 text-center font-medium text-foreground\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity + 1)}\n className=\"p-2 text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n ) : (\n // Add to cart button\n <button\n type=\"button\"\n onClick={handleAddToCart}\n disabled={justAdded}\n className={`flex-1 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-center flex items-center justify-center gap-1.5 ${\n justAdded\n ? 'bg-status-success-bg text-primary-foreground hover:bg-status-success-bg/90'\n : 'text-primary-foreground bg-primary hover:bg-primary/90'\n }`}\n >\n {justAdded ? (\n <>\n <Check className=\"size-4\" />\n Added\n </>\n ) : (\n <>\n <ShoppingCart className=\"size-4\" />\n Add to Cart\n </>\n )}\n </button>\n )}\n </footer>\n </article>\n );\n};\n\nexport const RecommendedAddonsSection: FC<RecommendedAddonsSectionProps> = ({\n addons,\n hasSubscription,\n onViewAddon,\n onPurchaseAddon,\n onViewAllAddons,\n onAddToCart,\n isInCart,\n getCartQuantity,\n onUpdateQuantity,\n onRemoveFromCart,\n maxAddons = 3,\n isLoading = false,\n className = '',\n}) => {\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 // Only show if user has a subscription\n if (!hasSubscription) {\n return null;\n }\n\n // Limit addons to maxAddons\n const displayAddons = addons.slice(0, maxAddons);\n\n if (isLoading) {\n return (\n <section className={`space-y-4 ${className}`}>\n <header>\n <h3 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.overview.recommendedAddons', 'Recommended Add-ons')}\n </h3>\n <p className=\"text-sm text-muted-foreground\">\n Enhance your subscription with these popular add-ons\n </p>\n </header>\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n </div>\n </section>\n );\n }\n\n if (displayAddons.length === 0) {\n return null;\n }\n\n return (\n <section className={`space-y-4 ${className}`}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Sparkles className=\"size-5 text-status-warning-text\" />\n <div>\n <h3 className=\"text-lg font-semibold text-foreground\">Recommended Add-ons</h3>\n <p className=\"text-sm text-muted-foreground\">\n Enhance your subscription with these popular add-ons\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-primary hover:text-primary/80 transition-colors\"\n >\n View All\n <ChevronRight className=\"size-4\" />\n </button>\n </header>\n\n {/* Addons Grid - Limited to maxAddons */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {displayAddons.map((addon) => (\n <AddonCard\n key={addon.id}\n addon={addon}\n onView={() => onViewAddon(addon.id)}\n onPurchase={() => onPurchaseAddon(addon.id)}\n onAddToCart={onAddToCart ? () => onAddToCart(addon) : undefined}\n isInCart={isInCart ? isInCart(addon.id) : false}\n cartQuantity={getCartQuantity ? getCartQuantity(addon.id) : 0}\n onUpdateQuantity={\n onUpdateQuantity ? (qty) => onUpdateQuantity(addon.id, qty) : undefined\n }\n onRemoveFromCart={onRemoveFromCart ? () => onRemoveFromCart(addon.id) : undefined}\n />\n ))}\n </div>\n\n {/* See More Link - show if there are more addons */}\n {addons.length > maxAddons && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n View all {addons.length} add-ons\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"RecommendedAddonsSection.js","names":[],"sources":["../../../../../src/billing/modules/dashboard/components/RecommendedAddonsSection.tsx"],"sourcesContent":["/**\n * Recommended Addons Section Component\n * Displays recommended addons that users might want to purchase\n * Supports add to cart functionality\n */\n\nimport { type FC, useState, useMemo } from 'react';\nimport {\n Package,\n ArrowRight,\n Sparkles,\n ChevronRight,\n Loader2,\n ShoppingCart,\n Check,\n Plus,\n Minus,\n Trash2,\n} from 'lucide-react';\nimport type { Addon, PlanDuration, SubscriptionFeatures } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n * e.g., 10 bots + 10 bots = 20 bots (shown as one feature)\n */\nfunction combineQuotas(features: SubscriptionFeatures[]): 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 { value?: number } | null;\n const limitValue = limits?.value ?? 0;\n\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n totalValue: limitValue,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\ninterface RecommendedAddonsSectionProps {\n addons: Addon[];\n hasSubscription: boolean;\n onViewAddon: (addonId: string) => void;\n onPurchaseAddon: (addonId: string) => void;\n onViewAllAddons: () => void;\n /** Handler for adding addon to cart */\n onAddToCart?: (addon: Addon) => void;\n /** Check if addon is in cart */\n isInCart?: (addonId: string) => boolean;\n /** Get cart quantity for addon */\n getCartQuantity?: (addonId: string) => number;\n /** Update quantity in cart */\n onUpdateQuantity?: (addonId: string, quantity: number) => void;\n /** Remove from cart */\n onRemoveFromCart?: (addonId: string) => void;\n /** Maximum number of addons to show (default: 3) */\n maxAddons?: number;\n isLoading?: boolean;\n className?: string;\n /** Optional tour anchor applied to the section root (for onboarding tours). */\n dataTour?: string;\n}\n\nconst AddonCard: FC<{\n addon: Addon;\n onView: () => void;\n onPurchase: () => void;\n onAddToCart?: () => void;\n isInCart?: boolean;\n cartQuantity?: number;\n onUpdateQuantity?: (quantity: number) => void;\n onRemoveFromCart?: () => void;\n}> = ({\n addon,\n onView,\n onPurchase,\n onAddToCart,\n isInCart = false,\n cartQuantity = 0,\n onUpdateQuantity,\n onRemoveFromCart,\n}) => {\n const [justAdded, setJustAdded] = useState(false);\n\n const handleAddToCart = () => {\n if (onAddToCart) {\n onAddToCart();\n setJustAdded(true);\n setTimeout(() => setJustAdded(false), 2000);\n } else {\n onPurchase();\n }\n };\n\n const formatCurrency = (amount: number, currency: string) => {\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: currency || 'USD',\n minimumFractionDigits: 0,\n maximumFractionDigits: 2,\n }).format(amount);\n };\n\n const getDurationLabel = (duration: PlanDuration) => {\n return duration === 'yearly' ? 'year' : 'month';\n };\n\n // Combine duplicate quotas (e.g., 10 bots + 10 bots = 20 bots)\n const combinedQuotas = useMemo(() => {\n const features = addon.features || [];\n return combineQuotas(features as SubscriptionFeatures[]);\n }, [addon.features]);\n\n return (\n <article className=\"flex flex-col border border-border rounded-lg bg-card overflow-hidden hover:shadow-md transition-shadow\">\n {/* Header */}\n <header className=\"p-4 border-b border-border\">\n <div className=\"flex items-start justify-between gap-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"size-10 rounded-lg bg-status-success-bg-subtle flex items-center justify-center\">\n <Package className=\"size-5 text-status-success-text\" />\n </div>\n <div>\n <h4 className=\"font-semibold text-foreground\">{addon.name}</h4>\n <p className=\"text-sm text-muted-foreground\">\n {formatCurrency(addon.price, addon.currency)}/{getDurationLabel(addon.duration)}\n </p>\n </div>\n </div>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"p-4 flex-1\">\n {combinedQuotas.length > 0 ? (\n <ul className=\"space-y-2\">\n {combinedQuotas.slice(0, 2).map((quota) => (\n <li key={quota.quotaId} className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"size-1.5 rounded-full bg-status-success-bg\" />\n <span className=\"text-muted-foreground\">{quota.name}</span>\n </div>\n <span className=\"font-medium text-foreground\">\n {quota.totalValue.toLocaleString()}\n </span>\n </li>\n ))}\n {combinedQuotas.length > 2 && (\n <li className=\"text-xs text-muted-foreground\">\n +{combinedQuotas.length - 2} more quotas\n </li>\n )}\n </ul>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n Enhance your subscription with additional features\n </p>\n )}\n </div>\n\n {/* Footer */}\n <footer className=\"p-4 border-t border-border bg-muted/30 flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={onView}\n className=\"flex-1 px-3 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-muted rounded-lg transition-colors text-center\"\n >\n View Details\n </button>\n {isInCart && onUpdateQuantity ? (\n // Quantity controls when in cart\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={onRemoveFromCart}\n className=\"p-2 text-destructive hover:bg-destructive/10 rounded-lg transition-colors\"\n title=\"Remove from cart\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity - 1)}\n className=\"p-2 text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <Minus className=\"size-4\" />\n </button>\n <span className=\"w-8 text-center font-medium text-foreground\">{cartQuantity}</span>\n <button\n type=\"button\"\n onClick={() => onUpdateQuantity(cartQuantity + 1)}\n className=\"p-2 text-foreground hover:bg-muted rounded-lg transition-colors\"\n >\n <Plus className=\"size-4\" />\n </button>\n </div>\n ) : (\n // Add to cart button\n <button\n type=\"button\"\n onClick={handleAddToCart}\n disabled={justAdded}\n className={`flex-1 px-3 py-2 text-sm font-medium rounded-lg transition-colors text-center flex items-center justify-center gap-1.5 ${\n justAdded\n ? 'bg-status-success-bg text-primary-foreground hover:bg-status-success-bg/90'\n : 'text-primary-foreground bg-primary hover:bg-primary/90'\n }`}\n >\n {justAdded ? (\n <>\n <Check className=\"size-4\" />\n Added\n </>\n ) : (\n <>\n <ShoppingCart className=\"size-4\" />\n Add to Cart\n </>\n )}\n </button>\n )}\n </footer>\n </article>\n );\n};\n\nexport const RecommendedAddonsSection: FC<RecommendedAddonsSectionProps> = ({\n addons,\n hasSubscription,\n onViewAddon,\n onPurchaseAddon,\n onViewAllAddons,\n onAddToCart,\n isInCart,\n getCartQuantity,\n onUpdateQuantity,\n onRemoveFromCart,\n maxAddons = 3,\n isLoading = false,\n className = '',\n dataTour,\n}) => {\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 // Only show if user has a subscription\n if (!hasSubscription) {\n return null;\n }\n\n // Limit addons to maxAddons\n const displayAddons = addons.slice(0, maxAddons);\n\n if (isLoading) {\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n <header>\n <h3 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.overview.recommendedAddons', 'Recommended Add-ons')}\n </h3>\n <p className=\"text-sm text-muted-foreground\">\n Enhance your subscription with these popular add-ons\n </p>\n </header>\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-6 animate-spin text-muted-foreground\" />\n </div>\n </section>\n );\n }\n\n if (displayAddons.length === 0) {\n return null;\n }\n\n return (\n <section className={`space-y-4 ${className}`} data-tour={dataTour}>\n {/* Header */}\n <header className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Sparkles className=\"size-5 text-status-warning-text\" />\n <div>\n <h3 className=\"text-lg font-semibold text-foreground\">Recommended Add-ons</h3>\n <p className=\"text-sm text-muted-foreground\">\n Enhance your subscription with these popular add-ons\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium text-primary hover:text-primary/80 transition-colors\"\n >\n View All\n <ChevronRight className=\"size-4\" />\n </button>\n </header>\n\n {/* Addons Grid - Limited to maxAddons */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n {displayAddons.map((addon) => (\n <AddonCard\n key={addon.id}\n addon={addon}\n onView={() => onViewAddon(addon.id)}\n onPurchase={() => onPurchaseAddon(addon.id)}\n onAddToCart={onAddToCart ? () => onAddToCart(addon) : undefined}\n isInCart={isInCart ? isInCart(addon.id) : false}\n cartQuantity={getCartQuantity ? getCartQuantity(addon.id) : 0}\n onUpdateQuantity={\n onUpdateQuantity ? (qty) => onUpdateQuantity(addon.id, qty) : undefined\n }\n onRemoveFromCart={onRemoveFromCart ? () => onRemoveFromCart(addon.id) : undefined}\n />\n ))}\n </div>\n\n {/* See More Link - show if there are more addons */}\n {addons.length > maxAddons && (\n <div className=\"flex justify-center pt-2\">\n <button\n type=\"button\"\n onClick={onViewAllAddons}\n className=\"inline-flex items-center gap-2 text-sm font-medium text-muted-foreground hover:text-foreground transition-colors\"\n >\n View all {addons.length} add-ons\n <ArrowRight className=\"size-4\" />\n </button>\n </div>\n )}\n </section>\n );\n};\n"],"mappings":"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"}