@burdenoff/microfe-billing 2026.625.4 → 2026.626.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 (96) hide show
  1. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +192 -187
  2. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
  3. package/dist/billing/modules/addons/pages/AddonCreatePage.js +322 -317
  4. package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
  5. package/dist/billing/modules/addons/pages/AddonDetailPage.js +190 -185
  6. package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
  7. package/dist/billing/modules/addons/pages/AddonEditPage.js +297 -292
  8. package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
  9. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +60 -75
  10. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
  11. package/dist/billing/modules/addons/pages/AddonsListPage.js +86 -101
  12. package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
  13. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +124 -121
  14. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
  15. package/dist/billing/modules/analytics/pages/AnalyticsPage.js +103 -111
  16. package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
  17. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +196 -160
  18. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  19. package/dist/billing/modules/billing/pages/InvoicesListPage.js +58 -73
  20. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  21. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +65 -75
  22. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
  23. package/dist/billing/modules/billing/pages/RefundsPage.js +399 -399
  24. package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
  25. package/dist/billing/modules/billing/pages/TransactionsPage.js +49 -48
  26. package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
  27. package/dist/billing/modules/billing/pages/UserRefundsPage.js +144 -151
  28. package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
  29. package/dist/billing/modules/checkout/pages/CheckoutPage.js +430 -425
  30. package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
  31. package/dist/billing/modules/coupons/pages/CouponDetailPage.js +121 -93
  32. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  33. package/dist/billing/modules/coupons/pages/CouponsListPage.js +154 -152
  34. package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
  35. package/dist/billing/modules/coupons/pages/CreateCouponPage.js +467 -462
  36. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  37. package/dist/billing/modules/coupons/pages/EditCouponPage.js +294 -286
  38. package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
  39. package/dist/billing/modules/credits/pages/AdminCreditsPage.js +123 -117
  40. package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
  41. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +43 -42
  42. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
  43. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +192 -207
  44. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
  45. package/dist/billing/modules/credits/pages/CreditsPage.js +40 -56
  46. package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
  47. package/dist/billing/modules/dashboard/pages/OverviewPage.js +128 -121
  48. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  49. package/dist/billing/modules/discounts/pages/DiscountsListPage.js +165 -180
  50. package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
  51. package/dist/billing/modules/earnings/pages/EarningsPage.js +103 -100
  52. package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
  53. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +175 -175
  54. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  55. package/dist/billing/modules/earnings/pages/PayoutsPage.js +96 -96
  56. package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
  57. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +42 -40
  58. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  59. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +45 -38
  60. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  61. package/dist/billing/modules/plans/pages/PlanCreatePage.js +339 -334
  62. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  63. package/dist/billing/modules/plans/pages/PlanDetailPage.js +80 -74
  64. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  65. package/dist/billing/modules/plans/pages/PlanEditPage.js +342 -337
  66. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  67. package/dist/billing/modules/plans/pages/PlansBrowsePage.js +38 -53
  68. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  69. package/dist/billing/modules/plans/pages/PlansListPage.js +60 -75
  70. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  71. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +242 -237
  72. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  73. package/dist/billing/modules/promotions/pages/PromotionsListPage.js +29 -39
  74. package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
  75. package/dist/billing/modules/quota/pages/QuotaPage.js +1 -0
  76. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +299 -294
  77. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
  78. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +191 -186
  79. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
  80. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +308 -303
  81. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
  82. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +57 -78
  83. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
  84. package/dist/billing/modules/settings/pages/SettingsPage.js +79 -78
  85. package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
  86. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +15 -13
  87. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
  88. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +338 -299
  89. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
  90. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +82 -103
  91. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
  92. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +59 -54
  93. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
  94. package/dist/billing/modules/usage/pages/UsagePage.js +172 -215
  95. package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
  96. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"PlanCreatePage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanCreatePage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Create Page\n * Form to create a new plan\n */\n\nimport { useState, type FC, type FormEvent } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlanMutations, useQuotas } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, CurrencyPriceOverridesEditor, PageHeader } from '../../../shared/components';\nimport {\n PlanDuration,\n type CreatePlanInput,\n type CurrencyPriceOverrideInput,\n} from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface QuotaAllocation {\n quotaId: string;\n quantity: number;\n}\n\nexport const PlanCreatePage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { quotas, isLoading: isQuotasLoading } = useQuotas({ onlyActive: true });\n const { createPlan, isCreating } = usePlanMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [price, setPrice] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [duration, setDuration] = useState<PlanDuration>(PlanDuration.MONTHLY);\n const [productId, setProductId] = useState('');\n const [isPurchasable, setIsPurchasable] = useState(true);\n const [quotaAllocations, setQuotaAllocations] = useState<QuotaAllocation[]>([]);\n const [currencyPriceOverrides, setCurrencyPriceOverrides] = useState<\n CurrencyPriceOverrideInput[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Permission check\n if (!permissions.canManagePlans) {\n return (\n <AccessDenied\n message={tr(\n 'billing.plans.noCreatePermission',\n \"You don't have permission to create plans.\"\n )}\n />\n );\n }\n\n // Loading state\n if (isQuotasLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-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 const handleAddQuota = () => {\n // Add new quota at the top with empty quotaId (placeholder)\n setQuotaAllocations([{ quotaId: '', quantity: 1 }, ...quotaAllocations]);\n };\n\n const handleRemoveQuota = (index: number) => {\n setQuotaAllocations(quotaAllocations.filter((_, i) => i !== index));\n };\n\n const handleQuotaChange = (\n index: number,\n field: 'quotaId' | 'quantity',\n value: string | number\n ) => {\n const updated = [...quotaAllocations];\n if (field === 'quotaId') {\n updated[index].quotaId = value as string;\n } else {\n updated[index].quantity = Number(value);\n }\n setQuotaAllocations(updated);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n setSuccess(false);\n\n // Validation\n if (!name.trim()) {\n setError('Plan name is required');\n return;\n }\n if (!price || isNaN(Number(price)) || Number(price) < 0) {\n setError('Valid price is required');\n return;\n }\n if (!productId.trim()) {\n setError('Product ID is required');\n return;\n }\n if (quotaAllocations.length === 0) {\n setError('At least one quota is required');\n return;\n }\n // Check for unselected quotas\n const unselectedQuotas = quotaAllocations.filter((a) => a.quotaId === '');\n if (unselectedQuotas.length > 0) {\n setError('Please select a quota for all allocations');\n return;\n }\n\n const input: CreatePlanInput = {\n name: name.trim(),\n price: Number(price),\n currency,\n duration,\n productId: productId.trim(),\n isPurchasable,\n quotas: quotaAllocations.map((a) => ({\n quotaId: a.quotaId,\n quantity: a.quantity,\n })),\n ...(currencyPriceOverrides.length > 0 ? { currencyPriceOverrides } : {}),\n };\n\n try {\n await createPlan(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo('/plans'), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create plan');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.plans.createNewPlan', 'Create New Plan')}\n description={tr('billing.plans.createNewPlanDesc', 'Set up a new pricing plan with quotas')}\n />\n\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Error/Success Messages */}\n {error && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-error-text\">{error}</p>\n </div>\n )}\n {success && (\n <div className=\"p-4 bg-status-success-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-success-text\">\n {tr('billing.plans.createSuccess', 'Plan created successfully! Redirecting...')}\n </p>\n </div>\n )}\n\n {/* Two-column layout on desktop */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Basic Info */}\n <div className=\"border border-border-seam 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.plans.basicInfo', 'Basic Information')}\n </h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Name */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('billing.plans.planNameLabel', 'Plan Name')} *\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-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.plans.planNamePlaceholder', 'e.g., Pro Plan')}\n required\n />\n </div>\n\n {/* Product ID */}\n <div>\n <label\n htmlFor=\"productId\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.plans.productId', 'Product ID')} *\n </label>\n <input\n id=\"productId\"\n type=\"text\"\n value={productId}\n onChange={(e) => setProductId(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-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.plans.productIdPlaceholder', 'e.g., prod_abc123')}\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n {tr('billing.plans.productIdDesc', 'The product this plan belongs to')}\n </p>\n </div>\n\n {/* Price & Currency */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"price\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.plans.priceLabel', 'Price')} *\n </label>\n <input\n id=\"price\"\n type=\"number\"\n step=\"0.01\"\n min=\"0\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n placeholder=\"49.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.plans.currencyLabel', 'Currency')} *\n </label>\n <select\n id=\"currency\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-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.plans.billingPeriodLabel', 'Billing Period')} *\n </label>\n <select\n id=\"duration\"\n value={duration}\n onChange={(e) => setDuration(e.target.value as PlanDuration)}\n className=\"w-full px-3 py-2 border border-border-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 {/* Purchasable toggle */}\n <div className=\"flex items-start justify-between gap-4 pt-1\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('billing.plans.isPurchasableLabel', 'Publicly purchasable')}\n </p>\n <p className=\"text-xs text-text-muted mt-0.5\">\n {isPurchasable\n ? tr(\n 'billing.plans.isPurchasableOn',\n 'Visible on pricing page — users can subscribe directly.'\n )\n : tr(\n 'billing.plans.isPurchasableOff',\n 'Internal / custom plan — hidden from pricing page, assign via API only.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isPurchasable}\n onClick={() => setIsPurchasable((v) => !v)}\n className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2 ${\n isPurchasable ? 'bg-action-primary-bg' : 'bg-text-muted/30'\n }`}\n >\n <span\n className={`inline-block h-5 w-5 transform rounded-full bg-bg-surface shadow transition-transform ${\n isPurchasable ? 'translate-x-5' : 'translate-x-0'\n }`}\n />\n </button>\n </div>\n </div>\n </div>\n\n {/* Quota Allocations */}\n <div className=\"border border-border-seam 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.plans.quotaAllocations', 'Quota Allocations')} *\n </h2>\n <button\n type=\"button\"\n onClick={handleAddQuota}\n disabled={quotas.length === 0}\n className=\"px-3 py-1 text-sm font-medium text-text-link hover:bg-[var(--color-accent-soft)] rounded-md transition-colors disabled:opacity-50\"\n >\n + Add Quota\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.plans.noQuotasAvailable',\n 'No quotas available. Please create quotas first.'\n )}\n </p>\n ) : quotaAllocations.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.plans.noQuotasAllocated',\n 'No quotas allocated. Click \"Add Quota\" to add one.'\n )}\n </p>\n ) : (\n quotaAllocations.map((allocation, index) => {\n const selectedQuota = quotas.find((q) => q.id === allocation.quotaId);\n const limits = selectedQuota?.limits as { type?: string; value?: number } | null;\n const limitValue = limits?.value ?? 0;\n const limitType = limits?.type ?? 'unknown';\n const totalValue = limitValue * allocation.quantity;\n\n return (\n <div\n key={index}\n className=\"p-3 border border-border-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.plans.selectQuota', 'Select a quota...')}\n </option>\n {quotas.map((quota) => (\n <option key={quota.id} value={quota.id}>\n {quota.name}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n onClick={() => handleRemoveQuota(index)}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-md transition-colors\"\n title=\"Remove quota\"\n >\n <svg\n className=\"size-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n </button>\n </div>\n\n {/* Quota Info - only show when quota is selected */}\n {selectedQuota && (\n <>\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"text-text-muted\">\n <span className=\"capitalize\">Type: {limitType}</span>\n <span className=\"mx-2\">•</span>\n <span>\n Value per unit:{' '}\n <span className=\"font-medium text-text-primary\">\n {limitValue.toLocaleString()}\n </span>\n </span>\n </div>\n </div>\n\n {/* Quantity & Total */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-text-muted\">\n {tr('billing.plans.quantity', 'Quantity')}:\n </label>\n <input\n type=\"number\"\n min=\"1\"\n value={allocation.quantity}\n onChange={(e) =>\n handleQuotaChange(index, 'quantity', e.target.value)\n }\n className=\"w-24 px-3 py-1.5 border border-border-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 <div className=\"flex items-center gap-2 text-sm\">\n <span className=\"text-text-muted\">=</span>\n <span className=\"font-semibold text-status-success-text\">\n {totalValue.toLocaleString()} total\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n <CurrencyPriceOverridesEditor\n overrides={currencyPriceOverrides}\n onChange={setCurrencyPriceOverrides}\n />\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4\">\n <button\n type=\"submit\"\n disabled={isCreating || quotas.length === 0}\n className=\"px-6 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50\"\n >\n {isCreating\n ? tr('billing.plans.creating', 'Creating...')\n : tr('billing.plans.createPlanBtn', 'Create Plan')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"px-6 py-2 text-sm font-medium border border-border-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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"}
