@burdenoff/microfe-billing 2026.526.1 → 2026.528.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 (145) hide show
  1. package/dist/billing/BillingAdminRoutes.js +1 -1
  2. package/dist/billing/BillingAdminRoutes.js.map +1 -1
  3. package/dist/billing/BillingUserRoutes.js +1 -1
  4. package/dist/billing/BillingUserRoutes.js.map +1 -1
  5. package/dist/billing/modules/addons/components/FloatingCartDrawer.js +15 -8
  6. package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
  7. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js +13 -6
  8. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
  9. package/dist/billing/modules/addons/pages/AddonCreatePage.js +1 -1
  10. package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
  11. package/dist/billing/modules/addons/pages/AddonDetailPage.js +8 -4
  12. package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
  13. package/dist/billing/modules/addons/pages/AddonEditPage.js +3 -2
  14. package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
  15. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js +44 -23
  16. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
  17. package/dist/billing/modules/addons/pages/AddonsListPage.js +60 -31
  18. package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
  19. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +9 -9
  20. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
  21. package/dist/billing/modules/analytics/pages/AnalyticsPage.js +8 -8
  22. package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
  23. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +16 -10
  24. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  25. package/dist/billing/modules/billing/pages/InvoicesListPage.js +24 -16
  26. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  27. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js +5 -1
  28. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
  29. package/dist/billing/modules/billing/pages/RefundsPage.js +46 -29
  30. package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
  31. package/dist/billing/modules/billing/pages/TransactionsPage.js +34 -22
  32. package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
  33. package/dist/billing/modules/checkout/components/BillingAccountSelector.js +10 -10
  34. package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
  35. package/dist/billing/modules/checkout/components/CreditsAmountInput.js +3 -3
  36. package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
  37. package/dist/billing/modules/checkout/components/NativeCheckoutView.js +7 -0
  38. package/dist/billing/modules/checkout/components/NativeCheckoutView.js.map +1 -1
  39. package/dist/billing/modules/checkout/components/OrderSummary.js +13 -13
  40. package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
  41. package/dist/billing/modules/checkout/components/PromoCodeInput.js +5 -5
  42. package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
  43. package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js +5 -5
  44. package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
  45. package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js +4 -4
  46. package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
  47. package/dist/billing/modules/checkout/components/StripePaymentForm.js +11 -11
  48. package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
  49. package/dist/billing/modules/checkout/components/SubscriptionSelector.js +3 -3
  50. package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
  51. package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js +8 -8
  52. package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
  53. package/dist/billing/modules/checkout/pages/CheckoutPage.js +61 -52
  54. package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
  55. package/dist/billing/modules/coupons/pages/CouponDetailPage.js +22 -15
  56. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  57. package/dist/billing/modules/coupons/pages/CouponsListPage.js +30 -21
  58. package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
  59. package/dist/billing/modules/coupons/pages/CreateCouponPage.js +9 -8
  60. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  61. package/dist/billing/modules/coupons/pages/EditCouponPage.js +11 -8
  62. package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
  63. package/dist/billing/modules/credits/pages/AdminCreditsPage.js +1 -1
  64. package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
  65. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +22 -17
  66. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
  67. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js +15 -8
  68. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
  69. package/dist/billing/modules/credits/pages/CreditsPage.js +17 -11
  70. package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
  71. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +15 -11
  72. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
  73. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +21 -17
  74. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
  75. package/dist/billing/modules/dashboard/components/BillingAccountCard.js +14 -11
  76. package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
  77. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +32 -26
  78. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
  79. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +14 -10
  80. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
  81. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +18 -11
  82. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
  83. package/dist/billing/modules/dashboard/pages/OverviewPage.js +44 -32
  84. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  85. package/dist/billing/modules/discounts/pages/DiscountsListPage.js +35 -26
  86. package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
  87. package/dist/billing/modules/earnings/pages/EarningsPage.js +15 -15
  88. package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
  89. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +11 -11
  90. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  91. package/dist/billing/modules/earnings/pages/PayoutsPage.js +5 -5
  92. package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
  93. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +9 -4
  94. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  95. package/dist/billing/modules/plans/pages/PlanCreatePage.js +1 -1
  96. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  97. package/dist/billing/modules/plans/pages/PlanDetailPage.js +13 -8
  98. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  99. package/dist/billing/modules/plans/pages/PlanEditPage.js +3 -2
  100. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  101. package/dist/billing/modules/plans/pages/PlansBrowsePage.js +14 -7
  102. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  103. package/dist/billing/modules/plans/pages/PlansListPage.js +23 -9
  104. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  105. package/dist/billing/modules/quota/pages/QuotaPage.js +11 -7
  106. package/dist/billing/modules/quota/pages/QuotaPage.js.map +1 -1
  107. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +3 -3
  108. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
  109. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +6 -2
  110. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
  111. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +5 -4
  112. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
  113. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +26 -12
  114. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
  115. package/dist/billing/modules/settings/pages/SettingsPage.js +356 -349
  116. package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
  117. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js +157 -123
  118. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
  119. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js +13 -8
  120. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
  121. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +15 -7
  122. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
  123. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +30 -20
  124. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
  125. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js +25 -16
  126. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
  127. package/dist/billing/modules/usage/pages/UsagePage.js +50 -34
  128. package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
  129. package/dist/billing/shared/components/AccessDenied.js +2 -2
  130. package/dist/billing/shared/components/AccessDenied.js.map +1 -1
  131. package/dist/billing/shared/components/ActorIdentity.js +67 -0
  132. package/dist/billing/shared/components/ActorIdentity.js.map +1 -0
  133. package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js +6 -2
  134. package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
  135. package/dist/billing/shared/components/EmptyState.js +7 -21
  136. package/dist/billing/shared/components/EmptyState.js.map +1 -1
  137. package/dist/billing/shared/components/PageHeader.js +7 -19
  138. package/dist/billing/shared/components/PageHeader.js.map +1 -1
  139. package/dist/billing/shared/components/ServerError.js +5 -4
  140. package/dist/billing/shared/components/ServerError.js.map +1 -1
  141. package/dist/billing/shared/components/index.js +1 -0
  142. package/dist/billing/shared/hooks/index.js +1 -0
  143. package/dist/billing/shared/hooks/useActorProfiles.js +113 -0
  144. package/dist/billing/shared/hooks/useActorProfiles.js.map +1 -0
  145. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"EditCouponPage.js","names":[],"sources":["../../../../../src/billing/modules/coupons/pages/EditCouponPage.tsx"],"sourcesContent":["/**\n * Edit Coupon Page\n *\n * Form for editing existing coupon codes\n * Note: Only certain fields can be edited (name, description, status, usage limits, expiration)\n * Code, type, value, and scope cannot be changed after creation\n */\n\nimport { type FC, useState, useEffect } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { ArrowLeft, Save, AlertCircle, CheckCircle } from 'lucide-react';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport {\n useGetCouponLazyQuery,\n useUpdateCouponMutation,\n} from '../../../../generated/global-operations';\nimport { Coupon, CouponStatus } from '../../../../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface EditFormData {\n name: string;\n description: string;\n status: CouponStatus;\n maxTotalUses: string;\n maxUsesPerUser: string;\n validUntil: string;\n}\n\nexport const EditCouponPage: 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\n // Get coupon ID from URL\n const couponId = window.location.pathname.split('/').slice(-2)[0];\n\n const [getCoupon, { data, loading: loadingCoupon, error: loadError }] = useGetCouponLazyQuery();\n const [updateCoupon, { loading: updating }] = useUpdateCouponMutation();\n\n const [formData, setFormData] = useState<EditFormData>({\n name: '',\n description: '',\n status: CouponStatus.Active,\n maxTotalUses: '',\n maxUsesPerUser: '',\n validUntil: '',\n });\n\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [submitError, setSubmitError] = useState('');\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n // Load coupon data\n useEffect(() => {\n if (couponId) {\n getCoupon({ variables: { id: couponId } });\n }\n }, [couponId, getCoupon]);\n\n // Populate form when data loads\n useEffect(() => {\n if (data?.getCoupon) {\n const coupon = data.getCoupon as Coupon;\n setFormData({\n name: coupon.name,\n description: coupon.description || '',\n status: coupon.status,\n maxTotalUses: coupon.maxTotalUses?.toString() || '',\n maxUsesPerUser: coupon.maxUsesPerUser.toString(),\n validUntil: coupon.validUntil\n ? new Date(coupon.validUntil).toISOString().split('T')[0]\n : '',\n });\n }\n }, [data]);\n\n // Check permissions\n if (!permissions.canManageDiscounts) {\n return <AccessDenied message=\"You don't have permission to manage coupons.\" />;\n }\n\n const handleInputChange = (field: keyof EditFormData, value: string) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field]) {\n setErrors((prev) => ({ ...prev, [field]: '' }));\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: Record<string, string> = {};\n\n if (!formData.name.trim()) {\n newErrors.name = 'Name is required';\n }\n\n if (formData.maxTotalUses && parseInt(formData.maxTotalUses) < 1) {\n newErrors.maxTotalUses = 'Must be at least 1';\n }\n\n if (formData.maxUsesPerUser && parseInt(formData.maxUsesPerUser) < 1) {\n newErrors.maxUsesPerUser = 'Must be at least 1';\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError('');\n setSubmitSuccess(false);\n\n if (!validateForm()) {\n return;\n }\n\n if (!couponId) {\n setSubmitError('Coupon ID is missing');\n return;\n }\n\n try {\n const input: {\n name?: string;\n description?: string;\n status?: CouponStatus;\n maxTotalUses?: number;\n maxUsesPerUser?: number;\n validUntil?: string;\n } = {};\n\n if (formData.name.trim()) input.name = formData.name.trim();\n if (formData.description.trim()) input.description = formData.description.trim();\n if (formData.status) input.status = formData.status;\n if (formData.maxTotalUses) input.maxTotalUses = parseInt(formData.maxTotalUses);\n if (formData.maxUsesPerUser) input.maxUsesPerUser = parseInt(formData.maxUsesPerUser);\n if (formData.validUntil) input.validUntil = new Date(formData.validUntil).toISOString();\n\n await updateCoupon({\n variables: {\n id: couponId,\n input,\n },\n });\n\n setSubmitSuccess(true);\n setTimeout(() => {\n navigateTo('/coupons');\n }, 1500);\n } catch (error) {\n console.error('Failed to update coupon:', error);\n setSubmitError(error instanceof Error ? error.message : 'Failed to update coupon');\n }\n };\n\n if (loadingCoupon) {\n return (\n <div className=\"p-8\">\n <PageHeader\n title={tr('billing.coupons.editTitle', 'Edit Coupon')}\n description=\"Loading coupon data...\"\n actions={\n <button\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted flex items-center gap-2\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back\n </button>\n }\n />\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"text-muted-foreground\">Loading coupon...</div>\n </div>\n </div>\n );\n }\n\n if (loadError || !data?.getCoupon) {\n return (\n <div className=\"p-8\">\n <PageHeader\n title=\"Edit Coupon\"\n description=\"Coupon not found\"\n actions={\n <button\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted flex items-center gap-2\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back\n </button>\n }\n />\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"text-destructive\">\n {loadError ? `Error: ${loadError.message}` : 'Coupon not found'}\n </div>\n </div>\n </div>\n );\n }\n\n const coupon = data.getCoupon as Coupon;\n\n return (\n <div className=\"p-8\">\n <PageHeader\n title=\"Edit Coupon\"\n description={`Editing: ${coupon.code}`}\n actions={\n <button\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted flex items-center gap-2\"\n >\n <ArrowLeft className=\"h-4 w-4\" />\n Back to Coupons\n </button>\n }\n />\n\n <div className=\"max-w-3xl mx-auto mt-8\">\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Read-only Information Card */}\n <div className=\"bg-muted/30 border border-border rounded-lg p-6\">\n <h3 className=\"text-lg font-semibold text-foreground mb-4\">\n Coupon Details (Read-Only)\n </h3>\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-muted-foreground\">Code:</span>\n <span className=\"ml-2 font-mono font-medium text-foreground\">{coupon.code}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Type:</span>\n <span className=\"ml-2 text-foreground capitalize\">\n {coupon.type.replace('_', ' ').toLowerCase()}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Value:</span>\n <span className=\"ml-2 text-foreground\">\n {coupon.type === 'PERCENTAGE' ? `${coupon.value}%` : `$${coupon.value}`}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Scope:</span>\n <span className=\"ml-2 text-foreground\">{coupon.scope}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Current Uses:</span>\n <span className=\"ml-2 text-foreground\">\n {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Created:</span>\n <span className=\"ml-2 text-foreground\">\n {new Date(coupon.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n </div>\n\n {/* Editable Fields Card */}\n <div className=\"bg-card border border-border rounded-lg p-6 space-y-6\">\n <h3 className=\"text-lg font-semibold text-foreground\">Editable Fields</h3>\n\n {/* Name */}\n <div className=\"space-y-2\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-foreground\">\n Name <span className=\"text-destructive\">*</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={(e) => handleInputChange('name', e.target.value)}\n placeholder=\"e.g., Welcome Discount\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n {errors.name && <p className=\"text-sm text-destructive\">{errors.name}</p>}\n </div>\n\n {/* Description */}\n <div className=\"space-y-2\">\n <label htmlFor=\"description\" className=\"block text-sm font-medium text-foreground\">\n Description\n </label>\n <textarea\n id=\"description\"\n value={formData.description}\n onChange={(e) => handleInputChange('description', e.target.value)}\n placeholder=\"Optional description\"\n rows={3}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 resize-none\"\n />\n </div>\n\n {/* Status */}\n <div className=\"space-y-2\">\n <label htmlFor=\"status\" className=\"block text-sm font-medium text-foreground\">\n Status\n </label>\n <select\n id=\"status\"\n value={formData.status}\n onChange={(e) => handleInputChange('status', e.target.value)}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n >\n <option value=\"ACTIVE\">Active</option>\n <option value=\"INACTIVE\">Inactive</option>\n <option value=\"EXPIRED\">Expired</option>\n <option value=\"DEPLETED\">Depleted</option>\n </select>\n </div>\n\n {/* Max Total Uses */}\n <div className=\"space-y-2\">\n <label htmlFor=\"maxTotalUses\" className=\"block text-sm font-medium text-foreground\">\n Maximum Total Uses\n </label>\n <input\n id=\"maxTotalUses\"\n type=\"number\"\n min=\"1\"\n value={formData.maxTotalUses}\n onChange={(e) => handleInputChange('maxTotalUses', e.target.value)}\n placeholder=\"Leave empty for unlimited\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n {errors.maxTotalUses && (\n <p className=\"text-sm text-destructive\">{errors.maxTotalUses}</p>\n )}\n <p className=\"text-sm text-muted-foreground\">\n Current uses: {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </p>\n </div>\n\n {/* Max Uses Per User */}\n <div className=\"space-y-2\">\n <label htmlFor=\"maxUsesPerUser\" className=\"block text-sm font-medium text-foreground\">\n Maximum Uses Per User\n </label>\n <input\n id=\"maxUsesPerUser\"\n type=\"number\"\n min=\"1\"\n value={formData.maxUsesPerUser}\n onChange={(e) => handleInputChange('maxUsesPerUser', e.target.value)}\n placeholder=\"e.g., 1\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n {errors.maxUsesPerUser && (\n <p className=\"text-sm text-destructive\">{errors.maxUsesPerUser}</p>\n )}\n </div>\n\n {/* Valid Until */}\n <div className=\"space-y-2\">\n <label htmlFor=\"validUntil\" className=\"block text-sm font-medium text-foreground\">\n Valid Until\n </label>\n <input\n id=\"validUntil\"\n type=\"date\"\n value={formData.validUntil}\n onChange={(e) => handleInputChange('validUntil', e.target.value)}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n <p className=\"text-sm text-muted-foreground\">Leave empty for no expiration</p>\n </div>\n </div>\n\n {/* Error/Success Messages */}\n {submitError && (\n <div className=\"flex items-start gap-3 p-4 rounded-lg bg-destructive/10 border border-destructive/20\">\n <AlertCircle className=\"h-5 w-5 text-destructive mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-destructive\">Error</p>\n <p className=\"text-sm text-destructive/90 mt-1\">{submitError}</p>\n </div>\n </div>\n )}\n\n {submitSuccess && (\n <div className=\"flex items-start gap-3 p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-bg-subtle\">\n <CheckCircle className=\"h-5 w-5 text-status-success-text mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-success-text\">Success</p>\n <p className=\"text-sm text-status-success-text/90 mt-1\">\n Coupon updated successfully! Redirecting...\n </p>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex gap-4 pt-4 border-t border-border\">\n <button\n type=\"submit\"\n disabled={updating || submitSuccess}\n className=\"px-6 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2\"\n >\n <Save className=\"h-4 w-4\" />\n {updating ? 'Updating...' : 'Update Coupon'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-6 py-2 rounded-lg border border-border text-foreground hover:bg-muted\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;AA6BA,IAAa,UAA2B;CACtC,IAAM,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAa,EAAE,EAAI;AACzB,SAAO,MAAe,IAAM,IAAW;IAEnC,IAAa,GAAoB,EACjC,IAAc,GAAuB,EAGrC,IAAW,OAAO,SAAS,SAAS,MAAM,IAAI,CAAC,MAAM,GAAG,CAAC,IAEzD,CAAC,GAAW,EAAE,SAAM,SAAS,GAAe,OAAO,OAAe,GAAuB,EACzF,CAAC,GAAc,EAAE,SAAS,OAAc,GAAyB,EAEjE,CAAC,GAAU,KAAe,EAAuB;EACrD,MAAM;EACN,aAAa;EACb,QAAQ,EAAa;EACrB,cAAc;EACd,gBAAgB;EAChB,YAAY;EACb,CAAC,EAEI,CAAC,GAAQ,KAAa,EAAiC,EAAE,CAAC,EAC1D,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAe,KAAoB,EAAS,GAAM;AA2BzD,KAxBA,QAAgB;AACd,EAAI,KACF,EAAU,EAAE,WAAW,EAAE,IAAI,GAAU,EAAE,CAAC;IAE3C,CAAC,GAAU,EAAU,CAAC,EAGzB,QAAgB;AACd,MAAI,GAAM,WAAW;GACnB,IAAM,IAAS,EAAK;AACpB,KAAY;IACV,MAAM,EAAO;IACb,aAAa,EAAO,eAAe;IACnC,QAAQ,EAAO;IACf,cAAc,EAAO,cAAc,UAAU,IAAI;IACjD,gBAAgB,EAAO,eAAe,UAAU;IAChD,YAAY,EAAO,aACf,IAAI,KAAK,EAAO,WAAW,CAAC,aAAa,CAAC,MAAM,IAAI,CAAC,KACrD;IACL,CAAC;;IAEH,CAAC,EAAK,CAAC,EAGN,CAAC,EAAY,mBACf,QAAO,kBAAC,GAAD,EAAc,SAAQ,gDAAiD,CAAA;CAGhF,IAAM,KAAqB,GAA2B,MAAkB;AAEtE,EADA,GAAa,OAAU;GAAE,GAAG;IAAO,IAAQ;GAAO,EAAE,EAChD,EAAO,MACT,GAAW,OAAU;GAAE,GAAG;IAAO,IAAQ;GAAI,EAAE;IAI7C,UAA8B;EAClC,IAAM,IAAoC,EAAE;AAe5C,SAbK,EAAS,KAAK,MAAM,KACvB,EAAU,OAAO,qBAGf,EAAS,gBAAgB,SAAS,EAAS,aAAa,GAAG,MAC7D,EAAU,eAAe,uBAGvB,EAAS,kBAAkB,SAAS,EAAS,eAAe,GAAG,MACjE,EAAU,iBAAiB,uBAG7B,EAAU,EAAU,EACb,OAAO,KAAK,EAAU,CAAC,WAAW;IAGrC,IAAe,OAAO,MAAuB;AACjD,QAAE,gBAAgB,EAClB,EAAe,GAAG,EAClB,EAAiB,GAAM,EAElB,GAAc,EAInB;OAAI,CAAC,GAAU;AACb,MAAe,uBAAuB;AACtC;;AAGF,OAAI;IACF,IAAM,IAOF,EAAE;AAiBN,IAfI,EAAS,KAAK,MAAM,KAAE,EAAM,OAAO,EAAS,KAAK,MAAM,GACvD,EAAS,YAAY,MAAM,KAAE,EAAM,cAAc,EAAS,YAAY,MAAM,GAC5E,EAAS,WAAQ,EAAM,SAAS,EAAS,SACzC,EAAS,iBAAc,EAAM,eAAe,SAAS,EAAS,aAAa,GAC3E,EAAS,mBAAgB,EAAM,iBAAiB,SAAS,EAAS,eAAe,GACjF,EAAS,eAAY,EAAM,aAAa,IAAI,KAAK,EAAS,WAAW,CAAC,aAAa,GAEvF,MAAM,EAAa,EACjB,WAAW;KACT,IAAI;KACJ;KACD,EACF,CAAC,EAEF,EAAiB,GAAK,EACtB,iBAAiB;AACf,OAAW,WAAW;OACrB,KAAK;YACD,GAAO;AAEd,IADA,QAAQ,MAAM,4BAA4B,EAAM,EAChD,EAAe,aAAiB,QAAQ,EAAM,UAAU,0BAA0B;;;;AAItF,KAAI,EACF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,OAAO,EAAG,6BAA6B,cAAc;GACrD,aAAY;GACZ,SACE,kBAAC,UAAD;IACE,eAAe,EAAW,WAAW;IACrC,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,OAE1B;;GAEX,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cAAwB;IAAuB,CAAA;GAC1D,CAAA,CACF;;AAIV,KAAI,KAAa,CAAC,GAAM,UACtB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,OAAM;GACN,aAAY;GACZ,SACE,kBAAC,UAAD;IACE,eAAe,EAAW,WAAW;IACrC,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,OAE1B;;GAEX,CAAA,EACF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,OAAD;IAAK,WAAU;cACZ,IAAY,UAAU,EAAU,YAAY;IACzC,CAAA;GACF,CAAA,CACF;;CAIV,IAAM,IAAS,EAAK;AAEpB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,GAAD;GACE,OAAM;GACN,aAAa,YAAY,EAAO;GAChC,SACE,kBAAC,UAAD;IACE,eAAe,EAAW,WAAW;IACrC,WAAU;cAFZ,CAIE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,kBAE1B;;GAEX,CAAA,EAEF,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,QAAD;IAAM,UAAU;IAAc,WAAU;cAAxC;KAEE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA6C;OAEtD,CAAA,EACL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAY,CAAA,EACpD,kBAAC,QAAD;SAAM,WAAU;mBAA8C,EAAO;SAAY,CAAA,CAC7E,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAY,CAAA,EACpD,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAO,KAAK,QAAQ,KAAK,IAAI,CAAC,aAAa;SACvC,CAAA,CACH,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAa,CAAA,EACrD,kBAAC,QAAD;SAAM,WAAU;mBACb,EAAO,SAAS,eAAe,GAAG,EAAO,MAAM,KAAK,IAAI,EAAO;SAC3D,CAAA,CACH,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAa,CAAA,EACrD,kBAAC,QAAD;SAAM,WAAU;mBAAwB,EAAO;SAAa,CAAA,CACxD,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAoB,CAAA,EAC5D,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACG,EAAO,aACP,EAAO,eAAe,MAAM,EAAO,iBAAiB,GAChD;WACH,EAAA,CAAA;QACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAe,CAAA,EACvD,kBAAC,QAAD;SAAM,WAAU;mBACb,IAAI,KAAK,EAAO,UAAU,CAAC,oBAAoB;SAC3C,CAAA,CACH,EAAA,CAAA;QACF;SACF;;KAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,MAAD;QAAI,WAAU;kBAAwC;QAAoB,CAAA;OAG1E,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,SAAD;UAAO,SAAQ;UAAO,WAAU;oBAAhC,CAA4E,SACrE,kBAAC,QAAD;WAAM,WAAU;qBAAmB;WAAQ,CAAA,CAC1C;;SACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,QAAQ,EAAE,OAAO,MAAM;UAC1D,aAAY;UACZ,WAAU;UACV,CAAA;SACD,EAAO,QAAQ,kBAAC,KAAD;UAAG,WAAU;oBAA4B,EAAO;UAAS,CAAA;SACrE;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SAAO,SAAQ;SAAc,WAAU;mBAA4C;SAE3E,CAAA,EACR,kBAAC,YAAD;SACE,IAAG;SACH,OAAO,EAAS;SAChB,WAAW,MAAM,EAAkB,eAAe,EAAE,OAAO,MAAM;SACjE,aAAY;SACZ,MAAM;SACN,WAAU;SACV,CAAA,CACE;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,SAAD;SAAO,SAAQ;SAAS,WAAU;mBAA4C;SAEtE,CAAA,EACR,kBAAC,UAAD;SACE,IAAG;SACH,OAAO,EAAS;SAChB,WAAW,MAAM,EAAkB,UAAU,EAAE,OAAO,MAAM;SAC5D,WAAU;mBAJZ;UAME,kBAAC,UAAD;WAAQ,OAAM;qBAAS;WAAe,CAAA;UACtC,kBAAC,UAAD;WAAQ,OAAM;qBAAW;WAAiB,CAAA;UAC1C,kBAAC,UAAD;WAAQ,OAAM;qBAAU;WAAgB,CAAA;UACxC,kBAAC,UAAD;WAAQ,OAAM;qBAAW;WAAiB,CAAA;UACnC;WACL;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,SAAD;UAAO,SAAQ;UAAe,WAAU;oBAA4C;UAE5E,CAAA;SACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,KAAI;UACJ,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,gBAAgB,EAAE,OAAO,MAAM;UAClE,aAAY;UACZ,WAAU;UACV,CAAA;SACD,EAAO,gBACN,kBAAC,KAAD;UAAG,WAAU;oBAA4B,EAAO;UAAiB,CAAA;SAEnE,kBAAC,KAAD;UAAG,WAAU;oBAAb;WAA6C;WAC5B,EAAO;WACrB,EAAO,eAAe,MAAM,EAAO,iBAAiB;WACnD;;SACA;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,SAAD;UAAO,SAAQ;UAAiB,WAAU;oBAA4C;UAE9E,CAAA;SACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,KAAI;UACJ,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,kBAAkB,EAAE,OAAO,MAAM;UACpE,aAAY;UACZ,WAAU;UACV,CAAA;SACD,EAAO,kBACN,kBAAC,KAAD;UAAG,WAAU;oBAA4B,EAAO;UAAmB,CAAA;SAEjE;;OAGN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,SAAD;UAAO,SAAQ;UAAa,WAAU;oBAA4C;UAE1E,CAAA;SACR,kBAAC,SAAD;UACE,IAAG;UACH,MAAK;UACL,OAAO,EAAS;UAChB,WAAW,MAAM,EAAkB,cAAc,EAAE,OAAO,MAAM;UAChE,WAAU;UACV,CAAA;SACF,kBAAC,KAAD;UAAG,WAAU;oBAAgC;UAAiC,CAAA;SAC1E;;OACF;;KAGL,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,mCAAoC,CAAA,EAC3D,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAuC;QAAS,CAAA,EAC7D,kBAAC,KAAD;QAAG,WAAU;kBAAoC;QAAgB,CAAA,CAC7D;SACF;;KAGP,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAa,WAAU,2CAA4C,CAAA,EACnE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBAA+C;QAAW,CAAA,EACvE,kBAAC,KAAD;QAAG,WAAU;kBAA2C;QAEpD,CAAA,CACA;SACF;;KAIR,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,MAAK;OACL,UAAU,KAAY;OACtB,WAAU;iBAHZ,CAKE,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,IAAW,gBAAgB,gBACrB;UACT,kBAAC,UAAD;OACE,MAAK;OACL,eAAe,EAAW,WAAW;OACrC,WAAU;iBACX;OAEQ,CAAA,CACL;;KACD;;GACH,CAAA,CACF"}
