@burdenoff/microfe-billing 2026.821.1 → 2026.825.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (145) hide show
  1. package/dist/billing/BillingAdminRoutes.js.map +1 -1
  2. package/dist/billing/BillingRoot.js.map +1 -1
  3. package/dist/billing/BillingUserRoutes.js.map +1 -1
  4. package/dist/billing/hooks/useBillingAccountSelection.js.map +1 -1
  5. package/dist/billing/hooks/useBillingEventEmitter.js.map +1 -1
  6. package/dist/billing/hooks/useBillingNavigate.js.map +1 -1
  7. package/dist/billing/hooks/useBillingPermissions.js.map +1 -1
  8. package/dist/billing/hooks/useDefaultBillingCurrency.js.map +1 -1
  9. package/dist/billing/modules/addons/components/FloatingCartDrawer.js.map +1 -1
  10. package/dist/billing/modules/addons/hooks/useAddons.js.map +1 -1
  11. package/dist/billing/modules/addons/pages/AddonBrowseDetailPage.js.map +1 -1
  12. package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
  13. package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
  14. package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
  15. package/dist/billing/modules/addons/pages/AddonsBrowsePage.js.map +1 -1
  16. package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
  17. package/dist/billing/modules/addons/store/addonCartStore.js.map +1 -1
  18. package/dist/billing/modules/affiliate/api.js.map +1 -1
  19. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +107 -107
  20. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
  21. package/dist/billing/modules/analytics/pages/AnalyticsPage.js +80 -81
  22. package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
  23. package/dist/billing/modules/billing/hooks/useInvoices.js.map +1 -1
  24. package/dist/billing/modules/billing/hooks/useRefunds.js.map +1 -1
  25. package/dist/billing/modules/billing/hooks/useTransactions.js.map +1 -1
  26. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  27. package/dist/billing/modules/billing/pages/InvoicesListPage.js +1 -1
  28. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  29. package/dist/billing/modules/billing/pages/PaymentMethodsPage.js.map +1 -1
  30. package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
  31. package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
  32. package/dist/billing/modules/billing/pages/UserRefundsPage.js +127 -127
  33. package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
  34. package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
  35. package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
  36. package/dist/billing/modules/checkout/components/CreditsAmountInput.js.map +1 -1
  37. package/dist/billing/modules/checkout/components/NativeCheckoutView.js.map +1 -1
  38. package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
  39. package/dist/billing/modules/checkout/components/PromoCodeInput.js.map +1 -1
  40. package/dist/billing/modules/checkout/components/RazorpayPaymentButton.js.map +1 -1
  41. package/dist/billing/modules/checkout/components/SimpleAutoPayToggle.js.map +1 -1
  42. package/dist/billing/modules/checkout/components/StoreShippingSection.js.map +1 -1
  43. package/dist/billing/modules/checkout/components/StripePaymentForm.js.map +1 -1
  44. package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
  45. package/dist/billing/modules/checkout/components/UnifiedPaymentMethodSelector.js.map +1 -1
  46. package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
  47. package/dist/billing/modules/checkout/hooks/useCheckout.js +0 -1
  48. package/dist/billing/modules/checkout/hooks/useCheckout.js.map +1 -1
  49. package/dist/billing/modules/checkout/hooks/useNativeCheckout.js.map +1 -1
  50. package/dist/billing/modules/checkout/hooks/usePaymentProviders.js.map +1 -1
  51. package/dist/billing/modules/checkout/hooks/usePaymentStatusPolling.js.map +1 -1
  52. package/dist/billing/modules/checkout/hooks/useStoreShipping.js.map +1 -1
  53. package/dist/billing/modules/checkout/pages/CheckoutPage.js +412 -395
  54. package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
  55. package/dist/billing/modules/checkout/utils/storeOrder.js.map +1 -1
  56. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  57. package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
  58. package/dist/billing/modules/coupons/pages/CreateCouponPage.js.map +1 -1
  59. package/dist/billing/modules/coupons/pages/EditCouponPage.js.map +1 -1
  60. package/dist/billing/modules/credits/hooks/useCredits.js.map +1 -1
  61. package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
  62. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
  63. package/dist/billing/modules/credits/pages/CreditWithdrawalPage.js.map +1 -1
  64. package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
  65. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
  66. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
  67. package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
  68. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
  69. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
  70. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
  71. package/dist/billing/modules/dashboard/hooks/useBillingOverviewData.js.map +1 -1
  72. package/dist/billing/modules/dashboard/hooks/useDashboard.js.map +1 -1
  73. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  74. package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
  75. package/dist/billing/modules/earnings/api.js.map +1 -1
  76. package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js +145 -145
  77. package/dist/billing/modules/earnings/pages/DeveloperRevenueAdminPage.js.map +1 -1
  78. package/dist/billing/modules/earnings/pages/EarningsPage.js +133 -133
  79. package/dist/billing/modules/earnings/pages/EarningsPage.js.map +1 -1
  80. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +7 -9
  81. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  82. package/dist/billing/modules/earnings/pages/PayoutsPage.js +95 -95
  83. package/dist/billing/modules/earnings/pages/PayoutsPage.js.map +1 -1
  84. package/dist/billing/modules/plans/constants/featuredQuotas.js.map +1 -1
  85. package/dist/billing/modules/plans/hooks/usePlans.js.map +1 -1
  86. package/dist/billing/modules/plans/hooks/useQuotas.js.map +1 -1
  87. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  88. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  89. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  90. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  91. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  92. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  93. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  94. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  95. package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
  96. package/dist/billing/modules/quota/hooks/useQuotaTemplates.js.map +1 -1
  97. package/dist/billing/modules/quota/pages/QuotaAssignFreePage.js.map +1 -1
  98. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
  99. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
  100. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
  101. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
  102. package/dist/billing/modules/settings/pages/SettingsPage.js.map +1 -1
  103. package/dist/billing/modules/settings/pages/TeamPermissionsPage.js.map +1 -1
  104. package/dist/billing/modules/store/pages/StoreCheckoutPage.js +5 -6
  105. package/dist/billing/modules/store/pages/StoreCheckoutPage.js.map +1 -1
  106. package/dist/billing/modules/subscriptions/components/ManageSeatsModal.js.map +1 -1
  107. package/dist/billing/modules/subscriptions/hooks/useSubscriptions.js.map +1 -1
  108. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
  109. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
  110. package/dist/billing/modules/subscriptions/pages/UpgradeSubscriptionPage.js.map +1 -1
  111. package/dist/billing/modules/usage/hooks/useUsage.js +14 -9
  112. package/dist/billing/modules/usage/hooks/useUsage.js.map +1 -1
  113. package/dist/billing/modules/usage/pages/UsagePage.js +481 -453
  114. package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
  115. package/dist/billing/providers/BillingProvider.js.map +1 -1
  116. package/dist/billing/shared/components/AccessDenied.js.map +1 -1
  117. package/dist/billing/shared/components/ActorIdentity.js.map +1 -1
  118. package/dist/billing/shared/components/CurrencyPriceOverridesEditor.js.map +1 -1
  119. package/dist/billing/shared/components/EmptyState.js.map +1 -1
  120. package/dist/billing/shared/components/PageHeader.js.map +1 -1
  121. package/dist/billing/shared/components/ServerError.js.map +1 -1
  122. package/dist/billing/shared/components/StatCard.js.map +1 -1
  123. package/dist/billing/shared/hooks/useActorProfiles.js.map +1 -1
  124. package/dist/billing/shared/types/graphql.js.map +1 -1
  125. package/dist/billing/shared/ui/Card.js.map +1 -1
  126. package/dist/billing/shared/ui/MetricCard.js.map +1 -1
  127. package/dist/billing/shared/ui/ProgressBar.js.map +1 -1
  128. package/dist/billing/shared/ui/StatusPill.js.map +1 -1
  129. package/dist/billing/shared/utils/directBillingGraphql.js.map +1 -1
  130. package/dist/billing/shared/utils/error.js.map +1 -1
  131. package/dist/billing/shared/utils/format.js.map +1 -1
  132. package/dist/billing/shared/utils/gatewayGraphql.js.map +1 -1
  133. package/dist/billing/shared/utils/navigation.js.map +1 -1
  134. package/dist/billing/shared/utils/paymentTheme.js.map +1 -1
  135. package/dist/billing/shared/utils/planFeatureQuantity.js.map +1 -1
  136. package/dist/billing/shared/utils/status.js.map +1 -1
  137. package/dist/billing/shared/utils/tokens.js.map +1 -1
  138. package/dist/generated/global-operations.js +5 -1
  139. package/dist/generated/global-operations.js.map +1 -1
  140. package/dist/generated/global-types.js.map +1 -1
  141. package/dist/generated/wspace-operations.js +1 -1
  142. package/dist/generated/wspace-operations.js.map +1 -1
  143. package/dist/node_modules/@capacitor/core/dist/index.js +1 -1
  144. package/dist/node_modules/@capacitor/core/dist/index.js.map +1 -1
  145. package/package.json +13 -10