1
+ {"version":3,"file":"PlanCreatePage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanCreatePage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Create Page\n * Form to create a new plan\n */\n\nimport { useState, type FC, type FormEvent } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlanMutations, useQuotas } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, CurrencyPriceOverridesEditor, PageHeader } from '../../../shared/components';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport {\n PlanDuration,\n type CreatePlanInput,\n type CurrencyPriceOverrideInput,\n} from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface QuotaAllocation {\n quotaId: string;\n quantity: number;\n}\n\nexport const PlanCreatePage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { quotas, isLoading: isQuotasLoading } = useQuotas({ onlyActive: true });\n const { createPlan, isCreating } = usePlanMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [price, setPrice] = useState('');\n const [currency, setCurrency] = useState('USD');\n const [duration, setDuration] = useState<PlanDuration>(PlanDuration.MONTHLY);\n const [productId, setProductId] = useState('');\n const [isPurchasable, setIsPurchasable] = useState(true);\n const [quotaAllocations, setQuotaAllocations] = useState<QuotaAllocation[]>([]);\n const [currencyPriceOverrides, setCurrencyPriceOverrides] = useState<\n CurrencyPriceOverrideInput[]\n >([]);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Permission check\n if (!permissions.canManagePlans) {\n return (\n <AccessDenied\n message={tr(\n 'billing.plans.noCreatePermission',\n \"You don't have permission to create plans.\"\n )}\n />\n );\n }\n\n // Loading state\n if (isQuotasLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-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 const handleAddQuota = () => {\n // Add new quota at the top with empty quotaId (placeholder)\n setQuotaAllocations([{ quotaId: '', quantity: 1 }, ...quotaAllocations]);\n };\n\n const handleRemoveQuota = (index: number) => {\n setQuotaAllocations(quotaAllocations.filter((_, i) => i !== index));\n };\n\n const handleQuotaChange = (\n index: number,\n field: 'quotaId' | 'quantity',\n value: string | number\n ) => {\n const updated = [...quotaAllocations];\n if (field === 'quotaId') {\n updated[index].quotaId = value as string;\n } else {\n updated[index].quantity = Number(value);\n }\n setQuotaAllocations(updated);\n };\n\n const handleSubmit = async (e: FormEvent) => {\n e.preventDefault();\n setError(null);\n setSuccess(false);\n\n // Validation\n if (!name.trim()) {\n setError('Plan name is required');\n return;\n }\n if (!price || isNaN(Number(price)) || Number(price) < 0) {\n setError('Valid price is required');\n return;\n }\n if (!productId.trim()) {\n setError('Product ID is required');\n return;\n }\n if (quotaAllocations.length === 0) {\n setError('At least one quota is required');\n return;\n }\n // Check for unselected quotas\n const unselectedQuotas = quotaAllocations.filter((a) => a.quotaId === '');\n if (unselectedQuotas.length > 0) {\n setError('Please select a quota for all allocations');\n return;\n }\n\n const input: CreatePlanInput = {\n name: name.trim(),\n price: Number(price),\n currency,\n duration,\n productId: productId.trim(),\n isPurchasable,\n quotas: quotaAllocations.map((a) => ({\n quotaId: a.quotaId,\n quantity: a.quantity,\n })),\n ...(currencyPriceOverrides.length > 0 ? { currencyPriceOverrides } : {}),\n };\n\n try {\n await createPlan(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo('/plans'), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to create plan');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.plans.createNewPlan', 'Create New Plan')}\n description={tr('billing.plans.createNewPlanDesc', 'Set up a new pricing plan with quotas')}\n />\n\n <PagePurpose>\n {tr(\n 'billing.plans.createPurpose',\n 'Define a new subscription tier customers can buy — its price and billing period, optional per-currency and per-seat pricing, and the quotas it includes. Once active, it appears on the Plans page for purchase.'\n )}\n </PagePurpose>\n\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Error/Success Messages */}\n {error && (\n <div className=\"p-4 bg-status-error-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-error-text\">{error}</p>\n </div>\n )}\n {success && (\n <div className=\"p-4 bg-status-success-bg-subtle border border-border-subtle rounded-lg\">\n <p className=\"text-sm text-status-success-text\">\n {tr('billing.plans.createSuccess', 'Plan created successfully! Redirecting...')}\n </p>\n </div>\n )}\n\n {/* Two-column layout on desktop */}\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n {/* Basic Info */}\n <div className=\"border border-border-seam 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.plans.basicInfo', 'Basic Information')}\n </h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Name */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('billing.plans.planNameLabel', 'Plan Name')} *\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={name}\n onChange={(e) => setName(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-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.plans.planNamePlaceholder', 'e.g., Pro Plan')}\n required\n />\n </div>\n\n {/* Product ID */}\n <div>\n <label\n htmlFor=\"productId\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.plans.productId', 'Product ID')} *\n </label>\n <input\n id=\"productId\"\n type=\"text\"\n value={productId}\n onChange={(e) => setProductId(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-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.plans.productIdPlaceholder', 'e.g., prod_abc123')}\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n {tr('billing.plans.productIdDesc', 'The product this plan belongs to')}\n </p>\n </div>\n\n {/* Price & Currency */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"price\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.plans.priceLabel', 'Price')} *\n </label>\n <input\n id=\"price\"\n type=\"number\"\n step=\"0.01\"\n min=\"0\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n placeholder=\"49.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.plans.currencyLabel', 'Currency')} *\n </label>\n <select\n id=\"currency\"\n value={currency}\n onChange={(e) => setCurrency(e.target.value)}\n className=\"w-full px-3 py-2 border border-border-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.plans.billingPeriodLabel', 'Billing Period')} *\n </label>\n <select\n id=\"duration\"\n value={duration}\n onChange={(e) => setDuration(e.target.value as PlanDuration)}\n className=\"w-full px-3 py-2 border border-border-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 {/* Purchasable toggle */}\n <div className=\"flex items-start justify-between gap-4 pt-1\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {tr('billing.plans.isPurchasableLabel', 'Publicly purchasable')}\n </p>\n <p className=\"text-xs text-text-muted mt-0.5\">\n {isPurchasable\n ? tr(\n 'billing.plans.isPurchasableOn',\n 'Visible on pricing page — users can subscribe directly.'\n )\n : tr(\n 'billing.plans.isPurchasableOff',\n 'Internal / custom plan — hidden from pricing page, assign via API only.'\n )}\n </p>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={isPurchasable}\n onClick={() => setIsPurchasable((v) => !v)}\n className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:ring-offset-2 ${\n isPurchasable ? 'bg-action-primary-bg' : 'bg-text-muted/30'\n }`}\n >\n <span\n className={`inline-block h-5 w-5 transform rounded-full bg-bg-surface shadow transition-transform ${\n isPurchasable ? 'translate-x-5' : 'translate-x-0'\n }`}\n />\n </button>\n </div>\n </div>\n </div>\n\n {/* Quota Allocations */}\n <div className=\"border border-border-seam 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.plans.quotaAllocations', 'Quota Allocations')} *\n </h2>\n <button\n type=\"button\"\n onClick={handleAddQuota}\n disabled={quotas.length === 0}\n className=\"px-3 py-1 text-sm font-medium text-text-link hover:bg-[var(--color-accent-soft)] rounded-md transition-colors disabled:opacity-50\"\n >\n + Add Quota\n </button>\n </div>\n <div className=\"p-4 space-y-3\">\n {quotas.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.plans.noQuotasAvailable',\n 'No quotas available. Please create quotas first.'\n )}\n </p>\n ) : quotaAllocations.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr(\n 'billing.plans.noQuotasAllocated',\n 'No quotas allocated. Click \"Add Quota\" to add one.'\n )}\n </p>\n ) : (\n quotaAllocations.map((allocation, index) => {\n const selectedQuota = quotas.find((q) => q.id === allocation.quotaId);\n const limits = selectedQuota?.limits as { type?: string; value?: number } | null;\n const limitValue = limits?.value ?? 0;\n const limitType = limits?.type ?? 'unknown';\n const totalValue = limitValue * allocation.quantity;\n\n return (\n <div\n key={index}\n className=\"p-3 border border-border-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.plans.selectQuota', 'Select a quota...')}\n </option>\n {quotas.map((quota) => (\n <option key={quota.id} value={quota.id}>\n {quota.name}\n </option>\n ))}\n </select>\n <button\n type=\"button\"\n onClick={() => handleRemoveQuota(index)}\n className=\"p-2 text-status-error-text hover:bg-status-error-bg-subtle rounded-md transition-colors\"\n title=\"Remove quota\"\n >\n <svg\n className=\"size-5\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n </button>\n </div>\n\n {/* Quota Info - only show when quota is selected */}\n {selectedQuota && (\n <>\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"text-text-muted\">\n <span className=\"capitalize\">Type: {limitType}</span>\n <span className=\"mx-2\">•</span>\n <span>\n Value per unit:{' '}\n <span className=\"font-medium text-text-primary\">\n {limitValue.toLocaleString()}\n </span>\n </span>\n </div>\n </div>\n\n {/* Quantity & Total */}\n <div className=\"flex items-center gap-4\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-text-muted\">\n {tr('billing.plans.quantity', 'Quantity')}:\n </label>\n <input\n type=\"number\"\n min=\"1\"\n value={allocation.quantity}\n onChange={(e) =>\n handleQuotaChange(index, 'quantity', e.target.value)\n }\n className=\"w-24 px-3 py-1.5 border border-border-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 <div className=\"flex items-center gap-2 text-sm\">\n <span className=\"text-text-muted\">=</span>\n <span className=\"font-semibold text-status-success-text\">\n {totalValue.toLocaleString()} total\n </span>\n </div>\n </div>\n </>\n )}\n </div>\n );\n })\n )}\n </div>\n </div>\n </div>\n\n <CurrencyPriceOverridesEditor\n overrides={currencyPriceOverrides}\n onChange={setCurrencyPriceOverrides}\n />\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4\">\n <button\n type=\"submit\"\n disabled={isCreating || quotas.length === 0}\n className=\"px-6 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50\"\n >\n {isCreating\n ? tr('billing.plans.creating', 'Creating...')\n : tr('billing.plans.createPlanBtn', 'Create Plan')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"px-6 py-2 text-sm font-medium border border-border-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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"}
@@ -8,8 +8,9 @@ import { useMemo as c } from "react";
8
8
  import { useParams as l } from "react-router-dom";
