@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":"QuotaTemplateEditPage.js","names":[],"sources":["../../../../../src/billing/modules/quota/pages/QuotaTemplateEditPage.tsx"],"sourcesContent":["/**\n * Quota Module - Quota Template Edit Page\n * Form to edit an existing quota template\n */\n\nimport { useState, useEffect, type FC, type FormEvent } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useQuotaTemplate, useQuotaTemplateMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport { isServerError } from '../../../shared/utils';\nimport { QuotaResetPeriod, type UpdateQuotaInput } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const QuotaTemplateEditPage: 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 { quotaId } = useParams<{ quotaId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const {\n quota,\n isLoading: isQuotaLoading,\n error: quotaError,\n refetch,\n } = useQuotaTemplate(quotaId);\n const { updateQuota, isUpdating } = useQuotaTemplateMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [displayName, setDisplayName] = useState('');\n const [description, setDescription] = useState('');\n const [limitType, setLimitType] = useState('numeric');\n const [limitValue, setLimitValue] = useState('');\n const [unit, setUnit] = useState('');\n const [reusable, setReusable] = useState(false);\n const [resetPeriod, setResetPeriod] = useState<QuotaResetPeriod>(QuotaResetPeriod.NEVER);\n const [isActive, setIsActive] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Populate form when quota data loads\n useEffect(() => {\n if (quota) {\n setName(quota.name ?? '');\n setReusable(quota.reusable ?? false);\n setResetPeriod(quota.resetPeriod ?? QuotaResetPeriod.NEVER);\n setIsActive(quota.isActive ?? true);\n\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n setLimitType(limits?.type ?? 'numeric');\n setLimitValue(String(limits?.value ?? ''));\n setUnit(limits?.unit ?? '');\n\n const ctx = quota.context as { displayName?: string; description?: string } | null;\n setDisplayName(ctx?.displayName ?? '');\n setDescription(ctx?.description ?? '');\n }\n }, [quota]);\n\n // Permission check\n if (!permissions.canManageQuotas) {\n return <AccessDenied message=\"You don't have permission to edit quotas.\" />;\n }\n\n // Loading state\n if (isQuotaLoading) {\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].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 (quotaError && isServerError(quotaError)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load quota. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n if (!quota) {\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\">Quota not found</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas')}\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 Quotas\n </button>\n </div>\n </div>\n );\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('Quota name is required');\n return;\n }\n if (!limitValue || isNaN(Number(limitValue)) || Number(limitValue) < 0) {\n setError('Valid limit value is required');\n return;\n }\n if (!unit.trim()) {\n setError('Unit is required (e.g., bots, GB, seats)');\n return;\n }\n\n const input: UpdateQuotaInput = {\n id: quotaId!,\n name: name.trim(),\n limits: {\n type: limitType,\n value: Number(limitValue),\n unit: unit.trim(),\n },\n reusable,\n isActive,\n resetPeriod,\n context: {\n displayName: displayName.trim() || name.trim(),\n description: description.trim(),\n },\n };\n\n try {\n await updateQuota(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo(`/quotas/${quotaId}`), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update quota');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={`Edit Quota: ${(quota.context as { displayName?: string } | null)?.displayName ?? quota.name}`}\n description=\"Update the quota template configuration\"\n />\n\n <form onSubmit={handleSubmit} className=\"max-w-2xl 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 Quota updated successfully! Redirecting…\n </p>\n </div>\n )}\n\n {/* Basic Info */}\n <div className=\"border border-border-seam 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\">Basic Information</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Machine Key (name) */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary mb-1\">\n Quota Key *\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)] font-mono text-sm\"\n placeholder=\"e.g., sessions-started, tunnels-created\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Internal machine key, must match the <code>quotaName</code> used in{' '}\n <code>@usage</code> directives. Never shown to users.\n </p>\n </div>\n\n {/* Display Name */}\n <div>\n <label\n htmlFor=\"displayName\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Display Name *\n </label>\n <input\n id=\"displayName\"\n type=\"text\"\n value={displayName}\n onChange={(e) => setDisplayName(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=\"e.g., Tunnels, Session Starts, Webhooks\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Human-readable name shown to users in billing and usage UI.\n </p>\n </div>\n\n {/* Description */}\n <div>\n <label\n htmlFor=\"description\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Description\n </label>\n <textarea\n id=\"description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\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)] resize-none\"\n placeholder=\"e.g., Active tunnels created through VibeControls (slot freed on delete)\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Short explanation shown in billing and usage detail views.\n </p>\n </div>\n\n {/* Quota Type (read-only) */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Quota Type</label>\n <div className=\"px-3 py-2 border border-border-subtle rounded-md bg-bg-sunken/50 text-text-muted\">\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n <span className=\"text-xs ml-2\">(cannot be changed)</span>\n </div>\n </div>\n\n {/* Reusable */}\n <div className=\"flex items-center gap-3\">\n <input\n id=\"reusable\"\n type=\"checkbox\"\n checked={reusable}\n onChange={(e) => setReusable(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <div>\n <label htmlFor=\"reusable\" className=\"text-sm font-medium text-text-primary\">\n Reusable Quota\n </label>\n <p className=\"text-xs text-text-muted\">\n Enable for quotas like seats or bots that can be freed up\n </p>\n </div>\n </div>\n\n {/* Reset Period */}\n <div>\n <label\n htmlFor=\"resetPeriod\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Reset Period\n </label>\n <select\n id=\"resetPeriod\"\n value={resetPeriod}\n onChange={(e) => setResetPeriod(e.target.value as QuotaResetPeriod)}\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={QuotaResetPeriod.NEVER}>Never (one-time limit)</option>\n <option value={QuotaResetPeriod.DAILY}>Daily (resets every day)</option>\n <option value={QuotaResetPeriod.MONTHLY}>Monthly (resets every month)</option>\n </select>\n <p className=\"text-xs text-text-muted mt-1\">\n How often the quota usage counter is reset to zero\n </p>\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 Quota is active\n </label>\n </div>\n </div>\n </div>\n\n {/* Limits Configuration */}\n <div className=\"border border-border-seam 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\">Limits Configuration</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Limit Type */}\n <div>\n <label\n htmlFor=\"limitType\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Limit Type *\n </label>\n <select\n id=\"limitType\"\n value={limitType}\n onChange={(e) => setLimitType(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=\"numeric\">Numeric (count)</option>\n <option value=\"storage\">Storage (bytes)</option>\n <option value=\"duration\">Duration (seconds)</option>\n <option value=\"percentage\">Percentage</option>\n </select>\n </div>\n\n {/* Limit Value */}\n <div>\n <label\n htmlFor=\"limitValue\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Limit Value *\n </label>\n <input\n id=\"limitValue\"\n type=\"number\"\n min=\"0\"\n value={limitValue}\n onChange={(e) => setLimitValue(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=\"1000\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">The maximum limit value for this quota</p>\n </div>\n\n {/* Unit */}\n <div>\n <label htmlFor=\"unit\" className=\"block text-sm font-medium text-text-primary mb-1\">\n Unit *\n </label>\n <input\n id=\"unit\"\n type=\"text\"\n value={unit}\n onChange={(e) => setUnit(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=\"e.g., bots, GB, MB, seats, workflow runs\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n The unit of measurement displayed with usage values (e.g., &quot;2/6 bots&quot;)\n </p>\n </div>\n </div>\n </div>\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.quota.saving', 'Saving...')\n : tr('billing.quota.saveChanges', 'Save Changes')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/quotas/${quotaId}`)}\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":"QuotaTemplateEditPage.js","names":[],"sources":["../../../../../src/billing/modules/quota/pages/QuotaTemplateEditPage.tsx"],"sourcesContent":["/**\n * Quota Module - Quota Template Edit Page\n * Form to edit an existing quota template\n */\n\nimport { useState, useEffect, type FC, type FormEvent } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useQuotaTemplate, useQuotaTemplateMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader, ServerError } from '../../../shared/components';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { isServerError } from '../../../shared/utils';\nimport { QuotaResetPeriod, type UpdateQuotaInput } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\nexport const QuotaTemplateEditPage: 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 { quotaId } = useParams<{ quotaId: string }>();\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const {\n quota,\n isLoading: isQuotaLoading,\n error: quotaError,\n refetch,\n } = useQuotaTemplate(quotaId);\n const { updateQuota, isUpdating } = useQuotaTemplateMutations();\n\n // Form state\n const [name, setName] = useState('');\n const [displayName, setDisplayName] = useState('');\n const [description, setDescription] = useState('');\n const [limitType, setLimitType] = useState('numeric');\n const [limitValue, setLimitValue] = useState('');\n const [unit, setUnit] = useState('');\n const [reusable, setReusable] = useState(false);\n const [resetPeriod, setResetPeriod] = useState<QuotaResetPeriod>(QuotaResetPeriod.NEVER);\n const [isActive, setIsActive] = useState(true);\n const [error, setError] = useState<string | null>(null);\n const [success, setSuccess] = useState(false);\n\n // Populate form when quota data loads\n useEffect(() => {\n if (quota) {\n setName(quota.name ?? '');\n setReusable(quota.reusable ?? false);\n setResetPeriod(quota.resetPeriod ?? QuotaResetPeriod.NEVER);\n setIsActive(quota.isActive ?? true);\n\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n setLimitType(limits?.type ?? 'numeric');\n setLimitValue(String(limits?.value ?? ''));\n setUnit(limits?.unit ?? '');\n\n const ctx = quota.context as { displayName?: string; description?: string } | null;\n setDisplayName(ctx?.displayName ?? '');\n setDescription(ctx?.description ?? '');\n }\n }, [quota]);\n\n // Permission check\n if (!permissions.canManageQuotas) {\n return <AccessDenied message=\"You don't have permission to edit quotas.\" />;\n }\n\n // Loading state\n if (isQuotaLoading) {\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].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 (quotaError && isServerError(quotaError)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load quota. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n if (!quota) {\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\">Quota not found</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas')}\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 Quotas\n </button>\n </div>\n </div>\n );\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('Quota name is required');\n return;\n }\n if (!limitValue || isNaN(Number(limitValue)) || Number(limitValue) < 0) {\n setError('Valid limit value is required');\n return;\n }\n if (!unit.trim()) {\n setError('Unit is required (e.g., bots, GB, seats)');\n return;\n }\n\n const input: UpdateQuotaInput = {\n id: quotaId!,\n name: name.trim(),\n limits: {\n type: limitType,\n value: Number(limitValue),\n unit: unit.trim(),\n },\n reusable,\n isActive,\n resetPeriod,\n context: {\n displayName: displayName.trim() || name.trim(),\n description: description.trim(),\n },\n };\n\n try {\n await updateQuota(input);\n setSuccess(true);\n // Navigate back after short delay\n setTimeout(() => navigateTo(`/quotas/${quotaId}`), 1500);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to update quota');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={`Edit Quota: ${(quota.context as { displayName?: string } | null)?.displayName ?? quota.name}`}\n description=\"Update the quota template configuration\"\n />\n\n <PagePurpose>\n Adjust this quota template&apos;s configuration. Changes apply wherever the template is\n referenced, so review which plans and add-ons use it before changing its type or unit.\n </PagePurpose>\n\n <form onSubmit={handleSubmit} className=\"max-w-2xl 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 Quota updated successfully! Redirecting…\n </p>\n </div>\n )}\n\n {/* Basic Info */}\n <div className=\"border border-border-seam 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\">Basic Information</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Machine Key (name) */}\n <div>\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary mb-1\">\n Quota Key *\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)] font-mono text-sm\"\n placeholder=\"e.g., sessions-started, tunnels-created\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Internal machine key, must match the <code>quotaName</code> used in{' '}\n <code>@usage</code> directives. Never shown to users.\n </p>\n </div>\n\n {/* Display Name */}\n <div>\n <label\n htmlFor=\"displayName\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Display Name *\n </label>\n <input\n id=\"displayName\"\n type=\"text\"\n value={displayName}\n onChange={(e) => setDisplayName(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=\"e.g., Tunnels, Session Starts, Webhooks\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Human-readable name shown to users in billing and usage UI.\n </p>\n </div>\n\n {/* Description */}\n <div>\n <label\n htmlFor=\"description\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Description\n </label>\n <textarea\n id=\"description\"\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n rows={2}\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)] resize-none\"\n placeholder=\"e.g., Active tunnels created through VibeControls (slot freed on delete)\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Short explanation shown in billing and usage detail views.\n </p>\n </div>\n\n {/* Quota Type (read-only) */}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">Quota Type</label>\n <div className=\"px-3 py-2 border border-border-subtle rounded-md bg-bg-sunken/50 text-text-muted\">\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n <span className=\"text-xs ml-2\">(cannot be changed)</span>\n </div>\n </div>\n\n {/* Reusable */}\n <div className=\"flex items-center gap-3\">\n <input\n id=\"reusable\"\n type=\"checkbox\"\n checked={reusable}\n onChange={(e) => setReusable(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <div>\n <label htmlFor=\"reusable\" className=\"text-sm font-medium text-text-primary\">\n Reusable Quota\n </label>\n <p className=\"text-xs text-text-muted\">\n Enable for quotas like seats or bots that can be freed up\n </p>\n </div>\n </div>\n\n {/* Reset Period */}\n <div>\n <label\n htmlFor=\"resetPeriod\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Reset Period\n </label>\n <select\n id=\"resetPeriod\"\n value={resetPeriod}\n onChange={(e) => setResetPeriod(e.target.value as QuotaResetPeriod)}\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={QuotaResetPeriod.NEVER}>Never (one-time limit)</option>\n <option value={QuotaResetPeriod.DAILY}>Daily (resets every day)</option>\n <option value={QuotaResetPeriod.MONTHLY}>Monthly (resets every month)</option>\n </select>\n <p className=\"text-xs text-text-muted mt-1\">\n How often the quota usage counter is reset to zero\n </p>\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 Quota is active\n </label>\n </div>\n </div>\n </div>\n\n {/* Limits Configuration */}\n <div className=\"border border-border-seam 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\">Limits Configuration</h2>\n </div>\n <div className=\"p-4 space-y-4\">\n {/* Limit Type */}\n <div>\n <label\n htmlFor=\"limitType\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Limit Type *\n </label>\n <select\n id=\"limitType\"\n value={limitType}\n onChange={(e) => setLimitType(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=\"numeric\">Numeric (count)</option>\n <option value=\"storage\">Storage (bytes)</option>\n <option value=\"duration\">Duration (seconds)</option>\n <option value=\"percentage\">Percentage</option>\n </select>\n </div>\n\n {/* Limit Value */}\n <div>\n <label\n htmlFor=\"limitValue\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Limit Value *\n </label>\n <input\n id=\"limitValue\"\n type=\"number\"\n min=\"0\"\n value={limitValue}\n onChange={(e) => setLimitValue(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=\"1000\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">The maximum limit value for this quota</p>\n </div>\n\n {/* Unit */}\n <div>\n <label htmlFor=\"unit\" className=\"block text-sm font-medium text-text-primary mb-1\">\n Unit *\n </label>\n <input\n id=\"unit\"\n type=\"text\"\n value={unit}\n onChange={(e) => setUnit(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=\"e.g., bots, GB, MB, seats, workflow runs\"\n required\n />\n <p className=\"text-xs text-text-muted mt-1\">\n The unit of measurement displayed with usage values (e.g., &quot;2/6 bots&quot;)\n </p>\n </div>\n </div>\n </div>\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.quota.saving', 'Saving...')\n : tr('billing.quota.saveChanges', 'Save Changes')}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/quotas/${quotaId}`)}\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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"}
@@ -9,25 +9,26 @@ import "../hooks/index.js";
9
9
  import { useState as s } from "react";
10
10
  import { Fragment as c, jsx as l, jsxs as u } from "react/jsx-runtime";
11
11
  import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
+ import { IllustratedEmptyState as f, PagePurpose as p } from "@burdenoff/fe-libs/ui";
12
13
  //#region src/billing/modules/quota/pages/QuotaTemplatesListPage.tsx
13
- var f = () => {
14
+ var m = () => {
14
15
  let { t: n } = d(), c = (e, t) => {
15
16
  let r = n(e);
16
17
  return r === e ? t : r;
17
- }, f = i(), h = r(), { quotas: g, isLoading: _, error: v, refetch: y } = o(), { toggleQuota: b, isToggling: x } = a(), [S, C] = s("grid"), [w, T] = s(!1), [E, D] = s("all");
18
- if (!h.canViewQuotas) return /* @__PURE__ */ l(e, { message: "You don't have permission to view quotas." });
19
- let O = g.filter((e) => !(!w && !e.isActive || E !== "all" && e.quotaType !== E)), k = async (e) => {
20
- if (h.canManageQuotas) try {
21
- await b(e.id, !e.isActive), await y();
18
+ }, m = i(), _ = r(), { quotas: v, isLoading: y, error: b, refetch: x } = o(), { toggleQuota: S, isToggling: C } = a(), [w, T] = s("grid"), [E, D] = s(!1), [O, k] = s("all");
19
+ if (!_.canViewQuotas) return /* @__PURE__ */ l(e, { message: "You don't have permission to view quotas." });
20
+ let A = v.filter((e) => !(!E && !e.isActive || O !== "all" && e.quotaType !== O)), j = async (e) => {
21
+ if (_.canManageQuotas) try {
22
+ await S(e.id, !e.isActive), await x();
22
23
  } catch (e) {
23
24
  console.error("Failed to toggle quota:", e);
24
25
  }
25
- }, A = (e) => {
26
- f(`/quotas/${e.id}`);
27
- }, j = (e) => {
28
- f(`/quotas/${e.id}/edit`);
26
+ }, M = (e) => {
27
+ m(`/quotas/${e.id}`);
28
+ }, N = (e) => {
29
+ m(`/quotas/${e.id}/edit`);
29
30
  };
30
- return _ && g.length === 0 ? /* @__PURE__ */ u("div", {
31
+ return y && v.length === 0 ? /* @__PURE__ */ u("div", {
31
32
  className: "space-y-6 p-6",
32
33
  children: [/* @__PURE__ */ l("div", { className: "h-8 w-48 bg-bg-sunken animate-pulse rounded" }), /* @__PURE__ */ l("div", {
33
34
  className: "grid grid-cols-1 md:grid-cols-3 gap-6",
@@ -47,7 +48,7 @@ var f = () => {
47
48
  ]
48
49
  }, e))
49
50
  })]
50
- }) : v ? /* @__PURE__ */ l("div", {
51
+ }) : b ? /* @__PURE__ */ l("div", {
51
52
  className: "flex items-center justify-center h-full min-h-[400px]",
52
53
  children: /* @__PURE__ */ u("div", {
53
54
  className: "text-center space-y-4",
@@ -73,11 +74,11 @@ var f = () => {
73
74
  }),
74
75
  /* @__PURE__ */ l("p", {
75
76
  className: "text-sm text-text-muted",
76
- children: v.message
77
+ children: b.message
77
78
  }),
78
79
  /* @__PURE__ */ l("button", {
79
80
  type: "button",
80
- onClick: () => y(),
81
+ onClick: () => x(),
81
82
  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",
82
83
  children: "Try Again"
83
84
  })
@@ -91,9 +92,9 @@ var f = () => {
91
92
  children: [/* @__PURE__ */ l(t, {
92
93
  title: c("billing.quota.quotaTemplatesTitle", "Quota Templates"),
93
94
  description: "Manage quota templates that can be assigned to plans and addons"
94
- }), h.canManageQuotas && /* @__PURE__ */ u("button", {
95
+ }), _.canManageQuotas && /* @__PURE__ */ u("button", {
95
96
  type: "button",
96
- onClick: () => f("/quotas/new"),
97
+ onClick: () => m("/quotas/new"),
97
98
  className: "inline-flex items-center gap-2 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",
98
99
  children: [/* @__PURE__ */ l("svg", {
99
100
  className: "size-4",
@@ -109,6 +110,7 @@ var f = () => {
109
110
  }), "Create Quota"]
110
111
  })]
111
112
  }),
113
+ /* @__PURE__ */ l(p, { children: c("billing.quota.templatesPurpose", "Quota templates are the reusable limit definitions you attach to plans and add-ons — like \"API calls per month\" or \"storage\". Create and manage them here once, then reference them across multiple plans instead of redefining the same limit each time.") }),
112
114
  /* @__PURE__ */ u("div", {
113
115
  className: "flex flex-wrap items-center gap-4 pb-4 border-b border-border-subtle",
114
116
  children: [
@@ -117,30 +119,30 @@ var f = () => {
117
119
  children: [
118
120
  /* @__PURE__ */ l("button", {
119
121
  type: "button",
120
- onClick: () => D("all"),
121
- className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${E === "all" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
122
+ onClick: () => k("all"),
123
+ className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${O === "all" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
122
124
  children: "All"
123
125
  }),
124
126
  /* @__PURE__ */ l("button", {
125
127
  type: "button",
126
- onClick: () => D("POOLED"),
127
- className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${E === "POOLED" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
128
+ onClick: () => k("POOLED"),
129
+ className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${O === "POOLED" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
128
130
  children: "Pooled"
129
131
  }),
130
132
  /* @__PURE__ */ l("button", {
131
133
  type: "button",
132
- onClick: () => D("PRODUCT_SPECIFIC"),
133
- className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${E === "PRODUCT_SPECIFIC" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
134
+ onClick: () => k("PRODUCT_SPECIFIC"),
135
+ className: `px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${O === "PRODUCT_SPECIFIC" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
134
136
  children: "Product Specific"
135
137
  })
136
138
  ]
137
139
  }),
138
- h.canManageQuotas && /* @__PURE__ */ u("label", {
140
+ _.canManageQuotas && /* @__PURE__ */ u("label", {
139
141
  className: "flex items-center gap-2 text-sm cursor-pointer",
140
142
  children: [/* @__PURE__ */ l("input", {
141
143
  type: "checkbox",
142
- checked: w,
143
- onChange: (e) => T(e.target.checked),
144
+ checked: E,
145
+ onChange: (e) => D(e.target.checked),
144
146
  className: "size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]"
145
147
  }), /* @__PURE__ */ l("span", {
146
148
  className: "text-text-muted",
@@ -151,8 +153,8 @@ var f = () => {
151
153
  className: "ml-auto inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken/50",
152
154
  children: [/* @__PURE__ */ l("button", {
153
155
  type: "button",
154
- onClick: () => C("grid"),
155
- className: `p-1.5 rounded-md transition-colors ${S === "grid" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
156
+ onClick: () => T("grid"),
157
+ className: `p-1.5 rounded-md transition-colors ${w === "grid" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
156
158
  title: "Grid view",
157
159
  children: /* @__PURE__ */ l("svg", {
158
160
  className: "size-4",
@@ -168,8 +170,8 @@ var f = () => {
168
170
  })
169
171
  }), /* @__PURE__ */ l("button", {
170
172
  type: "button",
171
- onClick: () => C("list"),
172
- className: `p-1.5 rounded-md transition-colors ${S === "list" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
173
+ onClick: () => T("list"),
174
+ className: `p-1.5 rounded-md transition-colors ${w === "list" ? "bg-bg-surface text-text-primary shadow-sm" : "text-text-muted hover:text-text-primary"}`,
173
175
  title: "List view",
174
176
  children: /* @__PURE__ */ l("svg", {
175
177
  className: "size-4",
@@ -187,63 +189,40 @@ var f = () => {
187
189
  })
188
190
  ]
189
191
  }),
190
- O.length === 0 ? /* @__PURE__ */ u("div", {
191
- className: "flex flex-col items-center justify-center py-16 border border-dashed border-border-seam rounded-card bg-bg-surface",
192
- children: [
193
- /* @__PURE__ */ l("div", {
194
- className: "size-12 rounded-full bg-[var(--color-accent-soft)] flex items-center justify-center mb-4",
195
- children: /* @__PURE__ */ l("svg", {
196
- className: "size-6 text-text-muted",
197
- fill: "none",
198
- viewBox: "0 0 24 24",
199
- stroke: "currentColor",
200
- children: /* @__PURE__ */ l("path", {
201
- strokeLinecap: "round",
202
- strokeLinejoin: "round",
203
- strokeWidth: 2,
204
- d: "M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
205
- })
206
- })
207
- }),
208
- /* @__PURE__ */ l("h3", {
209
- className: "text-lg font-medium text-text-primary",
210
- children: c("billing.quota.noQuotasFound", "No quotas found")
211
- }),
212
- /* @__PURE__ */ l("p", {
213
- className: "text-sm text-text-muted mt-1",
214
- children: w ? "No quotas match your current filters." : "There are no active quotas available."
215
- }),
216
- h.canManageQuotas && /* @__PURE__ */ l("button", {
217
- type: "button",
218
- onClick: () => f("/quotas/new"),
219
- className: "mt-4 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",
220
- children: "Create your first quota"
221
- })
222
- ]
223
- }) : S === "grid" ? /* @__PURE__ */ l("div", {
192
+ A.length === 0 ? /* @__PURE__ */ l(f, {
193
+ illustration: "empty-data",
194
+ title: c("billing.quota.noQuotasFound", "No quotas found"),
195
+ description: E ? "No quotas match your current filters." : "There are no active quotas available.",
196
+ action: _.canManageQuotas ? /* @__PURE__ */ l("button", {
197
+ type: "button",
198
+ onClick: () => m("/quotas/new"),
199
+ 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",
200
+ children: "Create your first quota"
201
+ }) : void 0
202
+ }) : w === "grid" ? /* @__PURE__ */ l("div", {
224
203
  className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6",
225
- children: O.map((e) => /* @__PURE__ */ l(p, {
204
+ children: A.map((e) => /* @__PURE__ */ l(h, {
226
205
  quota: e,
227
- onView: A,
228
- onEdit: j,
229
- onToggle: k,
230
- canManage: h.canManageQuotas,
231
- isToggling: x
206
+ onView: M,
207
+ onEdit: N,
208
+ onToggle: j,
209
+ canManage: _.canManageQuotas,
210
+ isToggling: C
232
211
  }, e.id))
233
212
  }) : /* @__PURE__ */ l("div", {
234
213
  className: "space-y-3",
235
- children: O.map((e) => /* @__PURE__ */ l(m, {
214
+ children: A.map((e) => /* @__PURE__ */ l(g, {
236
215
  quota: e,
237
- onView: A,
238
- onEdit: j,
239
- onToggle: k,
240
- canManage: h.canManageQuotas,
241
- isToggling: x
216
+ onView: M,
217
+ onEdit: N,
218
+ onToggle: j,
219
+ canManage: _.canManageQuotas,
220
+ isToggling: C
242
221
  }, e.id))
243
222
  })
244
223
  ]
245
224
  });
246
- }, p = ({ quota: e, onView: t, onEdit: r, onToggle: i, canManage: a, isToggling: o }) => {
225
+ }, h = ({ quota: e, onView: t, onEdit: r, onToggle: i, canManage: a, isToggling: o }) => {
247
226
  let s = e.limits, c = s?.type ?? "unknown", d = s?.value ?? 0, f = s?.unit ?? "";
248
227
  return /* @__PURE__ */ u("div", {
249
228
  className: `relative border rounded-card p-6 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:border-border-strong hover:shadow-[var(--shadow-elevation-2)] flex flex-col h-full ${e.isActive ? "border-border-subtle bg-bg-surface" : "border-dashed border-text-muted/30 bg-bg-sunken/30"}`,
@@ -334,7 +313,7 @@ var f = () => {
334
313
  })
335
314
  ]
336
315
  });
337
- }, m = ({ quota: e, onView: t, onEdit: n, onToggle: r, canManage: i, isToggling: a }) => {
316
+ }, g = ({ quota: e, onView: t, onEdit: n, onToggle: r, canManage: i, isToggling: a }) => {
338
317
  let o = e.limits, s = o?.type ?? "unknown", d = o?.value ?? 0, f = o?.unit ?? "";
339
318
  return /* @__PURE__ */ u("div", {
340
319
  className: `flex items-center gap-4 p-4 border rounded-card shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:border-border-strong hover:shadow-[var(--shadow-elevation-2)] ${e.isActive ? "border-border-subtle bg-bg-surface" : "border-dashed border-text-muted/30 bg-bg-sunken/30"}`,
@@ -410,6 +389,6 @@ var f = () => {
410
389
  });
411
390
  };
412
391
  //#endregion
413
- export { f as QuotaTemplatesListPage };
392
+ export { m as QuotaTemplatesListPage };
414
393
 
415
394
  //# sourceMappingURL=QuotaTemplatesListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"QuotaTemplatesListPage.js","names":[],"sources":["../../../../../src/billing/modules/quota/pages/QuotaTemplatesListPage.tsx"],"sourcesContent":["/**\n * Quota Module - Quota Templates List Page\n * Displays all available quota templates for admin management\n */\n\nimport { useState, type FC } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useQuotaTemplates, useQuotaTemplateMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport { formatDate } from '../../../shared/utils/format';\nimport type { Quota, QuotaType } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ntype ViewMode = 'grid' | 'list';\n\nexport const QuotaTemplatesListPage: 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, error, refetch } = useQuotaTemplates();\n const { toggleQuota, isToggling } = useQuotaTemplateMutations();\n\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showInactive, setShowInactive] = useState(false);\n const [quotaTypeFilter, setQuotaTypeFilter] = useState<QuotaType | 'all'>('all');\n\n // Permission check\n if (!permissions.canViewQuotas) {\n return <AccessDenied message=\"You don't have permission to view quotas.\" />;\n }\n\n // Filter quotas\n const filteredQuotas = quotas.filter((quota) => {\n if (!showInactive && !quota.isActive) return false;\n if (quotaTypeFilter !== 'all' && quota.quotaType !== quotaTypeFilter) return false;\n return true;\n });\n\n const handleToggleQuota = async (quota: Quota) => {\n if (!permissions.canManageQuotas) return;\n try {\n await toggleQuota(quota.id, !quota.isActive);\n await refetch();\n } catch (err) {\n console.error('Failed to toggle quota:', err);\n }\n };\n\n const handleViewQuota = (quota: Quota) => {\n navigateTo(`/quotas/${quota.id}`);\n };\n\n const handleEditQuota = (quota: Quota) => {\n navigateTo(`/quotas/${quota.id}/edit`);\n };\n\n // Loading state\n if (isLoading && quotas.length === 0) {\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 md:grid-cols-3 gap-6\">\n {[1, 2, 3].map((i) => (\n <div key={i} 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-4 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"space-y-2\">\n {[1, 2].map((j) => (\n <div key={j} className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\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.quota.failedToLoad', 'Failed to load quotas')}\n </h2>\n <p className=\"text-sm text-text-muted\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\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 Try Again\n </button>\n </div>\n </div>\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-center sm:justify-between\">\n <PageHeader\n title={tr('billing.quota.quotaTemplatesTitle', 'Quota Templates')}\n description=\"Manage quota templates that can be assigned to plans and addons\"\n />\n\n {permissions.canManageQuotas && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas/new')}\n className=\"inline-flex items-center gap-2 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 <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 4v16m8-8H4\"\n />\n </svg>\n Create Quota\n </button>\n )}\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border-subtle\">\n {/* Quota Type Toggle */}\n <div className=\"inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken/50\">\n <button\n type=\"button\"\n onClick={() => setQuotaTypeFilter('all')}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n quotaTypeFilter === 'all'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n >\n All\n </button>\n <button\n type=\"button\"\n onClick={() => setQuotaTypeFilter('POOLED' as QuotaType)}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n quotaTypeFilter === 'POOLED'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n >\n Pooled\n </button>\n <button\n type=\"button\"\n onClick={() => setQuotaTypeFilter('PRODUCT_SPECIFIC' as QuotaType)}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n quotaTypeFilter === 'PRODUCT_SPECIFIC'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n >\n Product Specific\n </button>\n </div>\n\n {/* Show Inactive Toggle */}\n {permissions.canManageQuotas && (\n <label className=\"flex items-center gap-2 text-sm cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={showInactive}\n onChange={(e) => setShowInactive(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <span className=\"text-text-muted\">\n {tr('billing.quota.showInactive', 'Show inactive quotas')}\n </span>\n </label>\n )}\n\n {/* View Mode Toggle */}\n <div className=\"ml-auto inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken/50\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 rounded-md transition-colors ${\n viewMode === 'grid'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n title=\"Grid view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 rounded-md transition-colors ${\n viewMode === 'list'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n title=\"List view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6h16M4 12h16M4 18h16\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Quotas Grid/List */}\n {filteredQuotas.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-16 border border-dashed border-border-seam rounded-card bg-bg-surface\">\n <div className=\"size-12 rounded-full bg-[var(--color-accent-soft)] flex items-center justify-center mb-4\">\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 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-text-primary\">\n {tr('billing.quota.noQuotasFound', 'No quotas found')}\n </h3>\n <p className=\"text-sm text-text-muted mt-1\">\n {showInactive\n ? 'No quotas match your current filters.'\n : 'There are no active quotas available.'}\n </p>\n {permissions.canManageQuotas && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas/new')}\n className=\"mt-4 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 Create your first quota\n </button>\n )}\n </div>\n ) : viewMode === 'grid' ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n {filteredQuotas.map((quota) => (\n <QuotaCard\n key={quota.id}\n quota={quota}\n onView={handleViewQuota}\n onEdit={handleEditQuota}\n onToggle={handleToggleQuota}\n canManage={permissions.canManageQuotas}\n isToggling={isToggling}\n />\n ))}\n </div>\n ) : (\n <div className=\"space-y-3\">\n {filteredQuotas.map((quota) => (\n <QuotaRow\n key={quota.id}\n quota={quota}\n onView={handleViewQuota}\n onEdit={handleEditQuota}\n onToggle={handleToggleQuota}\n canManage={permissions.canManageQuotas}\n isToggling={isToggling}\n />\n ))}\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Quota Card Component\n// ============================================================================\n\ninterface QuotaCardProps {\n quota: Quota;\n onView: (quota: Quota) => void;\n onEdit: (quota: Quota) => void;\n onToggle: (quota: Quota) => void;\n canManage: boolean;\n isToggling: boolean;\n}\n\nconst QuotaCard: FC<QuotaCardProps> = ({\n quota,\n onView,\n onEdit,\n onToggle,\n canManage,\n isToggling,\n}) => {\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = limits?.value ?? 0;\n const limitUnit = limits?.unit ?? '';\n\n return (\n <div\n className={`relative border rounded-card p-6 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:border-border-strong hover:shadow-[var(--shadow-elevation-2)] flex flex-col h-full ${\n quota.isActive\n ? 'border-border-subtle bg-bg-surface'\n : 'border-dashed border-text-muted/30 bg-bg-sunken/30'\n }`}\n >\n {/* Status Badge */}\n {!quota.isActive && (\n <span className=\"absolute top-4 right-4 px-2 py-1 text-xs font-medium bg-bg-sunken text-text-muted rounded\">\n Inactive\n </span>\n )}\n\n {/* Quota Icon */}\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center mb-4\">\n <svg\n className=\"size-5 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 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n </div>\n\n {/* Quota Name */}\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-bold text-text-primary\">{quota.name}</h3>\n <div className=\"flex items-center gap-2 mt-1\">\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded ${\n quota.quotaType === 'POOLED'\n ? 'bg-status-info-bg-subtle text-status-info-text'\n : 'bg-accent-purple-subtle text-text-primary'\n }`}\n >\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n </span>\n {quota.reusable && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-status-success-bg-subtle text-status-success-text rounded\">\n Reusable\n </span>\n )}\n </div>\n </div>\n\n {/* Limits Info */}\n <div className=\"space-y-2 mb-4 flex-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Limit Type:</span>\n <span className=\"font-medium text-text-primary capitalize\">{limitType}</span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Value:</span>\n <span className=\"font-semibold text-text-primary\">\n {limitValue.toLocaleString()}\n {limitUnit && ` ${limitUnit}`}\n </span>\n </div>\n </div>\n\n {/* Created Date */}\n <div className=\"py-3 border-t border-border-subtle text-sm text-text-muted\">\n Created {formatDate(quota.createdAt, 'short')}\n </div>\n\n {/* Actions */}\n <div className=\"space-y-2 pt-4 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={() => onView(quota)}\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 View Details\n </button>\n {canManage && (\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => onEdit(quota)}\n className=\"flex-1 px-3 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => onToggle(quota)}\n disabled={isToggling}\n className={`px-3 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors ${\n quota.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 {quota.isActive ? 'Deactivate' : 'Activate'}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Quota Row Component (List View)\n// ============================================================================\n\ninterface QuotaRowProps {\n quota: Quota;\n onView: (quota: Quota) => void;\n onEdit: (quota: Quota) => void;\n onToggle: (quota: Quota) => void;\n canManage: boolean;\n isToggling: boolean;\n}\n\nconst QuotaRow: FC<QuotaRowProps> = ({\n quota,\n onView,\n onEdit,\n onToggle,\n canManage,\n isToggling,\n}) => {\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = limits?.value ?? 0;\n const limitUnit = limits?.unit ?? '';\n\n return (\n <div\n className={`flex items-center gap-4 p-4 border rounded-card shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:border-border-strong hover:shadow-[var(--shadow-elevation-2)] ${\n quota.isActive\n ? 'border-border-subtle bg-bg-surface'\n : 'border-dashed border-text-muted/30 bg-bg-sunken/30'\n }`}\n >\n {/* Quota Icon */}\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center shrink-0\">\n <svg\n className=\"size-5 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 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n </div>\n\n {/* Quota Info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"font-semibold text-text-primary truncate\">{quota.name}</h3>\n {!quota.isActive && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-bg-sunken text-text-muted rounded\">\n Inactive\n </span>\n )}\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded ${\n quota.quotaType === 'POOLED'\n ? 'bg-status-info-bg-subtle text-status-info-text'\n : 'bg-accent-purple-subtle text-text-primary'\n }`}\n >\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n </span>\n </div>\n <p className=\"text-sm text-text-muted\">\n <span className=\"capitalize\">{limitType}</span> limit: {limitValue.toLocaleString()}\n {limitUnit && ` ${limitUnit}`}\n {quota.reusable && ' (Reusable)'}\n </p>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => onView(quota)}\n className=\"px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n View\n </button>\n {canManage && (\n <>\n <button\n type=\"button\"\n onClick={() => onEdit(quota)}\n className=\"px-3 py-1.5 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => onToggle(quota)}\n disabled={isToggling}\n className={`px-3 py-1.5 text-sm font-medium border border-border-subtle rounded-md transition-colors ${\n quota.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 {quota.isActive ? 'Deactivate' : 'Activate'}\n </button>\n </>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"QuotaTemplatesListPage.js","names":[],"sources":["../../../../../src/billing/modules/quota/pages/QuotaTemplatesListPage.tsx"],"sourcesContent":["/**\n * Quota Module - Quota Templates List Page\n * Displays all available quota templates for admin management\n */\n\nimport { useState, type FC } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { useQuotaTemplates, useQuotaTemplateMutations } from '../hooks';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport { formatDate } from '../../../shared/utils/format';\nimport type { Quota, QuotaType } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\n\ntype ViewMode = 'grid' | 'list';\n\nexport const QuotaTemplatesListPage: 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, error, refetch } = useQuotaTemplates();\n const { toggleQuota, isToggling } = useQuotaTemplateMutations();\n\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showInactive, setShowInactive] = useState(false);\n const [quotaTypeFilter, setQuotaTypeFilter] = useState<QuotaType | 'all'>('all');\n\n // Permission check\n if (!permissions.canViewQuotas) {\n return <AccessDenied message=\"You don't have permission to view quotas.\" />;\n }\n\n // Filter quotas\n const filteredQuotas = quotas.filter((quota) => {\n if (!showInactive && !quota.isActive) return false;\n if (quotaTypeFilter !== 'all' && quota.quotaType !== quotaTypeFilter) return false;\n return true;\n });\n\n const handleToggleQuota = async (quota: Quota) => {\n if (!permissions.canManageQuotas) return;\n try {\n await toggleQuota(quota.id, !quota.isActive);\n await refetch();\n } catch (err) {\n console.error('Failed to toggle quota:', err);\n }\n };\n\n const handleViewQuota = (quota: Quota) => {\n navigateTo(`/quotas/${quota.id}`);\n };\n\n const handleEditQuota = (quota: Quota) => {\n navigateTo(`/quotas/${quota.id}/edit`);\n };\n\n // Loading state\n if (isLoading && quotas.length === 0) {\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 md:grid-cols-3 gap-6\">\n {[1, 2, 3].map((i) => (\n <div key={i} 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-4 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"space-y-2\">\n {[1, 2].map((j) => (\n <div key={j} className=\"h-4 w-full bg-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\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.quota.failedToLoad', 'Failed to load quotas')}\n </h2>\n <p className=\"text-sm text-text-muted\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\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 Try Again\n </button>\n </div>\n </div>\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-center sm:justify-between\">\n <PageHeader\n title={tr('billing.quota.quotaTemplatesTitle', 'Quota Templates')}\n description=\"Manage quota templates that can be assigned to plans and addons\"\n />\n\n {permissions.canManageQuotas && (\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas/new')}\n className=\"inline-flex items-center gap-2 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 <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 4v16m8-8H4\"\n />\n </svg>\n Create Quota\n </button>\n )}\n </div>\n\n <PagePurpose>\n {tr(\n 'billing.quota.templatesPurpose',\n 'Quota templates are the reusable limit definitions you attach to plans and add-ons — like \"API calls per month\" or \"storage\". Create and manage them here once, then reference them across multiple plans instead of redefining the same limit each time.'\n )}\n </PagePurpose>\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border-subtle\">\n {/* Quota Type Toggle */}\n <div className=\"inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken/50\">\n <button\n type=\"button\"\n onClick={() => setQuotaTypeFilter('all')}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n quotaTypeFilter === 'all'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n >\n All\n </button>\n <button\n type=\"button\"\n onClick={() => setQuotaTypeFilter('POOLED' as QuotaType)}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n quotaTypeFilter === 'POOLED'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n >\n Pooled\n </button>\n <button\n type=\"button\"\n onClick={() => setQuotaTypeFilter('PRODUCT_SPECIFIC' as QuotaType)}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n quotaTypeFilter === 'PRODUCT_SPECIFIC'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n >\n Product Specific\n </button>\n </div>\n\n {/* Show Inactive Toggle */}\n {permissions.canManageQuotas && (\n <label className=\"flex items-center gap-2 text-sm cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={showInactive}\n onChange={(e) => setShowInactive(e.target.checked)}\n className=\"size-4 rounded border-border-subtle text-text-link focus:ring-[var(--color-focus-ring)]\"\n />\n <span className=\"text-text-muted\">\n {tr('billing.quota.showInactive', 'Show inactive quotas')}\n </span>\n </label>\n )}\n\n {/* View Mode Toggle */}\n <div className=\"ml-auto inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken/50\">\n <button\n type=\"button\"\n onClick={() => setViewMode('grid')}\n className={`p-1.5 rounded-md transition-colors ${\n viewMode === 'grid'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n title=\"Grid view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => setViewMode('list')}\n className={`p-1.5 rounded-md transition-colors ${\n viewMode === 'list'\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-muted hover:text-text-primary'\n }`}\n title=\"List view\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M4 6h16M4 12h16M4 18h16\"\n />\n </svg>\n </button>\n </div>\n </div>\n\n {/* Quotas Grid/List */}\n {filteredQuotas.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tr('billing.quota.noQuotasFound', 'No quotas found')}\n description={\n showInactive\n ? 'No quotas match your current filters.'\n : 'There are no active quotas available.'\n }\n action={\n permissions.canManageQuotas ? (\n <button\n type=\"button\"\n onClick={() => navigateTo('/quotas/new')}\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 Create your first quota\n </button>\n ) : undefined\n }\n />\n ) : viewMode === 'grid' ? (\n <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n {filteredQuotas.map((quota) => (\n <QuotaCard\n key={quota.id}\n quota={quota}\n onView={handleViewQuota}\n onEdit={handleEditQuota}\n onToggle={handleToggleQuota}\n canManage={permissions.canManageQuotas}\n isToggling={isToggling}\n />\n ))}\n </div>\n ) : (\n <div className=\"space-y-3\">\n {filteredQuotas.map((quota) => (\n <QuotaRow\n key={quota.id}\n quota={quota}\n onView={handleViewQuota}\n onEdit={handleEditQuota}\n onToggle={handleToggleQuota}\n canManage={permissions.canManageQuotas}\n isToggling={isToggling}\n />\n ))}\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Quota Card Component\n// ============================================================================\n\ninterface QuotaCardProps {\n quota: Quota;\n onView: (quota: Quota) => void;\n onEdit: (quota: Quota) => void;\n onToggle: (quota: Quota) => void;\n canManage: boolean;\n isToggling: boolean;\n}\n\nconst QuotaCard: FC<QuotaCardProps> = ({\n quota,\n onView,\n onEdit,\n onToggle,\n canManage,\n isToggling,\n}) => {\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = limits?.value ?? 0;\n const limitUnit = limits?.unit ?? '';\n\n return (\n <div\n className={`relative border rounded-card p-6 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:border-border-strong hover:shadow-[var(--shadow-elevation-2)] flex flex-col h-full ${\n quota.isActive\n ? 'border-border-subtle bg-bg-surface'\n : 'border-dashed border-text-muted/30 bg-bg-sunken/30'\n }`}\n >\n {/* Status Badge */}\n {!quota.isActive && (\n <span className=\"absolute top-4 right-4 px-2 py-1 text-xs font-medium bg-bg-sunken text-text-muted rounded\">\n Inactive\n </span>\n )}\n\n {/* Quota Icon */}\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center mb-4\">\n <svg\n className=\"size-5 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 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n </div>\n\n {/* Quota Name */}\n <div className=\"mb-4\">\n <h3 className=\"text-lg font-bold text-text-primary\">{quota.name}</h3>\n <div className=\"flex items-center gap-2 mt-1\">\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded ${\n quota.quotaType === 'POOLED'\n ? 'bg-status-info-bg-subtle text-status-info-text'\n : 'bg-accent-purple-subtle text-text-primary'\n }`}\n >\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n </span>\n {quota.reusable && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-status-success-bg-subtle text-status-success-text rounded\">\n Reusable\n </span>\n )}\n </div>\n </div>\n\n {/* Limits Info */}\n <div className=\"space-y-2 mb-4 flex-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Limit Type:</span>\n <span className=\"font-medium text-text-primary capitalize\">{limitType}</span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Value:</span>\n <span className=\"font-semibold text-text-primary\">\n {limitValue.toLocaleString()}\n {limitUnit && ` ${limitUnit}`}\n </span>\n </div>\n </div>\n\n {/* Created Date */}\n <div className=\"py-3 border-t border-border-subtle text-sm text-text-muted\">\n Created {formatDate(quota.createdAt, 'short')}\n </div>\n\n {/* Actions */}\n <div className=\"space-y-2 pt-4 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={() => onView(quota)}\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 View Details\n </button>\n {canManage && (\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => onEdit(quota)}\n className=\"flex-1 px-3 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => onToggle(quota)}\n disabled={isToggling}\n className={`px-3 py-2 text-sm font-medium border border-border-subtle rounded-md transition-colors ${\n quota.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 {quota.isActive ? 'Deactivate' : 'Activate'}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Quota Row Component (List View)\n// ============================================================================\n\ninterface QuotaRowProps {\n quota: Quota;\n onView: (quota: Quota) => void;\n onEdit: (quota: Quota) => void;\n onToggle: (quota: Quota) => void;\n canManage: boolean;\n isToggling: boolean;\n}\n\nconst QuotaRow: FC<QuotaRowProps> = ({\n quota,\n onView,\n onEdit,\n onToggle,\n canManage,\n isToggling,\n}) => {\n const limits = quota.limits as { type?: string; value?: number; unit?: string } | null;\n const limitType = limits?.type ?? 'unknown';\n const limitValue = limits?.value ?? 0;\n const limitUnit = limits?.unit ?? '';\n\n return (\n <div\n className={`flex items-center gap-4 p-4 border rounded-card shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:border-border-strong hover:shadow-[var(--shadow-elevation-2)] ${\n quota.isActive\n ? 'border-border-subtle bg-bg-surface'\n : 'border-dashed border-text-muted/30 bg-bg-sunken/30'\n }`}\n >\n {/* Quota Icon */}\n <div className=\"size-10 rounded-lg bg-[var(--color-accent-soft)] flex items-center justify-center shrink-0\">\n <svg\n className=\"size-5 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 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z\"\n />\n </svg>\n </div>\n\n {/* Quota Info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"font-semibold text-text-primary truncate\">{quota.name}</h3>\n {!quota.isActive && (\n <span className=\"px-2 py-0.5 text-xs font-medium bg-bg-sunken text-text-muted rounded\">\n Inactive\n </span>\n )}\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded ${\n quota.quotaType === 'POOLED'\n ? 'bg-status-info-bg-subtle text-status-info-text'\n : 'bg-accent-purple-subtle text-text-primary'\n }`}\n >\n {quota.quotaType === 'POOLED' ? 'Pooled' : 'Product Specific'}\n </span>\n </div>\n <p className=\"text-sm text-text-muted\">\n <span className=\"capitalize\">{limitType}</span> limit: {limitValue.toLocaleString()}\n {limitUnit && ` ${limitUnit}`}\n {quota.reusable && ' (Reusable)'}\n </p>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => onView(quota)}\n className=\"px-3 py-1.5 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n View\n </button>\n {canManage && (\n <>\n <button\n type=\"button\"\n onClick={() => onEdit(quota)}\n className=\"px-3 py-1.5 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Edit\n </button>\n <button\n type=\"button\"\n onClick={() => onToggle(quota)}\n disabled={isToggling}\n className={`px-3 py-1.5 text-sm font-medium border border-border-subtle rounded-md transition-colors ${\n quota.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 {quota.isActive ? 'Deactivate' : 'Activate'}\n </button>\n </>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}