@burdenoff/microfe-billing 2026.614.1 → 2026.623.2

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 (135) hide show
  1. package/dist/billing/BillingAdminRoutes.js +7 -6
  2. package/dist/billing/BillingAdminRoutes.js.map +1 -1
  3. package/dist/billing/BillingRoot.js +1 -1
  4. package/dist/billing/BillingRoot.js.map +1 -1
  5. package/dist/billing/modules/addons/components/FloatingCartDrawer.js +22 -22
  6. package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
  7. package/dist/billing/modules/addons/pages/AddonCreatePage.js +31 -31
  8. package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
  9. package/dist/billing/modules/addons/pages/AddonDetailPage.js +39 -39
  10. package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
  11. package/dist/billing/modules/addons/pages/AddonEditPage.js +29 -29
  12. package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
  13. package/dist/billing/modules/addons/pages/AddonsListPage.js +91 -91
  14. package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
  15. package/dist/billing/modules/analytics/pages/AnalyticsPage.js +40 -40
  16. package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
  17. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +115 -115
  18. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  19. package/dist/billing/modules/billing/pages/InvoicesListPage.js +75 -72
  20. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  21. package/dist/billing/modules/billing/pages/RefundsPage.js +126 -126
  22. package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
  23. package/dist/billing/modules/billing/pages/UserRefundsPage.js +45 -45
  24. package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
  25. package/dist/billing/modules/checkout/components/BillingAccountSelector.js +25 -25
  26. package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
  27. package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js +31 -35
  28. package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
  29. package/dist/billing/modules/checkout/components/CreditsAmountInput.js +9 -9
  30. package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
  31. package/dist/billing/modules/checkout/components/OrderSummary.js +31 -31
  32. package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
  33. package/dist/billing/modules/checkout/components/PromoCodeInput.js +8 -8
  34. package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
  35. package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js +5 -5
  36. package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
  37. package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js +9 -9
  38. package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
  39. package/dist/billing/modules/checkout/components/StripePaymentForm.js +21 -21
  40. package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
  41. package/dist/billing/modules/checkout/components/SubscriptionSelector.js +11 -11
  42. package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
  43. package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js +31 -31
  44. package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
  45. package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js +23 -23
  46. package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
  47. package/dist/billing/modules/checkout/pages/CheckoutPage.js +91 -91
  48. package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
  49. package/dist/billing/modules/coupons/pages/CouponDetailPage.js +62 -62
  50. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  51. package/dist/billing/modules/coupons/pages/CouponsListPage.js +72 -60
  52. package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
  53. package/dist/billing/modules/coupons/pages/CreateCouponPage.js +66 -66
  54. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  55. package/dist/billing/modules/coupons/pages/EditCouponPage.js +46 -46
  56. package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
  57. package/dist/billing/modules/credits/pages/AdminCreditsPage.js +19 -19
  58. package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
  59. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +37 -37
  60. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
  61. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +90 -90
  62. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
  63. package/dist/billing/modules/credits/pages/CreditsPage.js +41 -41
  64. package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
  65. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +31 -31
  66. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
  67. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +35 -35
  68. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
  69. package/dist/billing/modules/dashboard/components/BillingAccountCard.js +24 -24
  70. package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
  71. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +99 -99
  72. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
  73. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +23 -23
  74. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
  75. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +22 -22
  76. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
  77. package/dist/billing/modules/dashboard/pages/OverviewPage.js +58 -58
  78. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  79. package/dist/billing/modules/discounts/pages/DiscountsListPage.js +126 -126
  80. package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
  81. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +83 -79
  82. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  83. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +16 -16
  84. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  85. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +40 -40
  86. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  87. package/dist/billing/modules/plans/pages/PlanCreatePage.js +35 -35
  88. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  89. package/dist/billing/modules/plans/pages/PlanDetailPage.js +57 -57
  90. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  91. package/dist/billing/modules/plans/pages/PlanEditPage.js +35 -35
  92. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  93. package/dist/billing/modules/plans/pages/PlansBrowsePage.js +57 -57
  94. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  95. package/dist/billing/modules/plans/pages/PlansListPage.js +52 -52
  96. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  97. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +40 -39
  98. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  99. package/dist/billing/modules/promotions/pages/PromotionsListPage.js +22 -19
  100. package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
  101. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +34 -34
  102. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
  103. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +47 -47
  104. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
  105. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +39 -39
  106. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
  107. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +52 -52
  108. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
  109. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +23 -23
  110. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
  111. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +259 -246
  112. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
  113. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +88 -85
  114. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
  115. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +51 -51
  116. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
  117. package/dist/billing/modules/usage/pages/UsagePage.js +206 -196
  118. package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
  119. package/dist/billing/shared/components/ServerError.js +4 -4
  120. package/dist/billing/shared/components/ServerError.js.map +1 -1
  121. package/dist/billing/shared/components/StatCard.js +11 -33
  122. package/dist/billing/shared/components/StatCard.js.map +1 -1
  123. package/dist/billing/shared/ui/Card.js +15 -0
  124. package/dist/billing/shared/ui/Card.js.map +1 -0
  125. package/dist/billing/shared/ui/MetricCard.js +44 -0
  126. package/dist/billing/shared/ui/MetricCard.js.map +1 -0
  127. package/dist/billing/shared/ui/ProgressBar.js +30 -0
  128. package/dist/billing/shared/ui/ProgressBar.js.map +1 -0
  129. package/dist/billing/shared/ui/SectionHeader.js +2 -0
  130. package/dist/billing/shared/ui/StatusPill.js +28 -0
  131. package/dist/billing/shared/ui/StatusPill.js.map +1 -0
  132. package/dist/billing/shared/ui/index.js +5 -0
  133. package/dist/billing/shared/utils/tokens.js +13 -30
  134. package/dist/billing/shared/utils/tokens.js.map +1 -1
  135. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AddonDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/addons/pages/AddonDetailPage.tsx"],"sourcesContent":["/**\n * Addons Module - Addon Detail Page\n * Displays detailed information about a specific addon\n */\n\nimport { useMemo, type FC } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useAddon, useAddonMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n isServerError,\n} from '../../../shared/utils';\nimport type { SubscriptionFeatures } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\nimport {\n useDefaultBillingCurrency,\n getDisplayPrice,\n} from '../../../hooks/useDefaultBillingCurrency';\n\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n type: string;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\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 limitType = (feature.quota.limits as { type?: string } | null)?.type ?? 'unknown';\n const limitValue = getPlanFeatureQuantity(feature);\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 type: limitType,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\nexport const AddonDetailPage: 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 { addonId } = useParams<{ addonId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const displayCurrency = useDefaultBillingCurrency();\n const { addon, isLoading, error, refetch } = useAddon(addonId);\n const { toggleAddon, isToggling } = useAddonMutations();\n\n // Memoize combined quotas before any conditional returns\n const combinedQuotas = useMemo(() => combineQuotas(addon?.features || []), [addon?.features]);\n\n // Permission check\n if (!permissions.canViewAddons) {\n return (\n <AccessDenied\n message={tr(\n 'billing.addons.noViewPermission',\n \"You don't have permission to view addon details.\"\n )}\n />\n );\n }\n\n // Loading state\n if (isLoading) {\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=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-24 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n <div className=\"h-64 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.addons.serverUnavailable', 'Server Unavailable')}\n message={tr(\n 'billing.addons.serverUnavailableMessage',\n 'Unable to load addon details. The server might be down or experiencing issues.'\n )}\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n // Not found state\n if (!addon) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-muted flex items-center justify-center\">\n <svg\n className=\"size-6 text-muted-foreground\"\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=\"M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.addons.addonNotFound', 'Addon not found')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'billing.addons.addonNotFoundMessage',\n \"The addon you're looking for doesn't exist or has been removed.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/addons')}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Back to Addons\n </button>\n </div>\n </div>\n );\n }\n\n const handleToggle = async () => {\n try {\n await toggleAddon(addon.id, !addon.isActive);\n refetch();\n } catch (err) {\n console.error('Failed to toggle addon:', err);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={addon.name}\n description={`${formatPlanDurationLabel(addon.duration)} addon`}\n />\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Pricing Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.addons.pricing', 'Pricing')}\n </h2>\n </div>\n <div className=\"p-6\">\n <div className=\"flex items-baseline gap-2\">\n <span className=\"text-4xl font-bold text-foreground\">\n {(() => {\n const { price, currency } = getDisplayPrice(\n addon.price,\n addon.currency,\n addon.currencyPrices as Record<string, number> | null,\n displayCurrency\n );\n return formatCurrency(price, currency);\n })()}\n </span>\n <span className=\"text-lg text-muted-foreground\">\n / {formatPlanDuration(addon.duration)}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground mt-2\">\n {tr('billing.addons.billed', 'Billed')}{' '}\n {addon.duration === 'monthly'\n ? tr('billing.addons.everyMonth', 'every month')\n : tr('billing.addons.annually', 'annually')}\n </p>\n </div>\n </div>\n\n {/* Quotas Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.addons.includedQuotas', 'Included Quotas')} ({combinedQuotas.length})\n </h2>\n </div>\n <div className=\"p-4\">\n {combinedQuotas.length === 0 ? (\n <p className=\"text-sm text-muted-foreground py-4 text-center\">\n {tr('billing.addons.noQuotas', 'No quotas associated with this addon.')}\n </p>\n ) : (\n <div className=\"space-y-3\">\n {combinedQuotas.map((quota) => (\n <div\n key={quota.quotaId}\n className=\"flex items-center justify-between p-3 border border-border rounded-lg bg-muted/20\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-full bg-primary/10 flex items-center justify-center\">\n <svg\n className=\"size-4 text-primary\"\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=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n <div>\n <p className=\"font-medium text-foreground\">{quota.name}</p>\n <p className=\"text-xs text-muted-foreground capitalize\">\n Type: {quota.type}\n </p>\n </div>\n </div>\n <div className=\"text-right\">\n <span className=\"text-lg font-semibold text-status-success-text\">\n {quota.totalValue.toLocaleString()}\n </span>\n <p className=\"text-xs text-muted-foreground\">total</p>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Status Card */}\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.addons.statusTitle', 'Status')}\n </h2>\n </div>\n <div className=\"p-4 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">\n {tr('billing.addons.status', 'Status')}\n </span>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${\n addon.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-muted text-muted-foreground'\n }`}\n >\n {addon.isActive ? 'Active' : 'Inactive'}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">\n {tr('billing.addons.currency', 'Currency')}\n </span>\n <span className=\"text-sm font-medium text-foreground\">\n {\n getDisplayPrice(\n addon.price,\n addon.currency,\n addon.currencyPrices as Record<string, number> | null,\n displayCurrency\n ).currency\n }\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">\n {tr('billing.addons.billingLabel', 'Billing')}\n </span>\n <span className=\"text-sm font-medium text-foreground capitalize\">\n {addon.duration}\n </span>\n </div>\n </div>\n </div>\n\n {/* Actions Card */}\n {permissions.canManageAddons && (\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">\n {tr('billing.addons.actions', 'Actions')}\n </h2>\n </div>\n <div className=\"p-4 space-y-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`/addons/${addon.id}/edit`)}\n className=\"w-full px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Edit Addon\n </button>\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={isToggling}\n className={`w-full px-4 py-2 text-sm font-medium border border-border rounded-md transition-colors disabled:opacity-50 ${\n addon.isActive\n ? 'text-destructive hover:bg-destructive/10'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {isToggling\n ? tr('billing.addons.processing', 'Processing...')\n : addon.isActive\n ? tr('billing.addons.deactivateAddon', 'Deactivate Addon')\n : tr('billing.addons.activateAddon', 'Activate Addon')}\n </button>\n </div>\n </div>\n )}\n\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={() => navigateTo('/addons')}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors\"\n >\n Back to Addons\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AddonDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/addons/pages/AddonDetailPage.tsx"],"sourcesContent":["/**\n * Addons Module - Addon Detail Page\n * Displays detailed information about a specific addon\n */\n\nimport { useMemo, type FC } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useAddon, useAddonMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n isServerError,\n} from '../../../shared/utils';\nimport type { SubscriptionFeatures } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\nimport {\n useDefaultBillingCurrency,\n getDisplayPrice,\n} from '../../../hooks/useDefaultBillingCurrency';\n\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n totalValue: number;\n type: string;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\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 limitType = (feature.quota.limits as { type?: string } | null)?.type ?? 'unknown';\n const limitValue = getPlanFeatureQuantity(feature);\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 type: limitType,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\nexport const AddonDetailPage: 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 { addonId } = useParams<{ addonId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const displayCurrency = useDefaultBillingCurrency();\n const { addon, isLoading, error, refetch } = useAddon(addonId);\n const { toggleAddon, isToggling } = useAddonMutations();\n\n // Memoize combined quotas before any conditional returns\n const combinedQuotas = useMemo(() => combineQuotas(addon?.features || []), [addon?.features]);\n\n // Permission check\n if (!permissions.canViewAddons) {\n return (\n <AccessDenied\n message={tr(\n 'billing.addons.noViewPermission',\n \"You don't have permission to view addon details.\"\n )}\n />\n );\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"h-24 bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n <div className=\"h-64 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n );\n }\n\n // Error state\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.addons.serverUnavailable', 'Server Unavailable')}\n message={tr(\n 'billing.addons.serverUnavailableMessage',\n 'Unable to load addon details. The server might be down or experiencing issues.'\n )}\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n // Not found state\n if (!addon) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.addons.addonNotFound', 'Addon not found')}\n </h2>\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.addons.addonNotFoundMessage',\n \"The addon you're looking for doesn't exist or has been removed.\"\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/addons')}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Back to Addons\n </button>\n </div>\n </div>\n );\n }\n\n const handleToggle = async () => {\n try {\n await toggleAddon(addon.id, !addon.isActive);\n refetch();\n } catch (err) {\n console.error('Failed to toggle addon:', err);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={addon.name}\n description={`${formatPlanDurationLabel(addon.duration)} addon`}\n />\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Pricing Card */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.addons.pricing', 'Pricing')}\n </h2>\n </div>\n <div className=\"p-6\">\n <div className=\"flex items-baseline gap-2\">\n <span className=\"text-4xl font-bold text-text-primary\">\n {(() => {\n const { price, currency } = getDisplayPrice(\n addon.price,\n addon.currency,\n addon.currencyPrices as Record<string, number> | null,\n displayCurrency\n );\n return formatCurrency(price, currency);\n })()}\n </span>\n <span className=\"text-lg text-text-muted\">\n / {formatPlanDuration(addon.duration)}\n </span>\n </div>\n <p className=\"text-sm text-text-muted mt-2\">\n {tr('billing.addons.billed', 'Billed')}{' '}\n {addon.duration === 'monthly'\n ? tr('billing.addons.everyMonth', 'every month')\n : tr('billing.addons.annually', 'annually')}\n </p>\n </div>\n </div>\n\n {/* Quotas Card */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.addons.includedQuotas', 'Included Quotas')} ({combinedQuotas.length})\n </h2>\n </div>\n <div className=\"p-4\">\n {combinedQuotas.length === 0 ? (\n <p className=\"text-sm text-text-muted py-4 text-center\">\n {tr('billing.addons.noQuotas', 'No quotas associated with this addon.')}\n </p>\n ) : (\n <div className=\"space-y-3\">\n {combinedQuotas.map((quota) => (\n <div\n key={quota.quotaId}\n className=\"flex items-center justify-between p-3 border border-border-subtle rounded-lg bg-bg-sunken/20\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-full bg-[var(--color-accent-soft)] flex items-center justify-center\">\n <svg\n className=\"size-4 text-text-link\"\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=\"M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z\"\n />\n </svg>\n </div>\n <div>\n <p className=\"font-medium text-text-primary\">{quota.name}</p>\n <p className=\"text-xs text-text-muted capitalize\">Type: {quota.type}</p>\n </div>\n </div>\n <div className=\"text-right\">\n <span className=\"text-lg font-semibold text-status-success-text\">\n {quota.totalValue.toLocaleString()}\n </span>\n <p className=\"text-xs text-text-muted\">total</p>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Status Card */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.addons.statusTitle', 'Status')}\n </h2>\n </div>\n <div className=\"p-4 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-muted\">\n {tr('billing.addons.status', 'Status')}\n </span>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${\n addon.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {addon.isActive ? 'Active' : 'Inactive'}\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-muted\">\n {tr('billing.addons.currency', 'Currency')}\n </span>\n <span className=\"text-sm font-medium text-text-primary\">\n {\n getDisplayPrice(\n addon.price,\n addon.currency,\n addon.currencyPrices as Record<string, number> | null,\n displayCurrency\n ).currency\n }\n </span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-muted\">\n {tr('billing.addons.billingLabel', 'Billing')}\n </span>\n <span className=\"text-sm font-medium text-text-primary capitalize\">\n {addon.duration}\n </span>\n </div>\n </div>\n </div>\n\n {/* Actions Card */}\n {permissions.canManageAddons && (\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.addons.actions', 'Actions')}\n </h2>\n </div>\n <div className=\"p-4 space-y-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo(`/addons/${addon.id}/edit`)}\n className=\"w-full px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Edit Addon\n </button>\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={isToggling}\n className={`w-full px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors disabled:opacity-50 ${\n addon.isActive\n ? 'text-status-error-text hover:bg-status-error-bg-subtle'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {isToggling\n ? tr('billing.addons.processing', 'Processing...')\n : addon.isActive\n ? tr('billing.addons.deactivateAddon', 'Deactivate Addon')\n : tr('billing.addons.activateAddon', 'Activate Addon')}\n </button>\n </div>\n </div>\n )}\n\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={() => navigateTo('/addons')}\n className=\"w-full px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Back to Addons\n </button>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -48,7 +48,7 @@ var y = () => {
48
48
  }, [w]), !C.canManageAddons) return /* @__PURE__ */ g(e, { message: b("billing.addons.noEditPermission", "You don't have permission to edit addons.") });