9
9
  import { Fragment as u, jsx as d, jsxs as f } from "react/jsx-runtime";
10
10
  import { useI18n as p } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
11
+ import { EmphasisPanel as m, PagePurpose as h } from "@burdenoff/fe-libs/ui";
11
12
  //#region src/billing/modules/plans/pages/PlanDetailPage.tsx
12
- function m(e) {
13
+ function g(e) {
13
14
  let t = /* @__PURE__ */ new Map();
14
15
  for (let n of e) {
15
16
  if (!n.quota) continue;
@@ -25,12 +26,12 @@ function m(e) {
25
26
  }
26
27
  return Array.from(t.values());
27
28
  }
28
- var h = () => {
29
- let { t: r } = p(), h = (e, t) => {
29
+ var _ = () => {
30
+ let { t: r } = p(), _ = (e, t) => {
30
31
  let n = r(e);
31
32
  return n === e ? t : n;
32
- }, { planId: g } = l(), _ = a(), v = i(), { plan: y, isLoading: b, error: x, refetch: S } = o(g), { togglePlan: C, isToggling: w } = s(), T = c(() => m(y?.features || []), [y?.features]);
33
- return v.canViewPlans ? b ? /* @__PURE__ */ f("div", {
33
+ }, { planId: v } = l(), y = a(), b = i(), { plan: x, isLoading: S, error: C, refetch: w } = o(v), { togglePlan: T, isToggling: E } = s(), D = c(() => g(x?.features || []), [x?.features]);
34
+ return b.canViewPlans ? S ? /* @__PURE__ */ f("div", {
34
35
  className: "space-y-6 p-6",
35
36
  children: [/* @__PURE__ */ f("div", {
36
37
  className: "flex items-center gap-4",
@@ -52,7 +53,7 @@ var h = () => {
52
53
  children: [/* @__PURE__ */ d("div", { className: "h-6 w-24 bg-bg-sunken animate-pulse rounded" }), /* @__PURE__ */ d("div", { className: "h-10 w-full bg-bg-sunken animate-pulse rounded" })]
53
54
  })]
54
55
  })]
55
- }) : x || !y ? /* @__PURE__ */ d("div", {
56
+ }) : C || !x ? /* @__PURE__ */ d("div", {
56
57
  className: "flex items-center justify-center h-full min-h-[400px]",
57
58
  children: /* @__PURE__ */ f("div", {
58
59
  className: "text-center space-y-4",
@@ -74,15 +75,15 @@ var h = () => {
74
75
  }),
75
76
  /* @__PURE__ */ d("h2", {
76
77
  className: "text-lg font-semibold text-text-primary",
77
- children: h("billing.plans.planNotFound", "Plan not found")
78
+ children: _("billing.plans.planNotFound", "Plan not found")
78
79
  }),
79
80
  /* @__PURE__ */ d("p", {
80
81
  className: "text-sm text-text-muted",
81
- children: x?.message || h("billing.plans.planNotFoundMessage", "The plan you are looking for does not exist.")
82
+ children: C?.message || _("billing.plans.planNotFoundMessage", "The plan you are looking for does not exist.")
82
83
  }),
83
84
  /* @__PURE__ */ d("button", {
84
85
  type: "button",
85
- onClick: () => _("/plans"),
86
+ onClick: () => y("/plans"),
86
87
  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",
87
88
  children: "Back to Plans"
88
89
  })
@@ -96,7 +97,7 @@ var h = () => {
96
97
  className: "flex items-start gap-4",
97
98
  children: [/* @__PURE__ */ d("button", {
98
99
  type: "button",
99
- onClick: () => _("/plans"),
100
+ onClick: () => y("/plans"),
100
101
  className: "p-2 -ml-2 text-text-muted hover:text-text-primary hover:bg-bg-sunken rounded-md transition-colors",
101
102
  children: /* @__PURE__ */ d("svg", {
102
103
  className: "size-5",
@@ -110,77 +111,82 @@ var h = () => {
110
111
  d: "M15 19l-7-7 7-7"
111
112
  })
112
113
  })
113
- }), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ f("div", {
114
- className: "flex items-center gap-3",
115
- children: [/* @__PURE__ */ d("h1", {
116
- className: "text-2xl md:text-3xl font-semibold tracking-tight text-text-primary",
117
- children: y.name
118
- }), /* @__PURE__ */ d("span", {
119
- className: `px-2 py-1 text-xs font-medium rounded ${y.isActive ? "bg-status-success-bg-subtle text-status-success-text" : "bg-bg-sunken text-text-muted"}`,
120
- children: y.isActive ? "Active" : "Inactive"
121
- })]
122
- }), /* @__PURE__ */ f("p", {
123
- className: "text-sm text-text-muted mt-1",
124
- children: [n(y.duration), " billing plan"]
125
- })] })]
114
+ }), /* @__PURE__ */ f("div", { children: [
115
+ /* @__PURE__ */ f("div", {
116
+ className: "flex items-center gap-3",
117
+ children: [/* @__PURE__ */ d("h1", {
118
+ className: "text-2xl md:text-3xl font-semibold tracking-tight text-text-primary",
119
+ children: x.name
120
+ }), /* @__PURE__ */ d("span", {
121
+ className: `px-2 py-1 text-xs font-medium rounded ${x.isActive ? "bg-status-success-bg-subtle text-status-success-text" : "bg-bg-sunken text-text-muted"}`,
122
+ children: x.isActive ? "Active" : "Inactive"
123
+ })]
124
+ }),
125
+ /* @__PURE__ */ f("p", {
126
+ className: "text-sm text-text-muted mt-1",
127
+ children: [n(x.duration), " billing plan"]
128
+ }),
129
+ /* @__PURE__ */ d(h, {
130
+ className: "mt-3",
131
+ children: _("billing.plans.detailPurpose", "A full breakdown of this plan — its price, billing period and every quota and feature it grants. Use it to decide whether the plan fits before subscribing, or (as an admin) to edit or activate it.")
132
+ })
133
+ ] })]
126
134
  }), /* @__PURE__ */ f("div", {
127
135
  className: "flex items-center gap-2",
128
- children: [v.canCreateSubscription && y.isActive && /* @__PURE__ */ d("button", {
136
+ children: [b.canCreateSubscription && x.isActive && /* @__PURE__ */ d("button", {
129
137
  type: "button",
130
- onClick: () => _(`/checkout/${y.id}`),
138
+ onClick: () => y(`/checkout/${x.id}`),
131
139
  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",
132
140
  children: "Subscribe to Plan"
133
- }), v.canManagePlans && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("button", {
141
+ }), b.canManagePlans && /* @__PURE__ */ f(u, { children: [/* @__PURE__ */ d("button", {
134
142
  type: "button",
135
- onClick: () => _(`/plans/${y.id}/edit`),
143
+ onClick: () => y(`/plans/${x.id}/edit`),
136
144
  className: "px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors",
137
145
  children: "Edit Plan"
138
146
  }), /* @__PURE__ */ d("button", {
139
147
  type: "button",
140
148
  onClick: async () => {
141
- if (v.canManagePlans) try {
142
- await C(y.id, !y.isActive), await S();
149
+ if (b.canManagePlans) try {
150
+ await T(x.id, !x.isActive), await w();
143
151
  } catch (e) {
144
152
  console.error("Failed to toggle plan:", e);
145
153
  }
146
154
  },
147
- disabled: w,
148
- className: `px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors ${y.isActive ? "text-status-error-text hover:bg-status-error-bg-subtle" : "text-status-success-text hover:bg-status-success-bg-subtle"}`,
149
- children: w ? h("billing.plans.processing", "Processing...") : y.isActive ? h("billing.plans.deactivate", "Deactivate") : h("billing.plans.activate", "Activate")
155
+ disabled: E,
156
+ className: `px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors ${x.isActive ? "text-status-error-text hover:bg-status-error-bg-subtle" : "text-status-success-text hover:bg-status-success-bg-subtle"}`,
157
+ children: E ? _("billing.plans.processing", "Processing...") : x.isActive ? _("billing.plans.deactivate", "Deactivate") : _("billing.plans.activate", "Activate")
150
158
  })] })]
151
159
  })]
152
160
  }), /* @__PURE__ */ f("div", {
153
161
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
154
162
  children: [/* @__PURE__ */ f("div", {
155
163
  className: "lg:col-span-2 space-y-6",
156
- children: [/* @__PURE__ */ f("div", {
157
- className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
158
- children: [/* @__PURE__ */ d("div", {
159
- className: "p-6 border-b border-border-subtle bg-bg-sunken/30",
160
- children: /* @__PURE__ */ d("h2", {
161
- className: "text-lg font-semibold text-text-primary",
162
- children: h("billing.plans.pricing", "Pricing")
163
- })
164
- }), /* @__PURE__ */ f("div", {
165
- className: "p-6",
166
- children: [/* @__PURE__ */ f("div", {
164
+ children: [/* @__PURE__ */ f(m, {
165
+ className: "p-6",
166
+ children: [
167
+ /* @__PURE__ */ d("h2", {
168
+ className: "text-lg font-semibold text-text-primary mb-4",
169
+ children: _("billing.plans.pricing", "Pricing")
170
+ }),
171
+ /* @__PURE__ */ f("div", {
167
172
  className: "flex items-baseline gap-1",
168
173
  children: [/* @__PURE__ */ d("span", {
169
174
  className: "text-4xl font-bold text-text-primary",
170
- children: e(y.price, y.currency)
175
+ children: e(x.price, x.currency)
171
176
  }), /* @__PURE__ */ f("span", {
172
177
  className: "text-lg text-text-muted",
173
- children: ["/ ", t(y.duration)]
178
+ children: ["/ ", t(x.duration)]
174
179
  })]
175
- }), /* @__PURE__ */ f("p", {
180
+ }),
181
+ /* @__PURE__ */ f("p", {
176
182
  className: "text-sm text-text-muted mt-2",
177
183
  children: [
178
- h("billing.plans.billed", "Billed"),
184
+ _("billing.plans.billed", "Billed"),
179
185
  " ",
180
- y.duration === "monthly" ? h("billing.plans.everyMonth", "every month") : h("billing.plans.annually", "annually")
186
+ x.duration === "monthly" ? _("billing.plans.everyMonth", "every month") : _("billing.plans.annually", "annually")
181
187
  ]
182
- })]
183
- })]
188
+ })
189
+ ]
184
190
  }), /* @__PURE__ */ f("div", {
185
191
  className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
186
192
  children: [/* @__PURE__ */ d("div", {
@@ -188,20 +194,20 @@ var h = () => {
188
194
  children: /* @__PURE__ */ f("h2", {
189
195
  className: "text-lg font-semibold text-text-primary",
190
196
  children: [
191
- h("billing.plans.featuresAndQuotas", "Features & Quotas"),
197
+ _("billing.plans.featuresAndQuotas", "Features & Quotas"),
192
198
  " (",
193
- T.length,
199
+ D.length,
194
200
  ")"
195
201
  ]
196
202
  })
197
203
  }), /* @__PURE__ */ d("div", {
198
204
  className: "p-6",
199
- children: T.length === 0 ? /* @__PURE__ */ d("p", {
205
+ children: D.length === 0 ? /* @__PURE__ */ d("p", {
200
206
  className: "text-sm text-text-muted",
201
- children: h("billing.plans.noFeatures", "No features configured for this plan.")
207
+ children: _("billing.plans.noFeatures", "No features configured for this plan.")
202
208
  }) : /* @__PURE__ */ d("ul", {
203
209
  className: "space-y-3",
204
- children: T.map((e) => /* @__PURE__ */ f("li", {
210
+ children: D.map((e) => /* @__PURE__ */ f("li", {
205
211
  className: "flex items-start gap-3 p-3 rounded-lg bg-bg-sunken/30",
206
212
  children: [/* @__PURE__ */ d("div", {
207
213
  className: "size-8 rounded-full bg-status-success-bg-subtle flex items-center justify-center shrink-0",
@@ -251,52 +257,52 @@ var h = () => {
251
257
  className: "p-6 border-b border-border-subtle bg-bg-sunken/30",
252
258
  children: /* @__PURE__ */ d("h2", {
253
259
  className: "text-lg font-semibold text-text-primary",
254
- children: h("billing.plans.planDetails", "Plan Details")
260
+ children: _("billing.plans.planDetails", "Plan Details")
255
261
  })
256
262
  }), /* @__PURE__ */ f("div", {
257
263
  className: "p-6 space-y-4",
258
264
  children: [
259
265
  /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
260
266
  className: "text-sm text-text-muted",
261
- children: h("billing.plans.planName", "Plan Name")
267
+ children: _("billing.plans.planName", "Plan Name")
262
268
  }), /* @__PURE__ */ d("p", {
263
269
  className: "text-sm font-medium text-text-primary mt-0.5",
264
- children: y.name
270
+ children: x.name
265
271
  })] }),
266
272
  /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
267
273
  className: "text-sm text-text-muted",
268
- children: h("billing.plans.status", "Status")
274
+ children: _("billing.plans.status", "Status")
269
275
  }), /* @__PURE__ */ d("p", {
270
276
  className: "text-sm text-text-primary mt-0.5",
271
277
  children: /* @__PURE__ */ d("span", {
272
- className: `inline-flex items-center px-2 py-0.5 text-xs font-medium rounded ${y.isActive ? "bg-status-success-bg-subtle text-status-success-text" : "bg-bg-sunken text-text-muted"}`,
273
- children: y.isActive ? "Active" : "Inactive"
278
+ className: `inline-flex items-center px-2 py-0.5 text-xs font-medium rounded ${x.isActive ? "bg-status-success-bg-subtle text-status-success-text" : "bg-bg-sunken text-text-muted"}`,
279
+ children: x.isActive ? "Active" : "Inactive"
274
280
  })
275
281
  })] }),
276
282
  /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
277
283
  className: "text-sm text-text-muted",
278
- children: h("billing.plans.price", "Price")
284
+ children: _("billing.plans.price", "Price")
279
285
  }), /* @__PURE__ */ f("p", {
280
286
  className: "text-sm font-medium text-text-primary mt-0.5",
281
287
  children: [
282
- e(y.price, y.currency),
288
+ e(x.price, x.currency),
283
289
  " / ",
284
- t(y.duration)
290
+ t(x.duration)
285
291
  ]
286
292
  })] }),
287
293
  /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
288
294
  className: "text-sm text-text-muted",
289
- children: h("billing.plans.currency", "Currency")
295
+ children: _("billing.plans.currency", "Currency")
290
296
  }), /* @__PURE__ */ d("p", {
291
297
  className: "text-sm text-text-primary mt-0.5",
292
- children: y.currency
298
+ children: x.currency
293
299
  })] }),
294
300
  /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
295
301
  className: "text-sm text-text-muted",
296
- children: h("billing.plans.billingPeriod", "Billing Period")
302
+ children: _("billing.plans.billingPeriod", "Billing Period")
297
303
  }), /* @__PURE__ */ d("p", {
298
304
  className: "text-sm text-text-primary mt-0.5",
299
- children: n(y.duration)
305
+ children: n(x.duration)
300
306
  })] })
301
307
  ]
302
308
  })]
@@ -306,7 +312,7 @@ var h = () => {
306
312
  className: "p-6 border-b border-border-subtle bg-bg-sunken/30",
307
313
  children: /* @__PURE__ */ d("h2", {
308
314
  className: "text-lg font-semibold text-text-primary",
309
- children: h("billing.plans.statistics", "Statistics")
315
+ children: _("billing.plans.statistics", "Statistics")
310
316
  })
311
317
  }), /* @__PURE__ */ d("div", {
312
318
  className: "p-6 space-y-4",
@@ -314,10 +320,10 @@ var h = () => {
314
320
  className: "flex items-center justify-between",
315
321
  children: [/* @__PURE__ */ d("p", {
316
322
  className: "text-sm text-text-muted",
317
- children: h("billing.plans.uniqueQuotas", "Unique Quotas")
323
+ children: _("billing.plans.uniqueQuotas", "Unique Quotas")
318
324
  }), /* @__PURE__ */ d("p", {
319
325
  className: "font-semibold text-text-primary",
320
- children: T.length
326
+ children: D.length
321
327
  })]
322
328
  })
323
329
  })]