1
+ {"version":3,"file":"EditCouponPage.js","names":[],"sources":["../../../../../src/billing/modules/coupons/pages/EditCouponPage.tsx"],"sourcesContent":["/**\n * Edit Coupon Page\n *\n * Form for editing existing coupon codes\n * Note: Only certain fields can be edited (name, description, status, usage limits, expiration)\n * Code, type, value, and scope cannot be changed after creation\n */\n\nimport { type FC, useState, useEffect } from 'react';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { ArrowLeft, Save, AlertCircle, CheckCircle } from 'lucide-react';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport {\n useGetCouponLazyQuery,\n useUpdateCouponMutation,\n} from '../../../../generated/global-operations';\nimport { Coupon, CouponStatus } from '../../../../generated/global-types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface EditFormData {\n name: string;\n description: string;\n status: CouponStatus;\n maxTotalUses: string;\n maxUsesPerUser: string;\n validUntil: string;\n}\n\nexport const EditCouponPage: 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\n // Get coupon ID from URL\n const couponId = window.location.pathname.split('/').slice(-2)[0];\n\n const [getCoupon, { data, loading: loadingCoupon, error: loadError }] = useGetCouponLazyQuery();\n const [updateCoupon, { loading: updating }] = useUpdateCouponMutation();\n\n const [formData, setFormData] = useState<EditFormData>({\n name: '',\n description: '',\n status: CouponStatus.Active,\n maxTotalUses: '',\n maxUsesPerUser: '',\n validUntil: '',\n });\n\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [submitError, setSubmitError] = useState('');\n const [submitSuccess, setSubmitSuccess] = useState(false);\n\n // Load coupon data\n useEffect(() => {\n if (couponId) {\n getCoupon({ variables: { id: couponId } });\n }\n }, [couponId, getCoupon]);\n\n // Populate form when data loads\n useEffect(() => {\n if (data?.getCoupon) {\n const coupon = data.getCoupon as Coupon;\n setFormData({\n name: coupon.name,\n description: coupon.description || '',\n status: coupon.status,\n maxTotalUses: coupon.maxTotalUses?.toString() || '',\n maxUsesPerUser: coupon.maxUsesPerUser.toString(),\n validUntil: coupon.validUntil\n ? new Date(coupon.validUntil).toISOString().split('T')[0]\n : '',\n });\n }\n }, [data]);\n\n // Check permissions\n if (!permissions.canManageDiscounts) {\n return <AccessDenied message=\"You don't have permission to manage coupons.\" />;\n }\n\n const handleInputChange = (field: keyof EditFormData, value: string) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field]) {\n setErrors((prev) => ({ ...prev, [field]: '' }));\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: Record<string, string> = {};\n\n if (!formData.name.trim()) {\n newErrors.name = 'Name is required';\n }\n\n if (formData.maxTotalUses && parseInt(formData.maxTotalUses) < 1) {\n newErrors.maxTotalUses = 'Must be at least 1';\n }\n\n if (formData.maxUsesPerUser && parseInt(formData.maxUsesPerUser) < 1) {\n newErrors.maxUsesPerUser = 'Must be at least 1';\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n setSubmitError('');\n setSubmitSuccess(false);\n\n if (!validateForm()) {\n return;\n }\n\n if (!couponId) {\n setSubmitError('Coupon ID is missing');\n return;\n }\n\n try {\n const input: {\n name?: string;\n description?: string;\n status?: CouponStatus;\n maxTotalUses?: number;\n maxUsesPerUser?: number;\n validUntil?: string;\n } = {};\n\n if (formData.name.trim()) input.name = formData.name.trim();\n if (formData.description.trim()) input.description = formData.description.trim();\n if (formData.status) input.status = formData.status;\n if (formData.maxTotalUses) input.maxTotalUses = parseInt(formData.maxTotalUses);\n if (formData.maxUsesPerUser) input.maxUsesPerUser = parseInt(formData.maxUsesPerUser);\n if (formData.validUntil) input.validUntil = new Date(formData.validUntil).toISOString();\n\n await updateCoupon({\n variables: {\n id: couponId,\n input,\n },\n });\n\n setSubmitSuccess(true);\n setTimeout(() => {\n navigateTo('/coupons');\n }, 1500);\n } catch (error) {\n console.error('Failed to update coupon:', error);\n setSubmitError(error instanceof Error ? error.message : 'Failed to update coupon');\n }\n };\n\n if (loadingCoupon) {\n return (\n <div className=\"p-8\">\n <PageHeader\n title={tr('billing.coupons.editTitle', 'Edit Coupon')}\n description=\"Loading coupon data...\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n }\n />\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"text-muted-foreground\">Loading coupon…</div>\n </div>\n </div>\n );\n }\n\n if (loadError || !data?.getCoupon) {\n return (\n <div className=\"p-8\">\n <PageHeader\n title=\"Edit Coupon\"\n description=\"Coupon not found\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back\n </button>\n }\n />\n <div className=\"flex items-center justify-center h-64\">\n <div className=\"text-destructive\">\n {loadError ? `Error: ${loadError.message}` : 'Coupon not found'}\n </div>\n </div>\n </div>\n );\n }\n\n const coupon = data.getCoupon as Coupon;\n\n return (\n <div className=\"p-8\">\n <PageHeader\n title=\"Edit Coupon\"\n description={`Editing: ${coupon.code}`}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-4 py-2 rounded-lg border border-border text-foreground hover:bg-muted flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back to Coupons\n </button>\n }\n />\n\n <div className=\"max-w-3xl mx-auto mt-8\">\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Read-only Information Card */}\n <div className=\"bg-muted/30 border border-border rounded-lg p-6\">\n <h3 className=\"text-lg font-semibold text-foreground mb-4\">\n Coupon Details (Read-Only)\n </h3>\n <div className=\"grid grid-cols-2 gap-4 text-sm\">\n <div>\n <span className=\"text-muted-foreground\">Code:</span>\n <span className=\"ml-2 font-mono font-medium text-foreground\">{coupon.code}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Type:</span>\n <span className=\"ml-2 text-foreground capitalize\">\n {coupon.type.replace('_', ' ').toLowerCase()}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Value:</span>\n <span className=\"ml-2 text-foreground\">\n {coupon.type === 'PERCENTAGE' ? `${coupon.value}%` : `$${coupon.value}`}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Scope:</span>\n <span className=\"ml-2 text-foreground\">{coupon.scope}</span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Current Uses:</span>\n <span className=\"ml-2 text-foreground\">\n {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </span>\n </div>\n <div>\n <span className=\"text-muted-foreground\">Created:</span>\n <span className=\"ml-2 text-foreground\">\n {new Date(coupon.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n </div>\n\n {/* Editable Fields Card */}\n <div className=\"bg-card border border-border rounded-lg p-6 space-y-6\">\n <h3 className=\"text-lg font-semibold text-foreground\">Editable Fields</h3>\n\n {/* Name */}\n <div className=\"space-y-2\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-foreground\">\n Name <span className=\"text-destructive\">*</span>\n </label>\n <input\n id=\"name\"\n type=\"text\"\n value={formData.name}\n onChange={(e) => handleInputChange('name', e.target.value)}\n placeholder=\"e.g., Welcome Discount\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n {errors.name && <p className=\"text-sm text-destructive\">{errors.name}</p>}\n </div>\n\n {/* Description */}\n <div className=\"space-y-2\">\n <label htmlFor=\"description\" className=\"block text-sm font-medium text-foreground\">\n Description\n </label>\n <textarea\n id=\"description\"\n value={formData.description}\n onChange={(e) => handleInputChange('description', e.target.value)}\n placeholder=\"Optional description\"\n rows={3}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20 resize-none\"\n />\n </div>\n\n {/* Status */}\n <div className=\"space-y-2\">\n <label htmlFor=\"status\" className=\"block text-sm font-medium text-foreground\">\n Status\n </label>\n <select\n id=\"status\"\n value={formData.status}\n onChange={(e) => handleInputChange('status', e.target.value)}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n >\n <option value=\"ACTIVE\">Active</option>\n <option value=\"INACTIVE\">Inactive</option>\n <option value=\"EXPIRED\">Expired</option>\n <option value=\"DEPLETED\">Depleted</option>\n </select>\n </div>\n\n {/* Max Total Uses */}\n <div className=\"space-y-2\">\n <label htmlFor=\"maxTotalUses\" className=\"block text-sm font-medium text-foreground\">\n Maximum Total Uses\n </label>\n <input\n id=\"maxTotalUses\"\n type=\"number\"\n min=\"1\"\n value={formData.maxTotalUses}\n onChange={(e) => handleInputChange('maxTotalUses', e.target.value)}\n placeholder=\"Leave empty for unlimited\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n {errors.maxTotalUses && (\n <p className=\"text-sm text-destructive\">{errors.maxTotalUses}</p>\n )}\n <p className=\"text-sm text-muted-foreground\">\n Current uses: {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </p>\n </div>\n\n {/* Max Uses Per User */}\n <div className=\"space-y-2\">\n <label htmlFor=\"maxUsesPerUser\" className=\"block text-sm font-medium text-foreground\">\n Maximum Uses Per User\n </label>\n <input\n id=\"maxUsesPerUser\"\n type=\"number\"\n min=\"1\"\n value={formData.maxUsesPerUser}\n onChange={(e) => handleInputChange('maxUsesPerUser', e.target.value)}\n placeholder=\"e.g., 1\"\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n {errors.maxUsesPerUser && (\n <p className=\"text-sm text-destructive\">{errors.maxUsesPerUser}</p>\n )}\n </div>\n\n {/* Valid Until */}\n <div className=\"space-y-2\">\n <label htmlFor=\"validUntil\" className=\"block text-sm font-medium text-foreground\">\n Valid Until\n </label>\n <input\n id=\"validUntil\"\n type=\"date\"\n value={formData.validUntil}\n onChange={(e) => handleInputChange('validUntil', e.target.value)}\n className=\"w-full px-4 py-2 rounded-lg border border-border bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary/20\"\n />\n <p className=\"text-sm text-muted-foreground\">Leave empty for no expiration</p>\n </div>\n </div>\n\n {/* Error/Success Messages */}\n {submitError && (\n <div className=\"flex items-start gap-3 p-4 rounded-lg bg-destructive/10 border border-destructive/20\">\n <AlertCircle className=\"size-5 text-destructive mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-destructive\">Error</p>\n <p className=\"text-sm text-destructive/90 mt-1\">{submitError}</p>\n </div>\n </div>\n )}\n\n {submitSuccess && (\n <div className=\"flex items-start gap-3 p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-bg-subtle\">\n <CheckCircle className=\"size-5 text-status-success-text mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-success-text\">Success</p>\n <p className=\"text-sm text-status-success-text/90 mt-1\">\n Coupon updated successfully! Redirecting…\n </p>\n </div>\n </div>\n )}\n\n {/* Actions */}\n <div className=\"flex gap-4 pt-4 border-t border-border\">\n <button\n type=\"submit\"\n disabled={updating || submitSuccess}\n className=\"px-6 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2\"\n >\n <Save className=\"size-4\" />\n {updating ? 'Updating...' : 'Update Coupon'}\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo('/coupons')}\n className=\"px-6 py-2 rounded-lg border border-border text-foreground hover:bg-muted\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -104,7 +104,7 @@ var u = () => {
104
104
  })