49
49
  if (T || k) return /* @__PURE__ */ _("div", {
50
50
  className: "space-y-6 p-6",
51
- children: [/* @__PURE__ */ g("div", { className: "h-8 w-48 bg-muted animate-pulse rounded" }), /* @__PURE__ */ g("div", {
51
+ children: [/* @__PURE__ */ g("div", { className: "h-8 w-48 bg-bg-sunken animate-pulse rounded" }), /* @__PURE__ */ g("div", {
52
52
  className: "max-w-2xl space-y-4",
53
53
  children: [
54
54
  1,
@@ -56,7 +56,7 @@ var y = () => {
56
56
  3,
57
57
  4,
58
58
  5
59
- ].map((e) => /* @__PURE__ */ g("div", { className: "h-16 bg-muted animate-pulse rounded" }, e))
59
+ ].map((e) => /* @__PURE__ */ g("div", { className: "h-16 bg-bg-sunken animate-pulse rounded" }, e))
60
60
  })]
61
61
  });
62
62
  if (E && a(E)) return /* @__PURE__ */ g("div", {
@@ -73,12 +73,12 @@ var y = () => {
73
73
  children: /* @__PURE__ */ _("div", {
74
74
  className: "text-center space-y-4",
75
75
  children: [/* @__PURE__ */ g("h2", {
76
- className: "text-lg font-semibold text-foreground",
76
+ className: "text-lg font-semibold text-text-primary",
77
77
  children: b("billing.addons.addonNotFound", "Addon not found")
78
78
  }), /* @__PURE__ */ g("button", {
79
79
  type: "button",
80
80
  onClick: () => S("/addons"),
81
- className: "px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90",
81
+ className: "px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover",
82
82
  children: "Back to Addons"
83
83
  })]
84
84
  })
@@ -155,11 +155,11 @@ var y = () => {
155
155
  /* @__PURE__ */ _("div", {
156
156
  className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
157
157
  children: [/* @__PURE__ */ _("div", {
158
- className: "border border-border rounded-lg bg-card overflow-hidden h-fit",
158
+ className: "border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)] h-fit",
159
159
  children: [/* @__PURE__ */ g("div", {
160
- className: "p-4 border-b border-border bg-muted/30",
160
+ className: "p-4 border-b border-border-subtle bg-bg-sunken/30",
161
161
  children: /* @__PURE__ */ g("h2", {
162
- className: "font-semibold text-foreground",
162
+ className: "font-semibold text-text-primary",
163
163
  children: b("billing.addons.basicInfo", "Basic Information")
164
164
  })
165
165
  }), /* @__PURE__ */ _("div", {
@@ -167,14 +167,14 @@ var y = () => {
167
167
  children: [
168
168
  /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ _("label", {
169
169
  htmlFor: "name",
170
- className: "block text-sm font-medium text-foreground mb-1",
170
+ className: "block text-sm font-medium text-text-primary mb-1",
171
171
  children: [b("billing.addons.addonNameLabel", "Addon Name"), " *"]
172
172
  }), /* @__PURE__ */ g("input", {
173
173
  id: "name",
174
174
  type: "text",
175
175
  value: M,
176
176
  onChange: (e) => N(e.target.value),
177
- 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",
177
+ className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
178
178
  placeholder: b("billing.addons.addonNamePlaceholder", "e.g., Extra Storage"),
179
179
  required: !0
180
180
  })] }),
@@ -182,7 +182,7 @@ var y = () => {
182
182
  className: "grid grid-cols-2 gap-4",
183
183
  children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ _("label", {
184
184
  htmlFor: "price",
185
- className: "block text-sm font-medium text-foreground mb-1",
185
+ className: "block text-sm font-medium text-text-primary mb-1",
186
186
  children: [b("billing.addons.priceLabel", "Price"), " *"]
187
187
  }), /* @__PURE__ */ g("input", {
188
188
  id: "price",
@@ -191,18 +191,18 @@ var y = () => {
191
191
  min: "0",
192
192
  value: P,
193
193
  onChange: (e) => F(e.target.value),
194
- 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",
194
+ className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
195
195
  placeholder: "9.99",
196
196
  required: !0
197
197
  })] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ _("label", {
198
198
  htmlFor: "currency",
199
- className: "block text-sm font-medium text-foreground mb-1",
199
+ className: "block text-sm font-medium text-text-primary mb-1",
200
200
  children: [b("billing.addons.currencyLabel", "Currency"), " *"]
201
201
  }), /* @__PURE__ */ _("select", {
202
202
  id: "currency",
203
203
  value: I,
204
204
  onChange: (e) => L(e.target.value),
205
- 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",
205
+ className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
206
206
  children: [
207
207
  /* @__PURE__ */ g("option", {
208
208
  value: "USD",
@@ -241,13 +241,13 @@ var y = () => {
241
241
  }),
242
242
  /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ _("label", {
243
243
  htmlFor: "duration",
244
- className: "block text-sm font-medium text-foreground mb-1",
244
+ className: "block text-sm font-medium text-text-primary mb-1",
245
245
  children: [b("billing.addons.billingPeriodLabel", "Billing Period"), " *"]
246
246
  }), /* @__PURE__ */ _("select", {
247
247
  id: "duration",
248
248
  value: R,
249
249
  onChange: (e) => z(e.target.value),
250
- 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",
250
+ className: "w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]",
251
251
  children: [/* @__PURE__ */ g("option", {
252
252
  value: i.MONTHLY,
253
253
  children: "Monthly"
@@ -263,43 +263,43 @@ var y = () => {
263
263
  type: "checkbox",
264
264
  checked: B,
265
265
  onChange: (e) => V(e.target.checked),
266
- className: "size-4 rounded border-border text-primary focus:ring-primary"
266
+ className: "size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]"
267
267
  }), /* @__PURE__ */ g("label", {
268
268
  htmlFor: "isActive",
269
- className: "text-sm font-medium text-foreground",
269
+ className: "text-sm font-medium text-text-primary",
270
270
  children: b("billing.addons.addonIsActive", "Addon is active")
271
271
  })]
272
272
  })
273
273
  ]
274
274
  })]
275
275
  }), /* @__PURE__ */ _("div", {
276
- className: "border border-border rounded-lg bg-card overflow-hidden h-fit",
276
+ className: "border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)] h-fit",
277
277
  children: [/* @__PURE__ */ _("div", {
278
- className: "p-4 border-b border-border bg-muted/30 flex items-center justify-between",
278
+ className: "p-4 border-b border-border-subtle bg-bg-sunken/30 flex items-center justify-between",
279
279
  children: [/* @__PURE__ */ g("h2", {
280
- className: "font-semibold text-foreground",
280
+ className: "font-semibold text-text-primary",
281
281
  children: b("billing.addons.quotaAllocations", "Quota Allocations")
282
282
  }), /* @__PURE__ */ g("button", {
283
283
  type: "button",
284
284
  onClick: X,
285
- className: "px-3 py-1 text-sm font-medium text-primary hover:bg-primary/10 rounded-md transition-colors",
285
+ className: "px-3 py-1 text-sm font-medium text-text-link hover:bg-[var(--color-accent-soft)] rounded-md transition-colors",
286
286
  children: "+ Add Quota"
287
287
  })]
288
288
  }), /* @__PURE__ */ g("div", {
289
289
  className: "p-4 space-y-3",
290
290
  children: H.length === 0 ? /* @__PURE__ */ g("p", {
291
- className: "text-sm text-muted-foreground",
291
+ className: "text-sm text-text-muted",
292
292
  children: b("billing.addons.noQuotasAllocated", "No quotas allocated. Click \"Add Quota\" to add one.")
293
293
  }) : H.map((e, t) => {
294
294
  let n = O.find((t) => t.id === e.quotaId), r = n?.limits?.type ?? "unknown";
295
295
  return /* @__PURE__ */ _("div", {
296
- className: "p-3 border border-border rounded-lg bg-muted/20 space-y-3",
296
+ className: "p-3 border border-border-subtle rounded-lg bg-bg-sunken/20 space-y-3",
297
297
  children: [/* @__PURE__ */ _("div", {
298
298
  className: "flex items-center gap-3",
299
299
  children: [/* @__PURE__ */ _("select", {
300
300
  value: e.quotaId,
301
301
  onChange: (e) => Q(t, "quotaId", e.target.value),
302
- 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"}`,
302
+ className: `flex-1 px-3 py-2 border border-border-subtle rounded-input bg-bg-surface focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${e.quotaId === "" ? "text-text-muted" : "text-text-primary"}`,
303
303
  children: [/* @__PURE__ */ g("option", {
304
304
  value: "",
305
305
  disabled: !0,
@@ -329,20 +329,20 @@ var y = () => {
329
329
  }), n && /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ g("div", {
330
330
  className: "flex items-center justify-between text-sm",
331
331
  children: /* @__PURE__ */ _("span", {
332
- className: "capitalize text-muted-foreground",
332
+ className: "capitalize text-text-muted",
333
333
  children: ["Type: ", r]
334
334
  })
335
335
  }), /* @__PURE__ */ _("div", {
336
336
  className: "flex items-center gap-2",
337
337
  children: [/* @__PURE__ */ _("label", {
338
- className: "text-sm text-muted-foreground",
338
+ className: "text-sm text-text-muted",
339
339
  children: [b("billing.addons.limit", "Limit"), ":"]
340
340
  }), /* @__PURE__ */ g("input", {
341
341
  type: "number",
342
342
  min: "1",
343
343
  value: e.quantity,
344
344
  onChange: (e) => Q(t, "quantity", e.target.value),
345
- className: "w-36 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"
345
+ className: "w-36 px-3 py-1.5 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] text-center"
346
346
  })]
347
347
  })] })]
348
348
  }, t);
@@ -359,12 +359,12 @@ var y = () => {
359
359
  children: [/* @__PURE__ */ g("button", {
360
360
  type: "submit",
361
361
  disabled: j,
362
- 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",
362
+ className: "px-6 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50",
363
363
  children: j ? b("billing.addons.saving", "Saving...") : b("billing.addons.saveChanges", "Save Changes")
364
364
  }), /* @__PURE__ */ g("button", {
365
365
  type: "button",
366
366
  onClick: () => S(`/addons/${x}`),
367
- className: "px-6 py-2 text-sm font-medium border border-border text-foreground rounded-md hover:bg-muted transition-colors",
367
+ className: "px-6 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors",
368
368
  children: "Cancel"
369
369
  })]
370
370
  })
@@ -1 +1 @@
1
- {"version":3,"file":"AddonEditPage.js","names":[],"sources":["../../../../../src/billing/modules/addons/pages/AddonEditPage.tsx"],"sourcesContent":["/**\n * Addons Module - Addon Edit Page\n * Form to edit an existing addon\n */\n\nimport { useState, useEffect, type FC, type FormEvent } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useAddon, useAddonMutations } from '../hooks';\nimport { useQuotas } from '../../plans/hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n AccessDenied,\n CurrencyPriceOverridesEditor,\n PageHeader,\n ServerError,\n} from '../../../shared/components';\nimport { isServerError } from '../../../shared/utils';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\nimport {\n PlanDuration,\n type CurrencyPriceOverrideInput,\n type UpdateAddonInput,\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 AddonEditPage: 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 { addonId } = useParams<{ addonId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { addon, isLoading: isAddonLoading, error: addonError, refetch } = useAddon(addonId);\n const { quotas, isLoading: isQuotasLoading } = useQuotas({ onlyActive: true });\n const { updateAddon, isUpdating } = useAddonMutations();\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 [isActive, setIsActive] = 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 // Populate form when addon data loads\n useEffect(() => {\n if (addon) {\n setName(addon.name ?? '');\n setPrice(String(addon.price ?? ''));\n setCurrency(addon.currency ?? 'USD');\n setDuration(addon.duration ?? PlanDuration.MONTHLY);\n setIsActive(addon.isActive ?? true);\n // Pre-populate currency price overrides so existing prices are preserved on save\n if (addon.currencyPrices && typeof addon.currencyPrices === 'object') {\n const existing = addon.currencyPrices as Record<string, number>;\n setCurrencyPriceOverrides(\n Object.entries(existing)\n .filter(([, v]) => typeof v === 'number')\n .map(([currency, amount]) => ({ currency, amount }))\n );\n }\n // Extract quota allocations from features\n if (addon.features && addon.features.length > 0) {\n const allocations: QuotaAllocation[] = [];\n const quotaMap = new Map<string, number>();\n\n for (const feature of addon.features) {\n if (feature.quota?.id) {\n const quotaId = feature.quota.id;\n const value = getPlanFeatureQuantity(feature);\n quotaMap.set(quotaId, (quotaMap.get(quotaId) ?? 0) + value);\n }\n }\n\n for (const [quotaId, quantity] of quotaMap) {\n allocations.push({ quotaId, quantity });\n }\n\n setQuotaAllocations(allocations);\n }\n }\n }, [addon]);\n\n // Permission check\n if (!permissions.canManageAddons) {\n return (\n <AccessDenied\n message={tr('billing.addons.noEditPermission', \"You don't have permission to edit addons.\")}\n />\n );\n }\n\n // Loading state\n if (isAddonLoading || 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 // Error state\n if (addonError && isServerError(addonError)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.addons.serverUnavailable', 'Server Unavailable')}\n message={tr(\n 'billing.addons.serverUnavailableLoadAddon',\n 'Unable to load addon. The server might be down or experiencing issues.'\n )}\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n if (!addon) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.addons.addonNotFound', 'Addon not found')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/addons')}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90\"\n >\n Back to Addons\n </button>\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('Addon 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 (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: UpdateAddonInput = {\n id: addonId!,\n name: name.trim(),\n price: Number(price),\n currency,\n duration,\n isActive,\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 updateAddon(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo(`/addons/${addonId}`), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update addon');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={`Edit Addon: ${addon.name}`}\n description={tr(\n 'billing.addons.editAddonDesc',\n 'Update the addon details and quota allocations'\n )}\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.addons.updateSuccess', 'Addon updated 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.addons.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.addons.addonNameLabel', 'Addon 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.addons.addonNamePlaceholder', 'e.g., Extra Storage')}\n required\n />\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.addons.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=\"9.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.addons.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.addons.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 {/* Status */}\n <div className=\"flex items-center gap-3\">\n <input\n id=\"isActive\"\n type=\"checkbox\"\n checked={isActive}\n onChange={(e) => setIsActive(e.target.checked)}\n className=\"size-4 rounded border-border text-primary focus:ring-primary\"\n />\n <label htmlFor=\"isActive\" className=\"text-sm font-medium text-foreground\">\n {tr('billing.addons.addonIsActive', 'Addon is active')}\n </label>\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.addons.quotaAllocations', 'Quota Allocations')}\n </h2>\n <button\n type=\"button\"\n onClick={handleAddQuota}\n className=\"px-3 py-1 text-sm font-medium text-primary hover:bg-primary/10 rounded-md transition-colors\"\n >\n + Add Quota\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {quotaAllocations.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'billing.addons.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 limitType =\n (selectedQuota?.limits as { type?: string } | null)?.type ?? 'unknown';\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.addons.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 <span className=\"capitalize text-muted-foreground\">\n Type: {limitType}\n </span>\n </div>\n\n {/* Limit value — allocation.quantity is the absolute limit */}\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-muted-foreground\">\n {tr('billing.addons.limit', 'Limit')}:\n </label>\n <input\n type=\"number\"\n min=\"1\"\n value={allocation.quantity}\n onChange={(e) => handleQuotaChange(index, 'quantity', e.target.value)}\n className=\"w-36 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 </>\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={isUpdating}\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 {isUpdating\n ? tr('billing.addons.saving', 'Saving...')\n : tr('billing.addons.saveChanges', 'Save Changes')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/addons/${addonId}`)}\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":"AddonEditPage.js","names":[],"sources":["../../../../../src/billing/modules/addons/pages/AddonEditPage.tsx"],"sourcesContent":["/**\n * Addons Module - Addon Edit Page\n * Form to edit an existing addon\n */\n\nimport { useState, useEffect, type FC, type FormEvent } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useAddon, useAddonMutations } from '../hooks';\nimport { useQuotas } from '../../plans/hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n AccessDenied,\n CurrencyPriceOverridesEditor,\n PageHeader,\n ServerError,\n} from '../../../shared/components';\nimport { isServerError } from '../../../shared/utils';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\nimport {\n PlanDuration,\n type CurrencyPriceOverrideInput,\n type UpdateAddonInput,\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 AddonEditPage: 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 { addonId } = useParams<{ addonId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { addon, isLoading: isAddonLoading, error: addonError, refetch } = useAddon(addonId);\n const { quotas, isLoading: isQuotasLoading } = useQuotas({ onlyActive: true });\n const { updateAddon, isUpdating } = useAddonMutations();\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 [isActive, setIsActive] = 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 // Populate form when addon data loads\n useEffect(() => {\n if (addon) {\n setName(addon.name ?? '');\n setPrice(String(addon.price ?? ''));\n setCurrency(addon.currency ?? 'USD');\n setDuration(addon.duration ?? PlanDuration.MONTHLY);\n setIsActive(addon.isActive ?? true);\n // Pre-populate currency price overrides so existing prices are preserved on save\n if (addon.currencyPrices && typeof addon.currencyPrices === 'object') {\n const existing = addon.currencyPrices as Record<string, number>;\n setCurrencyPriceOverrides(\n Object.entries(existing)\n .filter(([, v]) => typeof v === 'number')\n .map(([currency, amount]) => ({ currency, amount }))\n );\n }\n // Extract quota allocations from features\n if (addon.features && addon.features.length > 0) {\n const allocations: QuotaAllocation[] = [];\n const quotaMap = new Map<string, number>();\n\n for (const feature of addon.features) {\n if (feature.quota?.id) {\n const quotaId = feature.quota.id;\n const value = getPlanFeatureQuantity(feature);\n quotaMap.set(quotaId, (quotaMap.get(quotaId) ?? 0) + value);\n }\n }\n\n for (const [quotaId, quantity] of quotaMap) {\n allocations.push({ quotaId, quantity });\n }\n\n setQuotaAllocations(allocations);\n }\n }\n }, [addon]);\n\n // Permission check\n if (!permissions.canManageAddons) {\n return (\n <AccessDenied\n message={tr('billing.addons.noEditPermission', \"You don't have permission to edit addons.\")}\n />\n );\n }\n\n // Loading state\n if (isAddonLoading || isQuotasLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-bg-sunken 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-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (addonError && isServerError(addonError)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title={tr('billing.addons.serverUnavailable', 'Server Unavailable')}\n message={tr(\n 'billing.addons.serverUnavailableLoadAddon',\n 'Unable to load addon. The server might be down or experiencing issues.'\n )}\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n if (!addon) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.addons.addonNotFound', 'Addon not found')}\n </h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/addons')}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover\"\n >\n Back to Addons\n </button>\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('Addon 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 (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: UpdateAddonInput = {\n id: addonId!,\n name: name.trim(),\n price: Number(price),\n currency,\n duration,\n isActive,\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 updateAddon(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo(`/addons/${addonId}`), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update addon');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={`Edit Addon: ${addon.name}`}\n description={tr(\n 'billing.addons.editAddonDesc',\n 'Update the addon details and quota allocations'\n )}\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.addons.updateSuccess', 'Addon updated 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-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)] h-fit\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.addons.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-text-primary mb-1\">\n {tr('billing.addons.addonNameLabel', 'Addon 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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n placeholder={tr('billing.addons.addonNamePlaceholder', 'e.g., Extra Storage')}\n required\n />\n </div>\n\n {/* Price & Currency */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"price\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n placeholder=\"9.99\"\n required\n />\n </div>\n <div>\n <label\n htmlFor=\"currency\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.addons.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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\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-text-primary mb-1\"\n >\n {tr('billing.addons.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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n >\n <option value={PlanDuration.MONTHLY}>Monthly</option>\n <option value={PlanDuration.YEARLY}>Yearly</option>\n </select>\n </div>\n\n {/* Status */}\n <div className=\"flex items-center gap-3\">\n <input\n id=\"isActive\"\n type=\"checkbox\"\n checked={isActive}\n onChange={(e) => setIsActive(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <label htmlFor=\"isActive\" className=\"text-sm font-medium text-text-primary\">\n {tr('billing.addons.addonIsActive', 'Addon is active')}\n </label>\n </div>\n </div>\n </div>\n\n {/* Quota Allocations */}\n <div className=\"border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)] h-fit\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30 flex items-center justify-between\">\n <h2 className=\"font-semibold text-text-primary\">\n {tr('billing.addons.quotaAllocations', 'Quota Allocations')}\n </h2>\n <button\n type=\"button\"\n onClick={handleAddQuota}\n className=\"px-3 py-1 text-sm font-medium text-text-link hover:bg-[var(--color-accent-soft)] rounded-md transition-colors\"\n >\n + Add Quota\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {quotaAllocations.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.addons.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 limitType =\n (selectedQuota?.limits as { type?: string } | null)?.type ?? 'unknown';\n\n return (\n <div\n key={index}\n className=\"p-3 border border-border-subtle rounded-lg bg-bg-sunken/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-subtle rounded-input bg-bg-surface focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] ${\n allocation.quotaId === '' ? 'text-text-muted' : 'text-text-primary'\n }`}\n >\n <option value=\"\" disabled>\n {tr('billing.addons.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 <span className=\"capitalize text-text-muted\">Type: {limitType}</span>\n </div>\n\n {/* Limit value — allocation.quantity is the absolute limit */}\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-text-muted\">\n {tr('billing.addons.limit', 'Limit')}:\n </label>\n <input\n type=\"number\"\n min=\"1\"\n value={allocation.quantity}\n onChange={(e) => handleQuotaChange(index, 'quantity', e.target.value)}\n className=\"w-36 px-3 py-1.5 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] text-center\"\n />\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={isUpdating}\n className=\"px-6 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50\"\n >\n {isUpdating\n ? tr('billing.addons.saving', 'Saving...')\n : tr('billing.addons.saveChanges', 'Save Changes')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/addons/${addonId}`)}\n className=\"px-6 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n );\n};\n"],"mappings":"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"}