@@ -346,17 +352,17 @@ var h = () => {
346
352
  }),
347
353
  /* @__PURE__ */ d("h2", {
348
354
  className: "text-lg font-semibold text-text-primary",
349
- children: h("billing.accessDenied.title", "Access Denied")
355
+ children: _("billing.accessDenied.title", "Access Denied")
350
356
  }),
351
357
  /* @__PURE__ */ d("p", {
352
358
  className: "text-sm text-text-muted max-w-sm",
353
- children: h("billing.plans.noViewPlanPermission", "You don't have permission to view this plan.")
359
+ children: _("billing.plans.noViewPlanPermission", "You don't have permission to view this plan.")
354
360
  })
355
361
  ]
356
362
  })
357
363
  });
358
364
  };
359
365
  //#endregion
360
- export { h as PlanDetailPage };
366
+ export { _ as PlanDetailPage };
361
367
 
362
368
  //# sourceMappingURL=PlanDetailPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlanDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanDetailPage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Detail Page\n * Displays detailed information about a single plan\n */\n\nimport { type FC, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlan, usePlanMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n} from '../../../shared/utils/format';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\nimport type { PlanFeature } 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 quotaType: string;\n limitType: string;\n totalValue: number;\n count: number;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n */\nfunction combineQuotas(features: PlanFeature[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n\n for (const feature of features) {\n if (!feature.quota) continue;\n\n const quotaId = feature.quota.id;\n const limits = feature.quota.limits as { type?: string; value?: number } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = getPlanFeatureQuantity(feature);\n\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n existing.count += 1;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n quotaType: feature.quota.quotaType ?? '',\n limitType,\n totalValue: limitValue,\n count: 1,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\nexport const PlanDetailPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { planId } = useParams<{ planId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { plan, isLoading, error, refetch } = usePlan(planId);\n const { togglePlan, isToggling } = usePlanMutations();\n\n // Combine duplicate quotas for cleaner display (must be before any returns)\n const combinedQuotas = useMemo(() => combineQuotas(plan?.features || []), [plan?.features]);\n\n // Permission check\n if (!permissions.canViewPlans) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.accessDenied.title', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-muted max-w-sm\">\n {tr(\n 'billing.plans.noViewPlanPermission',\n \"You don't have permission to view this plan.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-3/4 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-10 w-full bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || !plan) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.planNotFound', 'Plan not found')}\n </h2>\n <p className=\"text-sm text-text-muted\">\n {error?.message ||\n tr(\n 'billing.plans.planNotFoundMessage',\n 'The plan you are looking for does not exist.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Back to Plans\n </button>\n </div>\n </div>\n );\n }\n\n const handleToggle = async () => {\n if (!permissions.canManagePlans) return;\n try {\n await togglePlan(plan.id, !plan.isActive);\n await refetch();\n } catch (err) {\n console.error('Failed to toggle plan:', err);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-4\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"p-2 -ml-2 text-text-muted hover:text-text-primary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 19l-7-7 7-7\"\n />\n </svg>\n </button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {plan.name}\n </h1>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${\n plan.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {plan.isActive ? 'Active' : 'Inactive'}\n </span>\n </div>\n <p className=\"text-sm text-text-muted mt-1\">\n {formatPlanDurationLabel(plan.duration)} billing plan\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {permissions.canCreateSubscription && plan.isActive && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/checkout/${plan.id}`)}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Subscribe to Plan\n </button>\n )}\n {permissions.canManagePlans && (\n <>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/plans/${plan.id}/edit`)}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Edit Plan\n </button>\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={isToggling}\n className={`px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors ${\n plan.isActive\n ? 'text-status-error-text hover:bg-status-error-bg-subtle'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {isToggling\n ? tr('billing.plans.processing', 'Processing...')\n : plan.isActive\n ? tr('billing.plans.deactivate', 'Deactivate')\n : tr('billing.plans.activate', 'Activate')}\n </button>\n </>\n )}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Pricing Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.pricing', 'Pricing')}\n </h2>\n </div>\n <div className=\"p-6\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold text-text-primary\">\n {formatCurrency(plan.price, plan.currency)}\n </span>\n <span className=\"text-lg text-text-muted\">\n / {formatPlanDuration(plan.duration)}\n </span>\n </div>\n <p className=\"text-sm text-text-muted mt-2\">\n {tr('billing.plans.billed', 'Billed')}{' '}\n {plan.duration === 'monthly'\n ? tr('billing.plans.everyMonth', 'every month')\n : tr('billing.plans.annually', 'annually')}\n </p>\n </div>\n </div>\n\n {/* Features Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.featuresAndQuotas', 'Features & Quotas')} (\n {combinedQuotas.length})\n </h2>\n </div>\n <div className=\"p-6\">\n {combinedQuotas.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.noFeatures', 'No features configured for this plan.')}\n </p>\n ) : (\n <ul className=\"space-y-3\">\n {combinedQuotas.map((quota) => (\n <li\n key={quota.quotaId}\n className=\"flex items-start gap-3 p-3 rounded-lg bg-bg-sunken/30\"\n >\n <div className=\"size-8 rounded-full bg-status-success-bg-subtle flex items-center justify-center shrink-0\">\n <svg\n className=\"size-4 text-status-success-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <p className=\"font-medium text-text-primary\">{quota.name}</p>\n <span className=\"font-semibold text-text-primary\">\n {quota.totalValue.toLocaleString()}\n </span>\n </div>\n <div className=\"flex items-center gap-2 mt-1\">\n <span className=\"text-sm text-text-muted capitalize\">\n Type: {quota.limitType}\n </span>\n {quota.quotaType && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-bg-sunken text-text-muted rounded\">\n {quota.quotaType}\n </span>\n )}\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Plan Details Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.planDetails', 'Plan Details')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.planName', 'Plan Name')}\n </p>\n <p className=\"text-sm font-medium text-text-primary mt-0.5\">{plan.name}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">{tr('billing.plans.status', 'Status')}</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n <span\n className={`inline-flex items-center px-2 py-0.5 text-xs font-medium rounded ${\n plan.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {plan.isActive ? 'Active' : 'Inactive'}\n </span>\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">{tr('billing.plans.price', 'Price')}</p>\n <p className=\"text-sm font-medium text-text-primary mt-0.5\">\n {formatCurrency(plan.price, plan.currency)} / {formatPlanDuration(plan.duration)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.currency', 'Currency')}\n </p>\n <p className=\"text-sm text-text-primary mt-0.5\">{plan.currency}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.billingPeriod', 'Billing Period')}\n </p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatPlanDurationLabel(plan.duration)}\n </p>\n </div>\n </div>\n </div>\n\n {/* Quick Stats Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.statistics', 'Statistics')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.uniqueQuotas', 'Unique Quotas')}\n </p>\n <p className=\"font-semibold text-text-primary\">{combinedQuotas.length}</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PlanDetailPage.js","names":[],"sources":["../../../../../src/billing/modules/plans/pages/PlanDetailPage.tsx"],"sourcesContent":["/**\n * Plans Module - Plan Detail Page\n * Displays detailed information about a single plan\n */\n\nimport { type FC, useMemo } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePlan, usePlanMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n formatCurrency,\n formatPlanDuration,\n formatPlanDurationLabel,\n} from '../../../shared/utils/format';\nimport { getPlanFeatureQuantity } from '../../../shared/utils/planFeatureQuantity';\nimport type { PlanFeature } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PagePurpose, EmphasisPanel } from '@burdenoff/fe-libs/ui';\n\n/**\n * Combined quota info for display\n */\ninterface CombinedQuota {\n quotaId: string;\n name: string;\n quotaType: string;\n limitType: string;\n totalValue: number;\n count: number;\n}\n\n/**\n * Combines duplicate quotas by quota ID and aggregates their values\n */\nfunction combineQuotas(features: PlanFeature[]): CombinedQuota[] {\n const quotaMap = new Map<string, CombinedQuota>();\n\n for (const feature of features) {\n if (!feature.quota) continue;\n\n const quotaId = feature.quota.id;\n const limits = feature.quota.limits as { type?: string; value?: number } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = getPlanFeatureQuantity(feature);\n\n const existing = quotaMap.get(quotaId);\n if (existing) {\n existing.totalValue += limitValue;\n existing.count += 1;\n } else {\n quotaMap.set(quotaId, {\n quotaId,\n name: feature.quota.name ?? 'Unknown Quota',\n quotaType: feature.quota.quotaType ?? '',\n limitType,\n totalValue: limitValue,\n count: 1,\n });\n }\n }\n\n return Array.from(quotaMap.values());\n}\n\nexport const PlanDetailPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const { planId } = useParams<{ planId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const { plan, isLoading, error, refetch } = usePlan(planId);\n const { togglePlan, isToggling } = usePlanMutations();\n\n // Combine duplicate quotas for cleaner display (must be before any returns)\n const combinedQuotas = useMemo(() => combineQuotas(plan?.features || []), [plan?.features]);\n\n // Permission check\n if (!permissions.canViewPlans) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"size-12 mx-auto rounded-full bg-bg-sunken flex items-center justify-center\">\n <svg\n className=\"size-6 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.accessDenied.title', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-muted max-w-sm\">\n {tr(\n 'billing.plans.noViewPlanPermission',\n \"You don't have permission to view this plan.\"\n )}\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (isLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-4 w-3/4 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n <div className=\"border border-border-subtle rounded-lg p-6 space-y-4\">\n <div className=\"h-6 w-24 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-10 w-full bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n </div>\n );\n }\n\n // Error state\n if (error || !plan) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.planNotFound', 'Plan not found')}\n </h2>\n <p className=\"text-sm text-text-muted\">\n {error?.message ||\n tr(\n 'billing.plans.planNotFoundMessage',\n 'The plan you are looking for does not exist.'\n )}\n </p>\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Back to Plans\n </button>\n </div>\n </div>\n );\n }\n\n const handleToggle = async () => {\n if (!permissions.canManagePlans) return;\n try {\n await togglePlan(plan.id, !plan.isActive);\n await refetch();\n } catch (err) {\n console.error('Failed to toggle plan:', err);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"flex items-start gap-4\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/plans')}\n className=\"p-2 -ml-2 text-text-muted hover:text-text-primary hover:bg-bg-sunken rounded-md transition-colors\"\n >\n <svg className=\"size-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M15 19l-7-7 7-7\"\n />\n </svg>\n </button>\n <div>\n <div className=\"flex items-center gap-3\">\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n {plan.name}\n </h1>\n <span\n className={`px-2 py-1 text-xs font-medium rounded ${\n plan.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {plan.isActive ? 'Active' : 'Inactive'}\n </span>\n </div>\n <p className=\"text-sm text-text-muted mt-1\">\n {formatPlanDurationLabel(plan.duration)} billing plan\n </p>\n <PagePurpose className=\"mt-3\">\n {tr(\n 'billing.plans.detailPurpose',\n 'A full breakdown of this plan — its price, billing period and every quota and feature it grants. Use it to decide whether the plan fits before subscribing, or (as an admin) to edit or activate it.'\n )}\n </PagePurpose>\n </div>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {permissions.canCreateSubscription && plan.isActive && (\n <button\n type=\"button\"\n onClick={() => navigateTo(`/checkout/${plan.id}`)}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Subscribe to Plan\n </button>\n )}\n {permissions.canManagePlans && (\n <>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/plans/${plan.id}/edit`)}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Edit Plan\n </button>\n <button\n type=\"button\"\n onClick={handleToggle}\n disabled={isToggling}\n className={`px-4 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors ${\n plan.isActive\n ? 'text-status-error-text hover:bg-status-error-bg-subtle'\n : 'text-status-success-text hover:bg-status-success-bg-subtle'\n }`}\n >\n {isToggling\n ? tr('billing.plans.processing', 'Processing...')\n : plan.isActive\n ? tr('billing.plans.deactivate', 'Deactivate')\n : tr('billing.plans.activate', 'Activate')}\n </button>\n </>\n )}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n {/* Main Content */}\n <div className=\"lg:col-span-2 space-y-6\">\n {/* Pricing Card — primary emphasis zone */}\n <EmphasisPanel className=\"p-6\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('billing.plans.pricing', 'Pricing')}\n </h2>\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold text-text-primary\">\n {formatCurrency(plan.price, plan.currency)}\n </span>\n <span className=\"text-lg text-text-muted\">/ {formatPlanDuration(plan.duration)}</span>\n </div>\n <p className=\"text-sm text-text-muted mt-2\">\n {tr('billing.plans.billed', 'Billed')}{' '}\n {plan.duration === 'monthly'\n ? tr('billing.plans.everyMonth', 'every month')\n : tr('billing.plans.annually', 'annually')}\n </p>\n </EmphasisPanel>\n\n {/* Features Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.featuresAndQuotas', 'Features & Quotas')} (\n {combinedQuotas.length})\n </h2>\n </div>\n <div className=\"p-6\">\n {combinedQuotas.length === 0 ? (\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.noFeatures', 'No features configured for this plan.')}\n </p>\n ) : (\n <ul className=\"space-y-3\">\n {combinedQuotas.map((quota) => (\n <li\n key={quota.quotaId}\n className=\"flex items-start gap-3 p-3 rounded-lg bg-bg-sunken/30\"\n >\n <div className=\"size-8 rounded-full bg-status-success-bg-subtle flex items-center justify-center shrink-0\">\n <svg\n className=\"size-4 text-status-success-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M5 13l4 4L19 7\"\n />\n </svg>\n </div>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <p className=\"font-medium text-text-primary\">{quota.name}</p>\n <span className=\"font-semibold text-text-primary\">\n {quota.totalValue.toLocaleString()}\n </span>\n </div>\n <div className=\"flex items-center gap-2 mt-1\">\n <span className=\"text-sm text-text-muted capitalize\">\n Type: {quota.limitType}\n </span>\n {quota.quotaType && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-bg-sunken text-text-muted rounded\">\n {quota.quotaType}\n </span>\n )}\n </div>\n </div>\n </li>\n ))}\n </ul>\n )}\n </div>\n </div>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-6\">\n {/* Plan Details Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.planDetails', 'Plan Details')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.planName', 'Plan Name')}\n </p>\n <p className=\"text-sm font-medium text-text-primary mt-0.5\">{plan.name}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">{tr('billing.plans.status', 'Status')}</p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n <span\n className={`inline-flex items-center px-2 py-0.5 text-xs font-medium rounded ${\n plan.isActive\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {plan.isActive ? 'Active' : 'Inactive'}\n </span>\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">{tr('billing.plans.price', 'Price')}</p>\n <p className=\"text-sm font-medium text-text-primary mt-0.5\">\n {formatCurrency(plan.price, plan.currency)} / {formatPlanDuration(plan.duration)}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.currency', 'Currency')}\n </p>\n <p className=\"text-sm text-text-primary mt-0.5\">{plan.currency}</p>\n </div>\n <div>\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.billingPeriod', 'Billing Period')}\n </p>\n <p className=\"text-sm text-text-primary mt-0.5\">\n {formatPlanDurationLabel(plan.duration)}\n </p>\n </div>\n </div>\n </div>\n\n {/* Quick Stats Card */}\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-6 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.plans.statistics', 'Statistics')}\n </h2>\n </div>\n <div className=\"p-6 space-y-4\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-sm text-text-muted\">\n {tr('billing.plans.uniqueQuotas', 'Unique Quotas')}\n </p>\n <p className=\"font-semibold text-text-primary\">{combinedQuotas.length}</p>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}