@@ -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 { PagePurpose } from '@burdenoff/fe-libs/ui';\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-subtle text-text-primary hover:bg-bg-sunken 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-text-muted\">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-subtle text-text-primary hover:bg-bg-sunken 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-status-error-text\">\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-subtle text-text-primary hover:bg-bg-sunken flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back to Coupons\n </button>\n }\n />\n\n <PagePurpose className=\"mt-2\">\n Update an existing coupon&apos;s discount, limits, expiry or applicable products. The code\n itself and its usage history are read-only; deactivate the coupon if you want to stop it\n being redeemed.\n </PagePurpose>\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-bg-sunken/30 border border-border-subtle rounded-lg p-6\">\n <h3 className=\"text-lg font-semibold text-text-primary 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-text-muted\">Code:</span>\n <span className=\"ml-2 font-mono font-medium text-text-primary\">{coupon.code}</span>\n </div>\n <div>\n <span className=\"text-text-muted\">Type:</span>\n <span className=\"ml-2 text-text-primary capitalize\">\n {coupon.type.replace('_', ' ').toLowerCase()}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Value:</span>\n <span className=\"ml-2 text-text-primary\">\n {coupon.type === 'PERCENTAGE' ? `${coupon.value}%` : `$${coupon.value}`}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Scope:</span>\n <span className=\"ml-2 text-text-primary\">{coupon.scope}</span>\n </div>\n <div>\n <span className=\"text-text-muted\">Current Uses:</span>\n <span className=\"ml-2 text-text-primary\">\n {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Created:</span>\n <span className=\"ml-2 text-text-primary\">\n {new Date(coupon.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n </div>\n\n {/* Editable Fields Card */}\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-6 space-y-6\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Editable Fields</h3>\n\n {/* Name */}\n <div className=\"space-y-2\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary\">\n Name <span className=\"text-status-error-text\">*</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-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {errors.name && <p className=\"text-sm text-status-error-text\">{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-text-primary\">\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-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] 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-text-primary\">\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-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\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-text-primary\">\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-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {errors.maxTotalUses && (\n <p className=\"text-sm text-status-error-text\">{errors.maxTotalUses}</p>\n )}\n <p className=\"text-sm text-text-muted\">\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\n htmlFor=\"maxUsesPerUser\"\n className=\"block text-sm font-medium text-text-primary\"\n >\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-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {errors.maxUsesPerUser && (\n <p className=\"text-sm text-status-error-text\">{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-text-primary\">\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-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-sm text-text-muted\">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-status-error-bg-subtle border border-status-error-border\">\n <AlertCircle className=\"size-5 text-status-error-text mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-error-text\">Error</p>\n <p className=\"text-sm text-status-error-text/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-subtle\">\n <button\n type=\"submit\"\n disabled={updating || submitSuccess}\n className=\"px-6 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover 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-subtle text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
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 { PagePurpose } from '@burdenoff/fe-libs/ui';\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-subtle text-text-primary hover:bg-bg-sunken 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-text-muted\">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-subtle text-text-primary hover:bg-bg-sunken 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-status-error-text\">\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-subtle text-text-primary hover:bg-bg-sunken flex items-center gap-2\"\n >\n <ArrowLeft className=\"size-4\" />\n Back to Coupons\n </button>\n }\n />\n\n <PagePurpose className=\"mt-2\">\n Update an existing coupon&apos;s discount, limits, expiry or applicable products. The code\n itself and its usage history are read-only; deactivate the coupon if you want to stop it\n being redeemed.\n </PagePurpose>\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-bg-sunken/30 border border-border-subtle rounded-lg p-6\">\n <h3 className=\"text-lg font-semibold text-text-primary 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-text-muted\">Code:</span>\n <span className=\"ml-2 font-mono font-medium text-text-primary\">{coupon.code}</span>\n </div>\n <div>\n <span className=\"text-text-muted\">Type:</span>\n <span className=\"ml-2 text-text-primary capitalize\">\n {coupon.type.replace('_', ' ').toLowerCase()}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Value:</span>\n <span className=\"ml-2 text-text-primary\">\n {coupon.type === 'PERCENTAGE' ? `${coupon.value}%` : `$${coupon.value}`}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Scope:</span>\n <span className=\"ml-2 text-text-primary\">{coupon.scope}</span>\n </div>\n <div>\n <span className=\"text-text-muted\">Current Uses:</span>\n <span className=\"ml-2 text-text-primary\">\n {coupon.currentUses}\n {coupon.maxTotalUses ? ` / ${coupon.maxTotalUses}` : ''}\n </span>\n </div>\n <div>\n <span className=\"text-text-muted\">Created:</span>\n <span className=\"ml-2 text-text-primary\">\n {new Date(coupon.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n </div>\n\n {/* Editable Fields Card */}\n <div className=\"bg-bg-surface border border-border-subtle rounded-lg p-6 space-y-6\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Editable Fields</h3>\n\n {/* Name */}\n <div className=\"space-y-2\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium text-text-primary\">\n Name <span className=\"text-status-error-text\">*</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-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {errors.name && <p className=\"text-sm text-status-error-text\">{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-text-primary\">\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-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] 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-text-primary\">\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-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\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-text-primary\">\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-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {errors.maxTotalUses && (\n <p className=\"text-sm text-status-error-text\">{errors.maxTotalUses}</p>\n )}\n <p className=\"text-sm text-text-muted\">\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\n htmlFor=\"maxUsesPerUser\"\n className=\"block text-sm font-medium text-text-primary\"\n >\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-input border border-border-subtle bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n {errors.maxUsesPerUser && (\n <p className=\"text-sm text-status-error-text\">{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-text-primary\">\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-input border border-border-subtle bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-sm text-text-muted\">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-status-error-bg-subtle border border-status-error-border\">\n <AlertCircle className=\"size-5 text-status-error-text mt-0.5\" />\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium text-status-error-text\">Error</p>\n <p className=\"text-sm text-status-error-text/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-subtle\">\n <button\n type=\"submit\"\n disabled={updating || submitSuccess}\n className=\"px-6 py-2 rounded-lg bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bgHover 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-subtle text-text-primary hover:bg-bg-sunken\"\n >\n Cancel\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"useCredits.js","names":[],"sources":["../../../../../src/billing/modules/credits/hooks/useCredits.ts"],"sourcesContent":["/**\n * Credits Module - useCredits Hook\n * Provides access to credit transactions and mutations\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback } from 'react';\nimport {\n useGetBillingAccountCreditTransactionsQuery,\n useGetCreditTransactionDetailsQuery,\n useGetCostOfCreditsQuery,\n useGetCreditNotesQuery,\n useCreateCreditNoteMutation,\n GetCreditNotesDocument,\n GrantCreditsDocument,\n} from '../../../../generated/global-operations';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { directBillingGraphqlRequest } from '../../../shared/utils';\nimport type {\n CreditTransaction,\n CreditNote,\n PaginatedCreditTransactions,\n} from '../../../shared/types';\n\ninterface UseCreditTransactionsOptions {\n billingAccountId: string;\n days?: number;\n page?: number;\n pageSize?: number;\n}\n\ninterface UseCreditTransactionsResult {\n transactions: CreditTransaction[];\n pagination: {\n page: number;\n pageSize: number;\n totalCount: number;\n hasMore: boolean;\n };\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseCreditTransactionResult {\n transaction: CreditTransaction | null;\n isLoading: boolean;\n error: Error | undefined;\n}\n\ninterface UseCostOfCreditsResult {\n cost: number | null;\n isLoading: boolean;\n error: Error | undefined;\n}\n\ninterface UseCreditNotesResult {\n creditNotes: CreditNote[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\n/**\n * Hook for fetching credit transactions\n */\nexport function useCreditTransactions(\n options: UseCreditTransactionsOptions\n): UseCreditTransactionsResult {\n const { billingAccountId, days = 30, page = 1, pageSize = 20 } = options;\n\n const { data, loading, error, refetch } = useGetBillingAccountCreditTransactionsQuery({\n variables: { billingAccountID: billingAccountId, days, page, pageSize },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const result: PaginatedCreditTransactions = data?.getBillingAccountCreditTransactions || {\n creditTransactions: [],\n page: 1,\n pageSize: 20,\n totalCount: 0,\n hasMore: false,\n };\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n transactions: result.creditTransactions,\n pagination: {\n page: result.page,\n pageSize: result.pageSize,\n totalCount: result.totalCount,\n hasMore: result.hasMore,\n },\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching a single credit transaction\n */\nexport function useCreditTransaction(\n creditTransactionId: string | undefined\n): UseCreditTransactionResult {\n const { data, loading, error } = useGetCreditTransactionDetailsQuery({\n variables: { creditTransactionID: creditTransactionId! },\n skip: !creditTransactionId,\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n transaction: data?.getCreditTransactionDetails || null,\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for getting cost of credits\n */\nexport function useCostOfCredits(creditAmount: number, currency: string): UseCostOfCreditsResult {\n const { data, loading, error } = useGetCostOfCreditsQuery({\n variables: { creditAmount, currency },\n skip: creditAmount <= 0 || !currency,\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n cost: data?.getCostOfCredits?.amount || null,\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for fetching credit notes\n */\nexport function useCreditNotes(): UseCreditNotesResult {\n const { data, loading, error, refetch } = useGetCreditNotesQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n creditNotes: data?.getCreditNotes || [],\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for credit mutations\n */\nexport function useCreditMutations() {\n const client = useApolloClient();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const { emit } = useBillingEventEmitter();\n\n const [createCreditNoteMutation, { loading: isCreating }] = useCreateCreditNoteMutation();\n\n const createCreditNote = useCallback(\n async (params: {\n billingAccountId: string;\n amount: number;\n reason?: string;\n invoiceId?: string;\n currency?: string;\n metadata?: Record<string, unknown>;\n }): Promise<CreditNote> => {\n try {\n const { data } = await createCreditNoteMutation({\n variables: params,\n });\n await client.refetchQueries({ include: [GetCreditNotesDocument] });\n return data!.createCreditNote;\n } catch (error) {\n console.error('Failed to create credit note:', error);\n throw error;\n }\n },\n [createCreditNoteMutation, client]\n );\n\n const grantCredits = useCallback(\n async (params: {\n billingAccountId: string;\n amount: number;\n productId?: string;\n reason?: string;\n metadata?: Record<string, unknown>;\n }): Promise<CreditTransaction> => {\n const result = await directBillingGraphqlRequest<\n {\n grantCredits: CreditTransaction;\n },\n { input: typeof params }\n >({\n apiGatewayUrl,\n authToken,\n orgId,\n query: GrantCreditsDocument,\n variables: { input: params },\n });\n\n await client.refetchQueries({ include: ['GetBillingAccountCreditTransactions'] });\n emit('billing.credits.granted', {\n entityId: result.grantCredits.id,\n source: 'credits-admin',\n amount: params.amount,\n });\n return result.grantCredits;\n },\n [apiGatewayUrl, authToken, client, emit, orgId]\n );\n\n return {\n createCreditNote,\n grantCredits,\n isCreating,\n };\n}\n"],"mappings":";;;;;;;;AAmEA,SAAgB,EACd,GAC6B;CAC7B,IAAM,EAAE,qBAAkB,UAAO,IAAI,UAAO,GAAG,cAAW,OAAO,GAE3D,EAAE,SAAM,YAAS,UAAO,eAAY,EAA4C;EACpF,WAAW;GAAE,kBAAkB;GAAkB;GAAM;GAAM;GAAU;EACvE,MAAM,CAAC;EACP,aAAa;EACd,CAAC,EAEI,IAAsC,GAAM,uCAAuC;EACvF,oBAAoB,EAAE;EACtB,MAAM;EACN,UAAU;EACV,YAAY;EACZ,SAAS;EACV,EAEK,IAAgB,EAAY,YAAY;AAC5C,QAAM,GAAS;IACd,CAAC,EAAQ,CAAC;AAEb,QAAO;EACL,cAAc,EAAO;EACrB,YAAY;GACV,MAAM,EAAO;GACb,UAAU,EAAO;GACjB,YAAY,EAAO;GACnB,SAAS,EAAO;GACjB;EACD,WAAW;EACJ;EACP,SAAS;EACV;;AA8DH,SAAgB,IAAqB;CACnC,IAAM,IAAS,GAAiB,EAC1B,EAAE,kBAAe,cAAW,aAAU,GAAY,EAClD,EAAE,YAAS,GAAwB,EAEnC,CAAC,GAA0B,EAAE,SAAS,OAAgB,GAA6B;AAyDzF,QAAO;EACL,kBAxDuB,EACvB,OAAO,MAOoB;AACzB,OAAI;IACF,IAAM,EAAE,YAAS,MAAM,EAAyB,EAC9C,WAAW,GACZ,CAAC;AAEF,WADA,MAAM,EAAO,eAAe,EAAE,SAAS,CAAC,EAAuB,EAAE,CAAC,EAC3D,EAAM;YACN,GAAO;AAEd,UADA,QAAQ,MAAM,iCAAiC,EAAM,EAC/C;;KAGV,CAAC,GAA0B,EAAO,CACnC;EAoCC,cAlCmB,EACnB,OAAO,MAM2B;GAChC,IAAM,IAAS,MAAM,EAKnB;IACA;IACA;IACA;IACA,OAAO;IACP,WAAW,EAAE,OAAO,GAAQ;IAC7B,CAAC;AAQF,UANA,MAAM,EAAO,eAAe,EAAE,SAAS,CAAC,sCAAsC,EAAE,CAAC,EACjF,EAAK,2BAA2B;IAC9B,UAAU,EAAO,aAAa;IAC9B,QAAQ;IACR,QAAQ,EAAO;IAChB,CAAC,EACK,EAAO;KAEhB;GAAC;GAAe;GAAW;GAAQ;GAAM;GAAM,CAChD;EAKC;EACD"}
1
+ {"version":3,"file":"useCredits.js","names":[],"sources":["../../../../../src/billing/modules/credits/hooks/useCredits.ts"],"sourcesContent":["/**\n * Credits Module - useCredits Hook\n * Provides access to credit transactions and mutations\n */\n\nimport { useApolloClient } from '@apollo/client/react';\nimport { useCallback } from 'react';\nimport {\n useGetBillingAccountCreditTransactionsQuery,\n useGetCreditTransactionDetailsQuery,\n useGetCostOfCreditsQuery,\n useGetCreditNotesQuery,\n useCreateCreditNoteMutation,\n GetCreditNotesDocument,\n GrantCreditsDocument,\n} from '../../../../generated/global-operations';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { directBillingGraphqlRequest } from '../../../shared/utils';\nimport type {\n CreditTransaction,\n CreditNote,\n PaginatedCreditTransactions,\n} from '../../../shared/types';\n\ninterface UseCreditTransactionsOptions {\n billingAccountId: string;\n days?: number;\n page?: number;\n pageSize?: number;\n}\n\ninterface UseCreditTransactionsResult {\n transactions: CreditTransaction[];\n pagination: {\n page: number;\n pageSize: number;\n totalCount: number;\n hasMore: boolean;\n };\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\ninterface UseCreditTransactionResult {\n transaction: CreditTransaction | null;\n isLoading: boolean;\n error: Error | undefined;\n}\n\ninterface UseCostOfCreditsResult {\n cost: number | null;\n isLoading: boolean;\n error: Error | undefined;\n}\n\ninterface UseCreditNotesResult {\n creditNotes: CreditNote[];\n isLoading: boolean;\n error: Error | undefined;\n refetch: () => Promise<void>;\n}\n\n/**\n * Hook for fetching credit transactions\n */\nexport function useCreditTransactions(\n options: UseCreditTransactionsOptions\n): UseCreditTransactionsResult {\n const { billingAccountId, days = 30, page = 1, pageSize = 20 } = options;\n\n const { data, loading, error, refetch } = useGetBillingAccountCreditTransactionsQuery({\n variables: { billingAccountID: billingAccountId, days, page, pageSize },\n skip: !billingAccountId,\n fetchPolicy: 'cache-and-network',\n });\n\n const result: PaginatedCreditTransactions = data?.getBillingAccountCreditTransactions || {\n creditTransactions: [],\n page: 1,\n pageSize: 20,\n totalCount: 0,\n hasMore: false,\n };\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n transactions: result.creditTransactions,\n pagination: {\n page: result.page,\n pageSize: result.pageSize,\n totalCount: result.totalCount,\n hasMore: result.hasMore,\n },\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for fetching a single credit transaction\n */\nexport function useCreditTransaction(\n creditTransactionId: string | undefined\n): UseCreditTransactionResult {\n const { data, loading, error } = useGetCreditTransactionDetailsQuery({\n variables: { creditTransactionID: creditTransactionId! },\n skip: !creditTransactionId,\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n transaction: data?.getCreditTransactionDetails || null,\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for getting cost of credits\n */\nexport function useCostOfCredits(creditAmount: number, currency: string): UseCostOfCreditsResult {\n const { data, loading, error } = useGetCostOfCreditsQuery({\n variables: { creditAmount, currency },\n skip: creditAmount <= 0 || !currency,\n fetchPolicy: 'cache-and-network',\n });\n\n return {\n cost: data?.getCostOfCredits?.amount || null,\n isLoading: loading,\n error: error as Error | undefined,\n };\n}\n\n/**\n * Hook for fetching credit notes\n */\nexport function useCreditNotes(): UseCreditNotesResult {\n const { data, loading, error, refetch } = useGetCreditNotesQuery({\n fetchPolicy: 'cache-and-network',\n });\n\n const handleRefetch = useCallback(async () => {\n await refetch();\n }, [refetch]);\n\n return {\n creditNotes: data?.getCreditNotes || [],\n isLoading: loading,\n error: error as Error | undefined,\n refetch: handleRefetch,\n };\n}\n\n/**\n * Hook for credit mutations\n */\nexport function useCreditMutations() {\n const client = useApolloClient();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n const { emit } = useBillingEventEmitter();\n\n const [createCreditNoteMutation, { loading: isCreating }] = useCreateCreditNoteMutation();\n\n const createCreditNote = useCallback(\n async (params: {\n billingAccountId: string;\n amount: number;\n reason?: string;\n invoiceId?: string;\n currency?: string;\n metadata?: Record<string, unknown>;\n }): Promise<CreditNote> => {\n try {\n const { data } = await createCreditNoteMutation({\n variables: params,\n });\n await client.refetchQueries({ include: [GetCreditNotesDocument] });\n return data!.createCreditNote;\n } catch (error) {\n console.error('Failed to create credit note:', error);\n throw error;\n }\n },\n [createCreditNoteMutation, client]\n );\n\n const grantCredits = useCallback(\n async (params: {\n billingAccountId: string;\n amount: number;\n productId?: string;\n reason?: string;\n metadata?: Record<string, unknown>;\n }): Promise<CreditTransaction> => {\n const result = await directBillingGraphqlRequest<\n {\n grantCredits: CreditTransaction;\n },\n { input: typeof params }\n >({\n apiGatewayUrl,\n authToken,\n orgId,\n query: GrantCreditsDocument,\n variables: { input: params },\n });\n\n await client.refetchQueries({ include: ['GetBillingAccountCreditTransactions'] });\n emit('billing.credits.granted', {\n entityId: result.grantCredits.id,\n source: 'credits-admin',\n amount: params.amount,\n });\n return result.grantCredits;\n },\n [apiGatewayUrl, authToken, client, emit, orgId]\n );\n\n return {\n createCreditNote,\n grantCredits,\n isCreating,\n };\n}\n"],"mappings":";;;;;;;;AAmEA,SAAgB,EACd,GAC6B;CAC7B,IAAM,EAAE,qBAAkB,UAAO,IAAI,UAAO,GAAG,cAAW,OAAO,GAE3D,EAAE,SAAM,YAAS,UAAO,eAAY,EAA4C;EACpF,WAAW;GAAE,kBAAkB;GAAkB;GAAM;GAAM;EAAS;EACtE,MAAM,CAAC;EACP,aAAa;CACf,CAAC,GAEK,IAAsC,GAAM,uCAAuC;EACvF,oBAAoB,CAAC;EACrB,MAAM;EACN,UAAU;EACV,YAAY;EACZ,SAAS;CACX,GAEM,IAAgB,EAAY,YAAY;EAC5C,MAAM,EAAQ;CAChB,GAAG,CAAC,CAAO,CAAC;CAEZ,OAAO;EACL,cAAc,EAAO;EACrB,YAAY;GACV,MAAM,EAAO;GACb,UAAU,EAAO;GACjB,YAAY,EAAO;GACnB,SAAS,EAAO;EAClB;EACA,WAAW;EACJ;EACP,SAAS;CACX;AACF;AA6DA,SAAgB,IAAqB;CACnC,IAAM,IAAS,EAAgB,GACzB,EAAE,kBAAe,cAAW,aAAU,EAAW,GACjD,EAAE,YAAS,EAAuB,GAElC,CAAC,GAA0B,EAAE,SAAS,OAAgB,EAA4B;CAyDxF,OAAO;EACL,kBAxDuB,EACvB,OAAO,MAOoB;GACzB,IAAI;IACF,IAAM,EAAE,YAAS,MAAM,EAAyB,EAC9C,WAAW,EACb,CAAC;IAED,OADA,MAAM,EAAO,eAAe,EAAE,SAAS,CAAC,CAAsB,EAAE,CAAC,GAC1D,EAAM;GACf,SAAS,GAAO;IAEd,MADA,QAAQ,MAAM,iCAAiC,CAAK,GAC9C;GACR;EACF,GACA,CAAC,GAA0B,CAAM,CAoCjC;EACA,cAlCmB,EACnB,OAAO,MAM2B;GAChC,IAAM,IAAS,MAAM,EAKnB;IACA;IACA;IACA;IACA,OAAO;IACP,WAAW,EAAE,OAAO,EAAO;GAC7B,CAAC;GAQD,OANA,MAAM,EAAO,eAAe,EAAE,SAAS,CAAC,qCAAqC,EAAE,CAAC,GAChF,EAAK,2BAA2B;IAC9B,UAAU,EAAO,aAAa;IAC9B,QAAQ;IACR,QAAQ,EAAO;GACjB,CAAC,GACM,EAAO;EAChB,GACA;GAAC;GAAe;GAAW;GAAQ;GAAM;EAAK,CAK9C;EACA;CACF;AACF"}
@@ -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';\nimport { PagePurpose, EmphasisPanel, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\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 <PagePurpose>\n Give credits to a customer&apos;s billing account — for promotions, service recovery or\n manual adjustments — and immediately see them reflected in that account&apos;s credit\n ledger. Pick an account, enter an amount and a reason, and grant.\n </PagePurpose>\n\n <div className=\"grid grid-cols-1 xl:grid-cols-[360px_minmax(0,1fr)] gap-6\">\n <EmphasisPanel className=\"p-6 space-y-4 h-fit\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary 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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\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-text-primary 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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n placeholder=\"500\"\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary 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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\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-status-error-text\">{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-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isSubmitting ? 'Granting Credits...' : 'Grant Credits'}\n </button>\n </EmphasisPanel>\n\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">Recent Credit Ledger</h2>\n </div>\n\n {isLoadingTransactions ? (\n <div className=\"p-6 text-sm text-text-muted\">Loading recent credit activity…</div>\n ) : transactions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No recent credit activity\"\n description=\"Credit grants and other ledger entries for this account will show up here.\"\n />\n ) : (\n <div className=\"divide-y divide-border-subtle\">\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-text-primary\">{transaction.type}</p>\n <p className=\"text-xs text-text-muted\">\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-text-primary\">\n {transaction.amount > 0 ? '+' : ''}\n {transaction.amount}\n </p>\n <p className=\"text-xs text-text-muted\">{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';\nimport { PagePurpose, EmphasisPanel, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\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 <PagePurpose>\n Give credits to a customer&apos;s billing account — for promotions, service recovery or\n manual adjustments — and immediately see them reflected in that account&apos;s credit\n ledger. Pick an account, enter an amount and a reason, and grant.\n </PagePurpose>\n\n <div className=\"grid grid-cols-1 xl:grid-cols-[360px_minmax(0,1fr)] gap-6\">\n <EmphasisPanel className=\"p-6 space-y-4 h-fit\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary 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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\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-text-primary 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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n placeholder=\"500\"\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary 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-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\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-status-error-text\">{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-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n {isSubmitting ? 'Granting Credits...' : 'Grant Credits'}\n </button>\n </EmphasisPanel>\n\n <div className=\"border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"p-4 border-b border-border-subtle bg-bg-sunken/30\">\n <h2 className=\"font-semibold text-text-primary\">Recent Credit Ledger</h2>\n </div>\n\n {isLoadingTransactions ? (\n <div className=\"p-6 text-sm text-text-muted\">Loading recent credit activity…</div>\n ) : transactions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No recent credit activity\"\n description=\"Credit grants and other ledger entries for this account will show up here.\"\n />\n ) : (\n <div className=\"divide-y divide-border-subtle\">\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-text-primary\">{transaction.type}</p>\n <p className=\"text-xs text-text-muted\">\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-text-primary\">\n {transaction.amount > 0 ? '+' : ''}\n {transaction.amount}\n </p>\n <p className=\"text-xs text-text-muted\">{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 +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';\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';\nimport { IllustratedEmptyState, PagePurpose, EmphasisPanel } from '@burdenoff/fe-libs/ui';\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-text-secondary\"\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 px-6 lg:px-8 py-6\">\n <div className=\"h-8 w-48 bg-bg-sunken 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-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={() => navigateTo('/overview')}\n className=\"inline-flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary 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 <PagePurpose>\n {tr(\n 'billing.credits.historyPurpose',\n 'A detailed ledger of every credit added to or spent from this billing account — purchases, grants, usage deductions and adjustments. Use it to understand exactly how your credit balance changed over time.'\n )}\n </PagePurpose>\n\n {/* Billing Account Info Card — primary emphasis zone */}\n {billingAccount && (\n <EmphasisPanel className=\"p-4\">\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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary 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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary 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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary mb-0.5\">Credit Balance</p>\n <p className=\"text-sm font-semibold text-text-primary\">\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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary 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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary 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 </EmphasisPanel>\n )}\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border-subtle\">\n {/* Time Range */}\n <div className=\"inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken\">\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-button transition-colors ${\n days === d\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\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-subtle p-1 bg-bg-sunken\">\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-button transition-colors capitalize ${\n filterType === type\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\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-text-secondary\">\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 <IllustratedEmptyState\n illustration={filterType !== 'all' ? 'empty-search' : 'empty-data'}\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-subtle pt-4\">\n <p className=\"text-sm text-text-secondary\">\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-button border border-border-subtle bg-bg-surface hover:bg-bg-sunken 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-text-secondary 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-button border border-border-subtle bg-bg-surface hover:bg-bg-sunken 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-text-secondary\" />;\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-bg-sunken';\n }\n };\n\n return (\n <div className=\"flex items-center gap-4 p-4 border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 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-text-primary 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-bg-sunken text-text-secondary capitalize\">\n {transaction.entityType}\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {formatRelativeTime(transaction.createdAt)} • {formatDateTime(transaction.createdAt)}\n </p>\n {transaction.productId && transaction.productId !== 'default' && (\n <p className=\"text-xs text-text-secondary 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';\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';\nimport { IllustratedEmptyState, PagePurpose, EmphasisPanel } from '@burdenoff/fe-libs/ui';\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-text-secondary\"\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 px-6 lg:px-8 py-6\">\n <div className=\"h-8 w-48 bg-bg-sunken 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-bg-sunken animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 px-6 lg:px-8 py-6\">\n {/* Back Button */}\n <button\n type=\"button\"\n onClick={() => navigateTo('/overview')}\n className=\"inline-flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary 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 <PagePurpose>\n {tr(\n 'billing.credits.historyPurpose',\n 'A detailed ledger of every credit added to or spent from this billing account — purchases, grants, usage deductions and adjustments. Use it to understand exactly how your credit balance changed over time.'\n )}\n </PagePurpose>\n\n {/* Billing Account Info Card — primary emphasis zone */}\n {billingAccount && (\n <EmphasisPanel className=\"p-4\">\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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary 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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary 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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary mb-0.5\">Credit Balance</p>\n <p className=\"text-sm font-semibold text-text-primary\">\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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary 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-text-secondary mt-0.5\" />\n <div>\n <p className=\"text-xs text-text-secondary 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 </EmphasisPanel>\n )}\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border-subtle\">\n {/* Time Range */}\n <div className=\"inline-flex rounded-lg border border-border-subtle p-1 bg-bg-sunken\">\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-button transition-colors ${\n days === d\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\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-subtle p-1 bg-bg-sunken\">\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-button transition-colors capitalize ${\n filterType === type\n ? 'bg-bg-surface text-text-primary shadow-sm'\n : 'text-text-secondary hover:text-text-primary'\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-text-secondary\">\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 <IllustratedEmptyState\n illustration={filterType !== 'all' ? 'empty-search' : 'empty-data'}\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-subtle pt-4\">\n <p className=\"text-sm text-text-secondary\">\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-button border border-border-subtle bg-bg-surface hover:bg-bg-sunken 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-text-secondary 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-button border border-border-subtle bg-bg-surface hover:bg-bg-sunken 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-text-secondary\" />;\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-bg-sunken';\n }\n };\n\n return (\n <div className=\"flex items-center gap-4 p-4 border border-border-seam rounded-card bg-bg-surface shadow-elevation-1 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-text-primary 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-bg-sunken text-text-secondary capitalize\">\n {transaction.entityType}\n </span>\n )}\n </div>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {formatRelativeTime(transaction.createdAt)} • {formatDateTime(transaction.createdAt)}\n </p>\n {transaction.productId && transaction.productId !== 'default' && (\n <p className=\"text-xs text-text-secondary 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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"}