105
105
  }), A ? /* @__PURE__ */ c("div", {
106
106
  className: "p-6 text-sm text-muted-foreground",
107
- children: "Loading recent credit activity..."
107
+ children: "Loading recent credit activity"
108
108
  }) : k.length === 0 ? /* @__PURE__ */ c("div", {
109
109
  className: "p-6 text-sm text-muted-foreground",
110
110
  children: "No recent credit transactions found."
@@ -1 +1 @@
1
- {"version":3,"file":"AdminCreditsPage.js","names":[],"sources":["../../../../../src/billing/modules/credits/pages/AdminCreditsPage.tsx"],"sourcesContent":["import { useMemo, useState, type FC } from 'react';\n\nimport { useGetBillingAccountsSettingsQuery } from '../../../../generated/global-operations';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport type { BillingAccount } from '../../../shared/types';\nimport { useCreditMutations, useCreditTransactions } from '../hooks/useCredits';\n\nexport const AdminCreditsPage: FC = () => {\n const permissions = useBillingPermissions();\n const { grantCredits } = useCreditMutations();\n const { data, loading, refetch } = useGetBillingAccountsSettingsQuery();\n const billingAccounts = useMemo<BillingAccount[]>(\n () => data?.getBillingAccountsByOrg || [],\n [data]\n );\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<string>('');\n const [amount, setAmount] = useState<string>('');\n const [reason, setReason] = useState<string>('');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [message, setMessage] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const activeBillingAccountId = selectedBillingAccountId || billingAccounts[0]?.id || '';\n const {\n transactions,\n isLoading: isLoadingTransactions,\n refetch: refetchTransactions,\n } = useCreditTransactions({\n billingAccountId: activeBillingAccountId,\n days: 30,\n page: 1,\n pageSize: 10,\n });\n\n if (!permissions.canGrantCredits) {\n return <AccessDenied message=\"You don't have permission to grant credits.\" />;\n }\n\n const handleGrantCredits = async () => {\n if (!activeBillingAccountId) {\n setError('Select a billing account first.');\n return;\n }\n\n if (!amount || Number(amount) <= 0) {\n setError('Enter a valid credit amount.');\n return;\n }\n\n setIsSubmitting(true);\n setError(null);\n setMessage(null);\n\n try {\n await grantCredits({\n billingAccountId: activeBillingAccountId,\n amount: Number(amount),\n reason,\n });\n\n await Promise.all([refetch(), refetchTransactions()]);\n setAmount('');\n setReason('');\n setMessage('Credits granted successfully.');\n } catch (grantError) {\n setError(grantError instanceof Error ? grantError.message : 'Failed to grant credits.');\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Credit Grants\"\n description=\"Grant credits to billing accounts and review the latest credit ledger activity.\"\n />\n\n <div className=\"grid grid-cols-1 xl:grid-cols-[360px_minmax(0,1fr)] gap-6\">\n <div className=\"border border-border rounded-lg bg-card p-6 space-y-4 h-fit\">\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n Billing Account\n </label>\n <select\n value={activeBillingAccountId}\n onChange={(event) => setSelectedBillingAccountId(event.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n {billingAccounts.map((account) => (\n <option key={account.id} value={account.id}>\n {account.name}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">Credits</label>\n <input\n type=\"number\"\n min=\"1\"\n step=\"1\"\n value={amount}\n onChange={(event) => setAmount(event.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"500\"\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">Reason</label>\n <textarea\n value={reason}\n onChange={(event) => setReason(event.target.value)}\n className=\"w-full min-h-24 px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"Promotional credit grant, service recovery, manual adjustment...\"\n />\n </div>\n\n {message ? <p className=\"text-sm text-status-success-text\">{message}</p> : null}\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n\n <button\n type=\"button\"\n onClick={handleGrantCredits}\n disabled={loading || isSubmitting || billingAccounts.length === 0}\n className=\"w-full px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isSubmitting ? 'Granting Credits...' : 'Grant Credits'}\n </button>\n </div>\n\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">Recent Credit Ledger</h2>\n </div>\n\n {isLoadingTransactions ? (\n <div className=\"p-6 text-sm text-muted-foreground\">\n Loading recent credit activity...\n </div>\n ) : transactions.length === 0 ? (\n <div className=\"p-6 text-sm text-muted-foreground\">\n No recent credit transactions found.\n </div>\n ) : (\n <div className=\"divide-y divide-border\">\n {transactions.map((transaction) => (\n <div key={transaction.id} className=\"p-4 flex items-center justify-between gap-4\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">{transaction.type}</p>\n <p className=\"text-xs text-muted-foreground\">\n {(transaction.context as { reason?: string } | undefined)?.reason ||\n 'No reason'}\n </p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-semibold text-foreground\">\n {transaction.amount > 0 ? '+' : ''}\n {transaction.amount}\n </p>\n <p className=\"text-xs text-muted-foreground\">{transaction.status}</p>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminCreditsPage.js","names":[],"sources":["../../../../../src/billing/modules/credits/pages/AdminCreditsPage.tsx"],"sourcesContent":["import { useMemo, useState, type FC } from 'react';\n\nimport { useGetBillingAccountsSettingsQuery } from '../../../../generated/global-operations';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied, PageHeader } from '../../../shared/components';\nimport type { BillingAccount } from '../../../shared/types';\nimport { useCreditMutations, useCreditTransactions } from '../hooks/useCredits';\n\nexport const AdminCreditsPage: FC = () => {\n const permissions = useBillingPermissions();\n const { grantCredits } = useCreditMutations();\n const { data, loading, refetch } = useGetBillingAccountsSettingsQuery();\n const billingAccounts = useMemo<BillingAccount[]>(\n () => data?.getBillingAccountsByOrg || [],\n [data]\n );\n const [selectedBillingAccountId, setSelectedBillingAccountId] = useState<string>('');\n const [amount, setAmount] = useState<string>('');\n const [reason, setReason] = useState<string>('');\n const [isSubmitting, setIsSubmitting] = useState(false);\n const [message, setMessage] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const activeBillingAccountId = selectedBillingAccountId || billingAccounts[0]?.id || '';\n const {\n transactions,\n isLoading: isLoadingTransactions,\n refetch: refetchTransactions,\n } = useCreditTransactions({\n billingAccountId: activeBillingAccountId,\n days: 30,\n page: 1,\n pageSize: 10,\n });\n\n if (!permissions.canGrantCredits) {\n return <AccessDenied message=\"You don't have permission to grant credits.\" />;\n }\n\n const handleGrantCredits = async () => {\n if (!activeBillingAccountId) {\n setError('Select a billing account first.');\n return;\n }\n\n if (!amount || Number(amount) <= 0) {\n setError('Enter a valid credit amount.');\n return;\n }\n\n setIsSubmitting(true);\n setError(null);\n setMessage(null);\n\n try {\n await grantCredits({\n billingAccountId: activeBillingAccountId,\n amount: Number(amount),\n reason,\n });\n\n await Promise.all([refetch(), refetchTransactions()]);\n setAmount('');\n setReason('');\n setMessage('Credits granted successfully.');\n } catch (grantError) {\n setError(grantError instanceof Error ? grantError.message : 'Failed to grant credits.');\n } finally {\n setIsSubmitting(false);\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Credit Grants\"\n description=\"Grant credits to billing accounts and review the latest credit ledger activity.\"\n />\n\n <div className=\"grid grid-cols-1 xl:grid-cols-[360px_minmax(0,1fr)] gap-6\">\n <div className=\"border border-border rounded-lg bg-card p-6 space-y-4 h-fit\">\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">\n Billing Account\n </label>\n <select\n value={activeBillingAccountId}\n onChange={(event) => setSelectedBillingAccountId(event.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n {billingAccounts.map((account) => (\n <option key={account.id} value={account.id}>\n {account.name}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">Credits</label>\n <input\n type=\"number\"\n min=\"1\"\n step=\"1\"\n value={amount}\n onChange={(event) => setAmount(event.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"500\"\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-foreground mb-1\">Reason</label>\n <textarea\n value={reason}\n onChange={(event) => setReason(event.target.value)}\n className=\"w-full min-h-24 px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"Promotional credit grant, service recovery, manual adjustment...\"\n />\n </div>\n\n {message ? <p className=\"text-sm text-status-success-text\">{message}</p> : null}\n {error ? <p className=\"text-sm text-destructive\">{error}</p> : null}\n\n <button\n type=\"button\"\n onClick={handleGrantCredits}\n disabled={loading || isSubmitting || billingAccounts.length === 0}\n className=\"w-full px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isSubmitting ? 'Granting Credits...' : 'Grant Credits'}\n </button>\n </div>\n\n <div className=\"border border-border rounded-lg bg-card overflow-hidden\">\n <div className=\"p-4 border-b border-border bg-muted/30\">\n <h2 className=\"font-semibold text-foreground\">Recent Credit Ledger</h2>\n </div>\n\n {isLoadingTransactions ? (\n <div className=\"p-6 text-sm text-muted-foreground\">Loading recent credit activity…</div>\n ) : transactions.length === 0 ? (\n <div className=\"p-6 text-sm text-muted-foreground\">\n No recent credit transactions found.\n </div>\n ) : (\n <div className=\"divide-y divide-border\">\n {transactions.map((transaction) => (\n <div key={transaction.id} className=\"p-4 flex items-center justify-between gap-4\">\n <div>\n <p className=\"text-sm font-medium text-foreground\">{transaction.type}</p>\n <p className=\"text-xs text-muted-foreground\">\n {(transaction.context as { reason?: string } | undefined)?.reason ||\n 'No reason'}\n </p>\n </div>\n <div className=\"text-right\">\n <p className=\"text-sm font-semibold text-foreground\">\n {transaction.amount > 0 ? '+' : ''}\n {transaction.amount}\n </p>\n <p className=\"text-xs text-muted-foreground\">{transaction.status}</p>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -35,7 +35,7 @@ var j = () => {
35
35
  className: "p-6",
36
36
  children: /* @__PURE__ */ O(n, {
37
37
  icon: /* @__PURE__ */ O("svg", {
38
- className: "w-6 h-6 text-muted-foreground",
38
+ className: "size-6 text-muted-foreground",
39
39
  fill: "none",
40
40
  viewBox: "0 0 24 24",
41
41
  stroke: "currentColor",
@@ -76,9 +76,10 @@ var j = () => {
76
76
  className: "space-y-6 p-6",
77
77
  children: [
78
78
  /* @__PURE__ */ k("button", {
79
+ type: "button",
79
80
  onClick: () => T("/overview"),
80
81
  className: "inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors",
81
- children: [/* @__PURE__ */ O(h, { className: "w-4 h-4" }), s("billing.credits.backToOverview", "Back to Overview")]
82
+ children: [/* @__PURE__ */ O(h, { className: "size-4" }), s("billing.credits.backToOverview", "Back to Overview")]
82
83
  }),
83
84
  /* @__PURE__ */ O(t, {
84
85
  title: "Credit History",
@@ -91,7 +92,7 @@ var j = () => {
91
92
  children: [
92
93
  /* @__PURE__ */ k("div", {
93
94
  className: "flex items-start gap-2",
94
- children: [/* @__PURE__ */ O(x, { className: "w-4 h-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [
95
+ children: [/* @__PURE__ */ O(x, { className: "size-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [
95
96
  /* @__PURE__ */ O("p", {
96
97
  className: "text-xs text-muted-foreground mb-0.5",
97
98
  children: "Account"
@@ -102,16 +103,16 @@ var j = () => {
102
103
  }),
103
104
  B.isActive ? /* @__PURE__ */ k("span", {
104
105
  className: "inline-flex items-center gap-1 mt-1 px-1.5 py-0.5 text-xs font-medium rounded bg-status-success-bg-subtle text-status-success-text",
105
- children: [/* @__PURE__ */ O(_, { className: "w-3 h-3" }), "Active"]
106
+ children: [/* @__PURE__ */ O(_, { className: "size-3" }), "Active"]
106
107
  }) : /* @__PURE__ */ k("span", {
107
108
  className: "inline-flex items-center gap-1 mt-1 px-1.5 py-0.5 text-xs font-medium rounded bg-status-error-bg-subtle text-status-error-text",
108
- children: [/* @__PURE__ */ O(E, { className: "w-3 h-3" }), "Inactive"]
109
+ children: [/* @__PURE__ */ O(E, { className: "size-3" }), "Inactive"]
109
110
  })
110
111
  ] })]
111
112
  }),
112
113
  /* @__PURE__ */ k("div", {
113
114
  className: "flex items-start gap-2",
114
- children: [/* @__PURE__ */ O(C, { className: "w-4 h-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
115
+ children: [/* @__PURE__ */ O(C, { className: "size-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
115
116
  className: "text-xs text-muted-foreground mb-0.5",
116
117
  children: "Email"
117
118
  }), /* @__PURE__ */ O("p", {
@@ -121,7 +122,7 @@ var j = () => {
121
122
  }),
122
123
  /* @__PURE__ */ k("div", {
123
124
  className: "flex items-start gap-2",
124
- children: [/* @__PURE__ */ O(b, { className: "w-4 h-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
125
+ children: [/* @__PURE__ */ O(b, { className: "size-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
125
126
  className: "text-xs text-muted-foreground mb-0.5",
126
127
  children: "Credit Balance"
127
128
  }), /* @__PURE__ */ k("p", {
@@ -131,7 +132,7 @@ var j = () => {
131
132
  }),
132
133
  /* @__PURE__ */ k("div", {
133
134
  className: "flex items-start gap-2",
134
- children: [/* @__PURE__ */ O(S, { className: "w-4 h-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
135
+ children: [/* @__PURE__ */ O(S, { className: "size-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
135
136
  className: "text-xs text-muted-foreground mb-0.5",
136
137
  children: "Tax ID"
137
138
  }), /* @__PURE__ */ O("p", {
@@ -141,7 +142,7 @@ var j = () => {
141
142
  }),
142
143
  /* @__PURE__ */ k("div", {
143
144
  className: "flex items-start gap-2",
144
- children: [/* @__PURE__ */ O(g, { className: "w-4 h-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
145
+ children: [/* @__PURE__ */ O(g, { className: "size-4 text-muted-foreground mt-0.5" }), /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
145
146
  className: "text-xs text-muted-foreground mb-0.5",
146
147
  children: "Member Since"
147
148
  }), /* @__PURE__ */ O("p", {
@@ -162,6 +163,7 @@ var j = () => {
162
163
  30,
163
164
  90
164
165
  ].map((e) => /* @__PURE__ */ k("button", {
166
+ type: "button",
165
167
  onClick: () => {
166
168
  N(e), L(1);
167
169
  },
@@ -177,6 +179,7 @@ var j = () => {
177
179
  "usage",
178
180
  "adjustment"
179
181
  ].map((e) => /* @__PURE__ */ O("button", {
182
+ type: "button",
180
183
  onClick: () => {
181
184
  F(e), L(1);
182
185
  },
@@ -199,7 +202,7 @@ var j = () => {
199
202
  ]
200
203
  }),
201
204
  q.length === 0 ? /* @__PURE__ */ O(n, {
202
- icon: /* @__PURE__ */ O(b, { className: "w-6 h-6 text-muted-foreground" }),
205
+ icon: /* @__PURE__ */ O(b, { className: "size-6 text-muted-foreground" }),
203
206
  title: "No credit transactions found",
204
207
  description: P === "all" ? "Your credit transaction history will appear here." : "No transactions match your filter."
205
208
  }) : /* @__PURE__ */ O("div", {
@@ -225,12 +228,13 @@ var j = () => {
225
228
  className: "flex items-center gap-2",
226
229
  children: [
227
230
  /* @__PURE__ */ k("button", {
231
+ type: "button",
228
232
  onClick: () => {
229
233
  I > 1 && L((e) => e - 1);
230
234
  },
231
235
  disabled: I <= 1,
232
236
  className: "inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
233
- children: [/* @__PURE__ */ O(v, { className: "w-4 h-4" }), "Previous"]
237
+ children: [/* @__PURE__ */ O(v, { className: "size-4" }), "Previous"]
234
238
  }),
235
239
  /* @__PURE__ */ k("span", {
236
240
  className: "text-sm text-muted-foreground px-2",
@@ -242,12 +246,13 @@ var j = () => {
242
246
  ]
243
247
  }),
244
248
  /* @__PURE__ */ k("button", {
249
+ type: "button",
245
250
  onClick: () => {
246
251
  H.hasMore && L((e) => e + 1);
247
252
  },
248
253
  disabled: !H.hasMore,
249
254
  className: "inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
250
- children: ["Next", /* @__PURE__ */ O(y, { className: "w-4 h-4" })]
255
+ children: ["Next", /* @__PURE__ */ O(y, { className: "size-4" })]
251
256
  })
252
257
  ]
253
258
  })]
@@ -260,7 +265,7 @@ var j = () => {
260
265
  className: "flex items-center gap-4 p-4 border border-border rounded-lg bg-card hover:shadow-sm transition-shadow",
261
266
  children: [
262
267
  /* @__PURE__ */ O("div", {
263
- className: `w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 ${(() => {
268
+ className: `size-10 rounded-full flex items-center justify-center flex-shrink-0 ${(() => {
264
269
  if (e.type === "adjustment") return r ? "bg-status-success-bg-subtle" : "bg-status-error-bg-subtle";
265
270
  switch (e.type) {
266
271
  case "purchase": return "bg-status-success-bg-subtle";
@@ -269,11 +274,11 @@ var j = () => {
269
274
  }
270
275
  })()}`,
271
276
  children: (() => {
272
- if (e.type === "adjustment") return r ? /* @__PURE__ */ O(T, { className: "w-4 h-4 text-status-success-text" }) : /* @__PURE__ */ O(w, { className: "w-4 h-4 text-status-error-text" });
277
+ if (e.type === "adjustment") return r ? /* @__PURE__ */ O(T, { className: "size-4 text-status-success-text" }) : /* @__PURE__ */ O(w, { className: "size-4 text-status-error-text" });
273
278
  switch (e.type) {
274
- case "purchase": return /* @__PURE__ */ O(T, { className: "w-4 h-4 text-status-success-text" });
275
- case "usage": return /* @__PURE__ */ O(w, { className: "w-4 h-4 text-status-warning-text" });
276
- default: return /* @__PURE__ */ O(b, { className: "w-4 h-4 text-muted-foreground" });
279
+ case "purchase": return /* @__PURE__ */ O(T, { className: "size-4 text-status-success-text" });
280
+ case "usage": return /* @__PURE__ */ O(w, { className: "size-4 text-status-warning-text" });
281
+ default: return /* @__PURE__ */ O(b, { className: "size-4 text-muted-foreground" });
277
282
  }
278
283
  })()
279
284
  }),
@@ -1 +1 @@
1
- {"version":3,"file":"CreditTransactionsPage.js","names":[],"sources":["../../../../../src/billing/modules/credits/pages/CreditTransactionsPage.tsx"],"sourcesContent":["/**\n * Credits Module - Credit Transactions Page\n * Displays credit transaction history for a specific billing account\n */\n\nimport { useState, type FC } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Coins,\n CreditCard,\n Mail,\n ChevronLeft,\n ChevronRight,\n Plus,\n Minus,\n CheckCircle,\n XCircle,\n FileText,\n Calendar,\n} from 'lucide-react';\nimport { useCreditTransactions } from '../hooks/useCredits';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { AccessDenied, PageHeader, EmptyState, ServerError } from '../../../shared/components';\nimport {\n formatDateTime,\n formatRelativeTime,\n formatCreditTransactionType,\n isServerError,\n} from '../../../shared/utils';\nimport {\n getCreditTransactionStatusColor,\n getStatusBadgeClasses,\n} from '../../../shared/utils/status';\nimport type { CreditTransaction, CreditTransactionType } from '../../../shared/types';\nimport { useGetBillingAccountQuery } from '../../../../generated/global-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ntype FilterType = 'all' | CreditTransactionType;\n\nexport const CreditTransactionsPage: 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 { billingAccountId } = useParams<{ billingAccountId: string }>();\n const permissions = useBillingPermissions();\n const navigateTo = useBillingNavigate();\n\n const [days, setDays] = useState(30);\n const [filterType, setFilterType] = useState<FilterType>('all');\n const [page, setPage] = useState(1);\n const pageSize = 20;\n\n // Fetch billing account details\n const { data: accountData, loading: isLoadingAccount } = useGetBillingAccountQuery({\n variables: { id: billingAccountId! },\n skip: !billingAccountId,\n });\n\n const billingAccount = accountData?.getBillingAccount;\n\n // Fetch credit transactions\n const {\n transactions,\n pagination,\n isLoading: isLoadingTransactions,\n error,\n refetch,\n } = useCreditTransactions({\n billingAccountId: billingAccountId || '',\n days,\n page,\n pageSize,\n });\n\n // Permission check\n if (!permissions.canViewCredits) {\n return <AccessDenied message=\"You don't have permission to view credit history.\" />;\n }\n\n // Show error if no billing account ID provided\n if (!billingAccountId) {\n return (\n <div className=\"p-6\">\n <EmptyState\n icon={\n <svg\n className=\"w-6 h-6 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"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 }\n title=\"No billing account selected\"\n description=\"Please select a billing account from the overview page to view credit history.\"\n />\n </div>\n );\n }\n\n // Show server error if backend is down\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load credit transactions. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n const isLoading = isLoadingAccount || isLoadingTransactions;\n\n // Filter transactions\n const filteredTransactions =\n filterType === 'all' ? transactions : transactions.filter((t) => t.type === filterType);\n\n // Calculate total pages\n const totalPages = Math.ceil(pagination.totalCount / pageSize);\n\n // Pagination handlers\n const goToNextPage = () => {\n if (pagination.hasMore) {\n setPage((p) => p + 1);\n }\n };\n\n const goToPreviousPage = () => {\n if (page > 1) {\n setPage((p) => p - 1);\n }\n };\n\n if (isLoading && !billingAccount) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"space-y-2\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-16 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Back Button */}\n <button\n onClick={() => navigateTo('/overview')}\n className=\"inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors\"\n >\n <ArrowLeft className=\"w-4 h-4\" />\n {tr('billing.credits.backToOverview', 'Back to Overview')}\n </button>\n\n <PageHeader\n title=\"Credit History\"\n description={\n billingAccount\n ? `Credit transactions for ${billingAccount.name}`\n : 'View your credit transaction history'\n }\n />\n\n {/* Billing Account Info Card */}\n {billingAccount && (\n <div className=\"border border-border rounded-lg p-4 bg-card\">\n <div className=\"grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4\">\n {/* Account Name & Status */}\n <div className=\"flex items-start gap-2\">\n <CreditCard className=\"w-4 h-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Account</p>\n <p className=\"text-sm font-medium\">{billingAccount.name}</p>\n {billingAccount.isActive ? (\n <span className=\"inline-flex items-center gap-1 mt-1 px-1.5 py-0.5 text-xs font-medium rounded bg-status-success-bg-subtle text-status-success-text\">\n <CheckCircle className=\"w-3 h-3\" />\n Active\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1 mt-1 px-1.5 py-0.5 text-xs font-medium rounded bg-status-error-bg-subtle text-status-error-text\">\n <XCircle className=\"w-3 h-3\" />\n Inactive\n </span>\n )}\n </div>\n </div>\n\n {/* Email */}\n <div className=\"flex items-start gap-2\">\n <Mail className=\"w-4 h-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Email</p>\n <p className=\"text-sm font-medium break-all\">\n {billingAccount.billingEmail || billingAccount.email}\n </p>\n </div>\n </div>\n\n {/* Credit Balance */}\n <div className=\"flex items-start gap-2\">\n <Coins className=\"w-4 h-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Credit Balance</p>\n <p className=\"text-sm font-semibold text-foreground\">\n {(billingAccount.creditAmount || 0).toLocaleString()} Credits\n </p>\n </div>\n </div>\n\n {/* Tax ID */}\n <div className=\"flex items-start gap-2\">\n <FileText className=\"w-4 h-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Tax ID</p>\n <p className=\"text-sm font-medium\">{billingAccount.taxId || '-'}</p>\n </div>\n </div>\n\n {/* Account Created */}\n <div className=\"flex items-start gap-2\">\n <Calendar className=\"w-4 h-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Member Since</p>\n <p className=\"text-sm font-medium\">\n {billingAccount.createdAt ? formatDateTime(billingAccount.createdAt) : '-'}\n </p>\n </div>\n </div>\n </div>\n </div>\n )}\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border\">\n {/* Time Range */}\n <div className=\"inline-flex rounded-lg border border-border p-1 bg-muted/50\">\n {[7, 30, 90].map((d) => (\n <button\n key={d}\n onClick={() => {\n setDays(d);\n setPage(1); // Reset to first page on filter change\n }}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n days === d\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {d}d\n </button>\n ))}\n </div>\n\n {/* Transaction Type Filter */}\n <div className=\"inline-flex rounded-lg border border-border p-1 bg-muted/50\">\n {(['all', 'purchase', 'usage', 'adjustment'] as FilterType[]).map((type) => (\n <button\n key={type}\n onClick={() => {\n setFilterType(type);\n setPage(1); // Reset to first page on filter change\n }}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors capitalize ${\n filterType === type\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {type === 'all' ? 'All' : formatCreditTransactionType(type as CreditTransactionType)}\n </button>\n ))}\n </div>\n\n {/* Transaction count */}\n <div className=\"ml-auto text-sm text-muted-foreground\">\n {pagination.totalCount} transaction{pagination.totalCount !== 1 ? 's' : ''} in last {days}{' '}\n days\n </div>\n </div>\n\n {/* Transactions List */}\n {filteredTransactions.length === 0 ? (\n <EmptyState\n icon={<Coins className=\"w-6 h-6 text-muted-foreground\" />}\n title=\"No credit transactions found\"\n description={\n filterType !== 'all'\n ? 'No transactions match your filter.'\n : 'Your credit transaction history will appear here.'\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {filteredTransactions.map((transaction) => (\n <CreditTransactionRow key={transaction.id} transaction={transaction} />\n ))}\n </div>\n )}\n\n {/* Pagination Controls */}\n {pagination.totalCount > 0 && (\n <div className=\"flex items-center justify-between border-t border-border pt-4\">\n <p className=\"text-sm text-muted-foreground\">\n Showing {(page - 1) * pageSize + 1} to{' '}\n {Math.min(page * pageSize, pagination.totalCount)} of {pagination.totalCount}{' '}\n transactions\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={goToPreviousPage}\n disabled={page <= 1}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n <ChevronLeft className=\"w-4 h-4\" />\n Previous\n </button>\n <span className=\"text-sm text-muted-foreground px-2\">\n Page {page} of {totalPages || 1}\n </span>\n <button\n onClick={goToNextPage}\n disabled={!pagination.hasMore}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n Next\n <ChevronRight className=\"w-4 h-4\" />\n </button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Credit Transaction Row Component\n// ============================================================================\n\ninterface CreditTransactionRowProps {\n transaction: CreditTransaction;\n}\n\nconst CreditTransactionRow: FC<CreditTransactionRowProps> = ({ transaction }) => {\n const statusColor = getCreditTransactionStatusColor(transaction.status);\n\n // FIX: Check the actual amount value, not just the transaction type\n // Negative amounts are debits (show red with \"-\"), positive amounts are credits (show green with \"+\")\n const amount = Number(transaction.amount);\n const isCredit = amount > 0;\n const absAmount = Math.abs(amount);\n\n const getTransactionIcon = () => {\n // For adjustments, show different icon based on amount sign\n if (transaction.type === 'adjustment') {\n return isCredit ? (\n <Plus className=\"w-4 h-4 text-status-success-text\" />\n ) : (\n <Minus className=\"w-4 h-4 text-status-error-text\" />\n );\n }\n\n switch (transaction.type) {\n case 'purchase':\n return <Plus className=\"w-4 h-4 text-status-success-text\" />;\n case 'usage':\n return <Minus className=\"w-4 h-4 text-status-warning-text\" />;\n default:\n return <Coins className=\"w-4 h-4 text-muted-foreground\" />;\n }\n };\n\n const getIconBackground = () => {\n // For adjustments, show different background based on amount sign\n if (transaction.type === 'adjustment') {\n return isCredit ? 'bg-status-success-bg-subtle' : 'bg-status-error-bg-subtle';\n }\n\n switch (transaction.type) {\n case 'purchase':\n return 'bg-status-success-bg-subtle';\n case 'usage':\n return 'bg-status-warning-bg-subtle';\n default:\n return 'bg-muted';\n }\n };\n\n return (\n <div className=\"flex items-center gap-4 p-4 border border-border rounded-lg bg-card hover:shadow-sm transition-shadow\">\n {/* Icon */}\n <div\n className={`w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 ${getIconBackground()}`}\n >\n {getTransactionIcon()}\n </div>\n\n {/* Info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <p className=\"font-medium text-foreground capitalize\">\n {formatCreditTransactionType(transaction.type)}\n </p>\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {transaction.status}\n </span>\n {transaction.entityType && (\n <span className=\"px-2 py-0.5 text-xs font-medium rounded bg-muted text-muted-foreground capitalize\">\n {transaction.entityType}\n </span>\n )}\n </div>\n <p className=\"text-sm text-muted-foreground mt-0.5\">\n {formatRelativeTime(transaction.createdAt)} • {formatDateTime(transaction.createdAt)}\n </p>\n {transaction.productId && transaction.productId !== 'default' && (\n <p className=\"text-xs text-muted-foreground mt-1\">Product: {transaction.productId}</p>\n )}\n </div>\n\n {/* Amount */}\n <div className=\"text-right flex-shrink-0\">\n <p\n className={`font-semibold text-lg ${isCredit ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {isCredit ? '+' : '-'}\n {absAmount.toLocaleString()} credits\n </p>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CreditTransactionsPage.js","names":[],"sources":["../../../../../src/billing/modules/credits/pages/CreditTransactionsPage.tsx"],"sourcesContent":["/**\n * Credits Module - Credit Transactions Page\n * Displays credit transaction history for a specific billing account\n */\n\nimport { useState, type FC } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n ArrowLeft,\n Coins,\n CreditCard,\n Mail,\n ChevronLeft,\n ChevronRight,\n Plus,\n Minus,\n CheckCircle,\n XCircle,\n FileText,\n Calendar,\n} from 'lucide-react';\nimport { useCreditTransactions } from '../hooks/useCredits';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { AccessDenied, PageHeader, EmptyState, ServerError } from '../../../shared/components';\nimport {\n formatDateTime,\n formatRelativeTime,\n formatCreditTransactionType,\n isServerError,\n} from '../../../shared/utils';\nimport {\n getCreditTransactionStatusColor,\n getStatusBadgeClasses,\n} from '../../../shared/utils/status';\nimport type { CreditTransaction, CreditTransactionType } from '../../../shared/types';\nimport { useGetBillingAccountQuery } from '../../../../generated/global-operations';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ntype FilterType = 'all' | CreditTransactionType;\n\nexport const CreditTransactionsPage: 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 { billingAccountId } = useParams<{ billingAccountId: string }>();\n const permissions = useBillingPermissions();\n const navigateTo = useBillingNavigate();\n\n const [days, setDays] = useState(30);\n const [filterType, setFilterType] = useState<FilterType>('all');\n const [page, setPage] = useState(1);\n const pageSize = 20;\n\n // Fetch billing account details\n const { data: accountData, loading: isLoadingAccount } = useGetBillingAccountQuery({\n variables: { id: billingAccountId! },\n skip: !billingAccountId,\n });\n\n const billingAccount = accountData?.getBillingAccount;\n\n // Fetch credit transactions\n const {\n transactions,\n pagination,\n isLoading: isLoadingTransactions,\n error,\n refetch,\n } = useCreditTransactions({\n billingAccountId: billingAccountId || '',\n days,\n page,\n pageSize,\n });\n\n // Permission check\n if (!permissions.canViewCredits) {\n return <AccessDenied message=\"You don't have permission to view credit history.\" />;\n }\n\n // Show error if no billing account ID provided\n if (!billingAccountId) {\n return (\n <div className=\"p-6\">\n <EmptyState\n icon={\n <svg\n className=\"size-6 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"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 }\n title=\"No billing account selected\"\n description=\"Please select a billing account from the overview page to view credit history.\"\n />\n </div>\n );\n }\n\n // Show server error if backend is down\n if (error && isServerError(error)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load credit transactions. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n const isLoading = isLoadingAccount || isLoadingTransactions;\n\n // Filter transactions\n const filteredTransactions =\n filterType === 'all' ? transactions : transactions.filter((t) => t.type === filterType);\n\n // Calculate total pages\n const totalPages = Math.ceil(pagination.totalCount / pageSize);\n\n // Pagination handlers\n const goToNextPage = () => {\n if (pagination.hasMore) {\n setPage((p) => p + 1);\n }\n };\n\n const goToPreviousPage = () => {\n if (page > 1) {\n setPage((p) => p - 1);\n }\n };\n\n if (isLoading && !billingAccount) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"space-y-2\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-16 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={() => navigateTo('/overview')}\n className=\"inline-flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground transition-colors\"\n >\n <ArrowLeft className=\"size-4\" />\n {tr('billing.credits.backToOverview', 'Back to Overview')}\n </button>\n\n <PageHeader\n title=\"Credit History\"\n description={\n billingAccount\n ? `Credit transactions for ${billingAccount.name}`\n : 'View your credit transaction history'\n }\n />\n\n {/* Billing Account Info Card */}\n {billingAccount && (\n <div className=\"border border-border rounded-lg p-4 bg-card\">\n <div className=\"grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4\">\n {/* Account Name & Status */}\n <div className=\"flex items-start gap-2\">\n <CreditCard className=\"size-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Account</p>\n <p className=\"text-sm font-medium\">{billingAccount.name}</p>\n {billingAccount.isActive ? (\n <span className=\"inline-flex items-center gap-1 mt-1 px-1.5 py-0.5 text-xs font-medium rounded bg-status-success-bg-subtle text-status-success-text\">\n <CheckCircle className=\"size-3\" />\n Active\n </span>\n ) : (\n <span className=\"inline-flex items-center gap-1 mt-1 px-1.5 py-0.5 text-xs font-medium rounded bg-status-error-bg-subtle text-status-error-text\">\n <XCircle className=\"size-3\" />\n Inactive\n </span>\n )}\n </div>\n </div>\n\n {/* Email */}\n <div className=\"flex items-start gap-2\">\n <Mail className=\"size-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Email</p>\n <p className=\"text-sm font-medium break-all\">\n {billingAccount.billingEmail || billingAccount.email}\n </p>\n </div>\n </div>\n\n {/* Credit Balance */}\n <div className=\"flex items-start gap-2\">\n <Coins className=\"size-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Credit Balance</p>\n <p className=\"text-sm font-semibold text-foreground\">\n {(billingAccount.creditAmount || 0).toLocaleString()} Credits\n </p>\n </div>\n </div>\n\n {/* Tax ID */}\n <div className=\"flex items-start gap-2\">\n <FileText className=\"size-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Tax ID</p>\n <p className=\"text-sm font-medium\">{billingAccount.taxId || '-'}</p>\n </div>\n </div>\n\n {/* Account Created */}\n <div className=\"flex items-start gap-2\">\n <Calendar className=\"size-4 text-muted-foreground mt-0.5\" />\n <div>\n <p className=\"text-xs text-muted-foreground mb-0.5\">Member Since</p>\n <p className=\"text-sm font-medium\">\n {billingAccount.createdAt ? formatDateTime(billingAccount.createdAt) : '-'}\n </p>\n </div>\n </div>\n </div>\n </div>\n )}\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border\">\n {/* Time Range */}\n <div className=\"inline-flex rounded-lg border border-border p-1 bg-muted/50\">\n {[7, 30, 90].map((d) => (\n <button\n type=\"button\"\n key={d}\n onClick={() => {\n setDays(d);\n setPage(1); // Reset to first page on filter change\n }}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n days === d\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {d}d\n </button>\n ))}\n </div>\n\n {/* Transaction Type Filter */}\n <div className=\"inline-flex rounded-lg border border-border p-1 bg-muted/50\">\n {(['all', 'purchase', 'usage', 'adjustment'] as FilterType[]).map((type) => (\n <button\n type=\"button\"\n key={type}\n onClick={() => {\n setFilterType(type);\n setPage(1); // Reset to first page on filter change\n }}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors capitalize ${\n filterType === type\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {type === 'all' ? 'All' : formatCreditTransactionType(type as CreditTransactionType)}\n </button>\n ))}\n </div>\n\n {/* Transaction count */}\n <div className=\"ml-auto text-sm text-muted-foreground\">\n {pagination.totalCount} transaction{pagination.totalCount !== 1 ? 's' : ''} in last {days}{' '}\n days\n </div>\n </div>\n\n {/* Transactions List */}\n {filteredTransactions.length === 0 ? (\n <EmptyState\n icon={<Coins className=\"size-6 text-muted-foreground\" />}\n title=\"No credit transactions found\"\n description={\n filterType !== 'all'\n ? 'No transactions match your filter.'\n : 'Your credit transaction history will appear here.'\n }\n />\n ) : (\n <div className=\"space-y-3\">\n {filteredTransactions.map((transaction) => (\n <CreditTransactionRow key={transaction.id} transaction={transaction} />\n ))}\n </div>\n )}\n\n {/* Pagination Controls */}\n {pagination.totalCount > 0 && (\n <div className=\"flex items-center justify-between border-t border-border pt-4\">\n <p className=\"text-sm text-muted-foreground\">\n Showing {(page - 1) * pageSize + 1} to{' '}\n {Math.min(page * pageSize, pagination.totalCount)} of {pagination.totalCount}{' '}\n transactions\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={goToPreviousPage}\n disabled={page <= 1}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n <ChevronLeft className=\"size-4\" />\n Previous\n </button>\n <span className=\"text-sm text-muted-foreground px-2\">\n Page {page} of {totalPages || 1}\n </span>\n <button\n type=\"button\"\n onClick={goToNextPage}\n disabled={!pagination.hasMore}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n Next\n <ChevronRight className=\"size-4\" />\n </button>\n </div>\n </div>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Credit Transaction Row Component\n// ============================================================================\n\ninterface CreditTransactionRowProps {\n transaction: CreditTransaction;\n}\n\nconst CreditTransactionRow: FC<CreditTransactionRowProps> = ({ transaction }) => {\n const statusColor = getCreditTransactionStatusColor(transaction.status);\n\n // FIX: Check the actual amount value, not just the transaction type\n // Negative amounts are debits (show red with \"-\"), positive amounts are credits (show green with \"+\")\n const amount = Number(transaction.amount);\n const isCredit = amount > 0;\n const absAmount = Math.abs(amount);\n\n const getTransactionIcon = () => {\n // For adjustments, show different icon based on amount sign\n if (transaction.type === 'adjustment') {\n return isCredit ? (\n <Plus className=\"size-4 text-status-success-text\" />\n ) : (\n <Minus className=\"size-4 text-status-error-text\" />\n );\n }\n\n switch (transaction.type) {\n case 'purchase':\n return <Plus className=\"size-4 text-status-success-text\" />;\n case 'usage':\n return <Minus className=\"size-4 text-status-warning-text\" />;\n default:\n return <Coins className=\"size-4 text-muted-foreground\" />;\n }\n };\n\n const getIconBackground = () => {\n // For adjustments, show different background based on amount sign\n if (transaction.type === 'adjustment') {\n return isCredit ? 'bg-status-success-bg-subtle' : 'bg-status-error-bg-subtle';\n }\n\n switch (transaction.type) {\n case 'purchase':\n return 'bg-status-success-bg-subtle';\n case 'usage':\n return 'bg-status-warning-bg-subtle';\n default:\n return 'bg-muted';\n }\n };\n\n return (\n <div className=\"flex items-center gap-4 p-4 border border-border rounded-lg bg-card hover:shadow-sm transition-shadow\">\n {/* Icon */}\n <div\n className={`size-10 rounded-full flex items-center justify-center flex-shrink-0 ${getIconBackground()}`}\n >\n {getTransactionIcon()}\n </div>\n\n {/* Info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <p className=\"font-medium text-foreground capitalize\">\n {formatCreditTransactionType(transaction.type)}\n </p>\n <span\n className={`px-2 py-0.5 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {transaction.status}\n </span>\n {transaction.entityType && (\n <span className=\"px-2 py-0.5 text-xs font-medium rounded bg-muted text-muted-foreground capitalize\">\n {transaction.entityType}\n </span>\n )}\n </div>\n <p className=\"text-sm text-muted-foreground mt-0.5\">\n {formatRelativeTime(transaction.createdAt)} • {formatDateTime(transaction.createdAt)}\n </p>\n {transaction.productId && transaction.productId !== 'default' && (\n <p className=\"text-xs text-muted-foreground mt-1\">Product: {transaction.productId}</p>\n )}\n </div>\n\n {/* Amount */}\n <div className=\"text-right flex-shrink-0\">\n <p\n className={`font-semibold text-lg ${isCredit ? 'text-status-success-text' : 'text-status-error-text'}`}\n >\n {isCredit ? '+' : '-'}\n {absAmount.toLocaleString()} credits\n </p>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -211,9 +211,9 @@ var h = () => {
211
211
  className: "text-center space-y-2",
212
212
  children: [
213
213
  /* @__PURE__ */ f("div", {
214
- className: "w-12 h-12 mx-auto rounded-full bg-muted flex items-center justify-center",
214
+ className: "size-12 mx-auto rounded-full bg-muted flex items-center justify-center",
215
215
  children: /* @__PURE__ */ f("svg", {
216
- className: "w-6 h-6 text-muted-foreground",
216
+ className: "size-6 text-muted-foreground",
217
217
  fill: "none",
218
218
  viewBox: "0 0 24 24",
219
219
  stroke: "currentColor",
@@ -316,7 +316,7 @@ var h = () => {
316
316
  className: "flex items-center gap-3 p-4 rounded-lg bg-status-success-bg-subtle border border-status-success-border text-status-success-text",
317
317
  children: [
318
318
  /* @__PURE__ */ f("svg", {
319
- className: "w-5 h-5 shrink-0",
319
+ className: "size-5 shrink-0",
320
320
  fill: "none",
321
321
  viewBox: "0 0 24 24",
322
322
  stroke: "currentColor",
@@ -335,10 +335,11 @@ var h = () => {
335
335
  children: "Our team will review and process your request."
336
336
  })] }),
337
337
  /* @__PURE__ */ f("button", {
338
+ type: "button",
338
339
  onClick: () => P(!1),
339
340
  className: "ml-auto text-current opacity-60 hover:opacity-100",
340
341
  children: /* @__PURE__ */ f("svg", {
341
- className: "w-4 h-4",
342
+ className: "size-4",
342
343
  fill: "none",
343
344
  viewBox: "0 0 24 24",
344
345
  stroke: "currentColor",
@@ -363,6 +364,7 @@ var h = () => {
363
364
  className: "text-base font-semibold text-foreground",
364
365
  children: "Payout Account"
365
366
  }), /* @__PURE__ */ f("button", {
367
+ type: "button",
366
368
  onClick: () => I((e) => !e),
367
369
  className: "text-sm text-primary hover:underline",
368
370
  children: F ? "Cancel" : "+ Add new"
@@ -489,6 +491,7 @@ var h = () => {
489
491
  children: V
490
492
  }),
491
493
  /* @__PURE__ */ f("button", {
494
+ type: "button",
492
495
  onClick: () => void ye(),
493
496
  disabled: z || !L.label || !L.accountHolderName,
494
497
  className: "w-full py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
@@ -503,7 +506,7 @@ var h = () => {
503
506
  className: "flex flex-col items-center justify-center py-8 border border-dashed border-border rounded-lg",
504
507
  children: [
505
508
  /* @__PURE__ */ f("svg", {
506
- className: "w-8 h-8 text-muted-foreground mb-2",
509
+ className: "size-8 text-muted-foreground mb-2",
507
510
  fill: "none",
508
511
  viewBox: "0 0 24 24",
509
512
  stroke: "currentColor",
@@ -519,6 +522,7 @@ var h = () => {
519
522
  children: "No payout accounts yet"
520
523
  }),
521
524
  /* @__PURE__ */ f("button", {
525
+ type: "button",
522
526
  onClick: () => I(!0),
523
527
  className: "mt-2 text-sm text-primary hover:underline",
524
528
  children: "Add a bank account or UPI"
@@ -527,6 +531,7 @@ var h = () => {
527
531
  }) : /* @__PURE__ */ f("div", {
528
532
  className: "space-y-2",
529
533
  children: $.map((e) => /* @__PURE__ */ f("button", {
534
+ type: "button",
530
535
  onClick: () => T(e.id),
531
536
  className: `w-full text-left p-4 border rounded-lg transition-colors ${w === e.id ? "border-primary bg-primary/5" : "border-border bg-card hover:border-primary/40"}`,
532
537
  children: /* @__PURE__ */ p("div", {
@@ -554,7 +559,7 @@ var h = () => {
554
559
  children: e.status.replaceAll("_", " ").toLowerCase()
555
560
  }),
556
561
  w === e.id && /* @__PURE__ */ f("svg", {
557
- className: "w-4 h-4 text-primary shrink-0",
562
+ className: "size-4 text-primary shrink-0",
558
563
  fill: "none",
559
564
  viewBox: "0 0 24 24",
560
565
  stroke: "currentColor",
@@ -658,6 +663,7 @@ var h = () => {
658
663
  " at the current credit rate. Withdrawals are reviewed before processing and may take 3–5 business days."
659
664
  ]
660
665
  }), /* @__PURE__ */ f("button", {
666
+ type: "button",
661
667
  onClick: () => void be(),
662
668
  disabled: A || !Se || !w || $.length === 0,
663
669
  className: "w-full py-2.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
@@ -676,11 +682,12 @@ var h = () => {
676
682
  className: "text-base font-semibold text-foreground",
677
683
  children: "Withdrawal History"
678
684
  }), /* @__PURE__ */ f("button", {
685
+ type: "button",
679
686
  onClick: () => void q(),
680
687
  className: "text-sm text-muted-foreground hover:text-foreground transition-colors",
681
688
  "aria-label": "Refresh withdrawal history",
682
689
  children: /* @__PURE__ */ f("svg", {
683
- className: "w-4 h-4",
690
+ className: "size-4",
684
691
  fill: "none",
685
692
  viewBox: "0 0 24 24",
686
693
  stroke: "currentColor",
@@ -702,7 +709,7 @@ var h = () => {
702
709
  }) : S.length === 0 ? /* @__PURE__ */ p("div", {
703
710
  className: "flex flex-col items-center justify-center py-10 border border-dashed border-border rounded-lg",
704
711
  children: [/* @__PURE__ */ f("svg", {
705
- className: "w-8 h-8 text-muted-foreground mb-2",
712
+ className: "size-8 text-muted-foreground mb-2",
706
713
  fill: "none",
707
714
  viewBox: "0 0 24 24",
708
715
  stroke: "currentColor",