@burdenoff/microfe-billing 2026.624.2 → 2026.625.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/billing/BillingAdminRoutes.js +1 -1
  2. package/dist/billing/BillingAdminRoutes.js.map +1 -1
  3. package/dist/billing/modules/addons/pages/AddonCreatePage.js +2 -2
  4. package/dist/billing/modules/addons/pages/AddonCreatePage.js.map +1 -1
  5. package/dist/billing/modules/addons/pages/AddonDetailPage.js +4 -4
  6. package/dist/billing/modules/addons/pages/AddonDetailPage.js.map +1 -1
  7. package/dist/billing/modules/addons/pages/AddonEditPage.js +2 -2
  8. package/dist/billing/modules/addons/pages/AddonEditPage.js.map +1 -1
  9. package/dist/billing/modules/addons/pages/AddonsListPage.js +1 -1
  10. package/dist/billing/modules/addons/pages/AddonsListPage.js.map +1 -1
  11. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js +98 -99
  12. package/dist/billing/modules/affiliate/pages/AffiliateDashboardPage.js.map +1 -1
  13. package/dist/billing/modules/analytics/pages/AnalyticsPage.js +88 -85
  14. package/dist/billing/modules/analytics/pages/AnalyticsPage.js.map +1 -1
  15. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js +6 -6
  16. package/dist/billing/modules/billing/pages/InvoiceDetailPage.js.map +1 -1
  17. package/dist/billing/modules/billing/pages/InvoicesListPage.js +2 -2
  18. package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
  19. package/dist/billing/modules/billing/pages/RefundsPage.js +383 -399
  20. package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
  21. package/dist/billing/modules/billing/pages/TransactionsPage.js +232 -252
  22. package/dist/billing/modules/billing/pages/TransactionsPage.js.map +1 -1
  23. package/dist/billing/modules/billing/pages/UserRefundsPage.js +2 -2
  24. package/dist/billing/modules/billing/pages/UserRefundsPage.js.map +1 -1
  25. package/dist/billing/modules/checkout/components/BillingAccountSelector.js +1 -1
  26. package/dist/billing/modules/checkout/components/BillingAccountSelector.js.map +1 -1
  27. package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js +1 -1
  28. package/dist/billing/modules/checkout/components/CompleteBillingAccountModal.js.map +1 -1
  29. package/dist/billing/modules/checkout/components/SubscriptionSelector.js +1 -1
  30. package/dist/billing/modules/checkout/components/SubscriptionSelector.js.map +1 -1
  31. package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js +2 -2
  32. package/dist/billing/modules/checkout/components/WorkspaceQuotaAssignmentModal.js.map +1 -1
  33. package/dist/billing/modules/coupons/pages/CouponDetailPage.js +102 -104
  34. package/dist/billing/modules/coupons/pages/CouponDetailPage.js.map +1 -1
  35. package/dist/billing/modules/coupons/pages/CouponsListPage.js +211 -249
  36. package/dist/billing/modules/coupons/pages/CouponsListPage.js.map +1 -1
  37. package/dist/billing/modules/credits/pages/AdminCreditsPage.js +1 -1
  38. package/dist/billing/modules/credits/pages/AdminCreditsPage.js.map +1 -1
  39. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js +1 -1
  40. package/dist/billing/modules/credits/pages/CreditTransactionsPage.js.map +1 -1
  41. package/dist/billing/modules/credits/pages/CreditsPage.js +29 -28
  42. package/dist/billing/modules/credits/pages/CreditsPage.js.map +1 -1
  43. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js +2 -2
  44. package/dist/billing/modules/dashboard/components/ActiveSubscriptionCard.js.map +1 -1
  45. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js +1 -1
  46. package/dist/billing/modules/dashboard/components/AddPaymentMethodModal.js.map +1 -1
  47. package/dist/billing/modules/dashboard/components/BillingAccountCard.js +3 -3
  48. package/dist/billing/modules/dashboard/components/BillingAccountCard.js.map +1 -1
  49. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js +2 -2
  50. package/dist/billing/modules/dashboard/components/CreateBillingAccountModal.js.map +1 -1
  51. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js +1 -1
  52. package/dist/billing/modules/dashboard/components/PaymentMethodsSection.js.map +1 -1
  53. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js +1 -1
  54. package/dist/billing/modules/dashboard/components/RecommendedAddonsSection.js.map +1 -1
  55. package/dist/billing/modules/dashboard/pages/OverviewPage.js +139 -142
  56. package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
  57. package/dist/billing/modules/discounts/pages/DiscountsListPage.js +6 -6
  58. package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
  59. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js +4 -4
  60. package/dist/billing/modules/earnings/pages/PayoutAdminPage.js.map +1 -1
  61. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js +1 -1
  62. package/dist/billing/modules/plans/pages/PlanAssignFreePage.js.map +1 -1
  63. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js +142 -133
  64. package/dist/billing/modules/plans/pages/PlanBrowseDetailPage.js.map +1 -1
  65. package/dist/billing/modules/plans/pages/PlanCreatePage.js +2 -2
  66. package/dist/billing/modules/plans/pages/PlanCreatePage.js.map +1 -1
  67. package/dist/billing/modules/plans/pages/PlanDetailPage.js +4 -4
  68. package/dist/billing/modules/plans/pages/PlanDetailPage.js.map +1 -1
  69. package/dist/billing/modules/plans/pages/PlanEditPage.js +2 -2
  70. package/dist/billing/modules/plans/pages/PlanEditPage.js.map +1 -1
  71. package/dist/billing/modules/plans/pages/PlansBrowsePage.js +3 -3
  72. package/dist/billing/modules/plans/pages/PlansBrowsePage.js.map +1 -1
  73. package/dist/billing/modules/plans/pages/PlansListPage.js +2 -2
  74. package/dist/billing/modules/plans/pages/PlansListPage.js.map +1 -1
  75. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js +4 -4
  76. package/dist/billing/modules/promotions/pages/CreatePromotionPage.js.map +1 -1
  77. package/dist/billing/modules/promotions/pages/PromotionsListPage.js +2 -2
  78. package/dist/billing/modules/promotions/pages/PromotionsListPage.js.map +1 -1
  79. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +2 -2
  80. package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
  81. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js +5 -5
  82. package/dist/billing/modules/quota/pages/QuotaTemplateDetailPage.js.map +1 -1
  83. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +2 -2
  84. package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
  85. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js +1 -1
  86. package/dist/billing/modules/quota/pages/QuotaTemplatesListPage.js.map +1 -1
  87. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js +259 -265
  88. package/dist/billing/modules/subscriptions/pages/SubscriptionDetailPage.js.map +1 -1
  89. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +7 -7
  90. package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
  91. package/dist/billing/modules/usage/pages/UsagePage.js +13 -13
  92. package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
  93. package/dist/billing/shared/ui/Card.js +1 -1
  94. package/dist/billing/shared/ui/Card.js.map +1 -1
  95. package/dist/billing/shared/ui/MetricCard.js +28 -21
  96. package/dist/billing/shared/ui/MetricCard.js.map +1 -1
  97. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"DiscountsListPage.js","names":[],"sources":["../../../../../src/billing/modules/discounts/pages/DiscountsListPage.tsx"],"sourcesContent":["/**\n * Discounts Module - Discounts List Page\n * Admin page for creating, viewing, and managing discount codes\n */\n\nimport { useMemo, useState, type FC, type FormEvent } from 'react';\nimport {\n useGetDiscountsQuery,\n useCreateDiscountMutation,\n useUpdateDiscountMutation,\n useDeleteDiscountMutation,\n} from '../../../../generated/global-operations';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied } from '../../../shared/components';\nimport { formatCurrency, formatDate } from '../../../shared/utils/format';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype DiscountType = 'PERCENTAGE' | 'FIXED';\n\ninterface BillingDiscount {\n id: string;\n code: string;\n type: DiscountType;\n value: number;\n currency: string | null;\n maxUses: number | null;\n usedCount: number;\n validFrom: string | null;\n validUntil: string | null;\n metadata: Record<string, unknown> | null;\n context: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface CreateDiscountForm {\n code: string;\n type: DiscountType;\n value: string;\n currency: string;\n maxUses: string;\n validFrom: string;\n validUntil: string;\n}\n\ninterface EditDiscountForm {\n maxUses: string;\n validUntil: string;\n}\n\ntype ModalState =\n | { kind: 'closed' }\n | { kind: 'create' }\n | { kind: 'edit'; discount: BillingDiscount }\n | { kind: 'delete'; discount: BillingDiscount };\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction isExpired(discount: BillingDiscount): boolean {\n if (!discount.validUntil) return false;\n return new Date(discount.validUntil) < new Date();\n}\n\nfunction formatDiscountValue(discount: BillingDiscount): string {\n if (discount.type === 'PERCENTAGE') {\n return `${discount.value}%`;\n }\n return formatCurrency(discount.value, discount.currency ?? 'USD');\n}\n\nconst INITIAL_CREATE_FORM: CreateDiscountForm = {\n code: '',\n type: 'PERCENTAGE',\n value: '',\n currency: 'USD',\n maxUses: '',\n validFrom: '',\n validUntil: '',\n};\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nexport const DiscountsListPage: 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 permissions = useBillingPermissions();\n\n const { data, loading, error, refetch } = useGetDiscountsQuery();\n\n const [createDiscount, { loading: isCreating }] = useCreateDiscountMutation();\n const [updateDiscount, { loading: isUpdating }] = useUpdateDiscountMutation();\n const [deleteDiscount, { loading: isDeleting }] = useDeleteDiscountMutation();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [modal, setModal] = useState<ModalState>({ kind: 'closed' });\n const [createForm, setCreateForm] = useState<CreateDiscountForm>(INITIAL_CREATE_FORM);\n const [editForm, setEditForm] = useState<EditDiscountForm>({ maxUses: '', validUntil: '' });\n const [mutationError, setMutationError] = useState<string | null>(null);\n const [selectedDiscountIds, setSelectedDiscountIds] = useState<Set<string>>(new Set());\n const [showBulkDeleteConfirm, setShowBulkDeleteConfirm] = useState(false);\n\n const discounts: BillingDiscount[] = (data?.getDiscounts ?? []) as BillingDiscount[];\n\n // Filtered discounts\n const filteredDiscounts = discounts.filter((discount: BillingDiscount) => {\n if (!searchQuery) return true;\n return discount.code.toLowerCase().includes(searchQuery.toLowerCase());\n });\n\n const selectedDiscounts = useMemo(\n () => filteredDiscounts.filter((discount) => selectedDiscountIds.has(discount.id)),\n [filteredDiscounts, selectedDiscountIds]\n );\n\n const allVisibleSelected =\n filteredDiscounts.length > 0 &&\n filteredDiscounts.every((discount) => selectedDiscountIds.has(discount.id));\n const partiallySelected =\n filteredDiscounts.some((discount) => selectedDiscountIds.has(discount.id)) &&\n !allVisibleSelected;\n\n // Permission check\n if (!permissions.canManageDiscounts) {\n return <AccessDenied message=\"You don't have permission to manage discount codes.\" />;\n }\n\n // Summary stats\n const totalDiscounts = discounts.length;\n const activeDiscounts = discounts.filter((d: BillingDiscount) => !isExpired(d)).length;\n const expiredDiscounts = discounts.filter((d: BillingDiscount) => isExpired(d)).length;\n\n // ── Modal handlers ──────────────────────────────────────────────────\n\n const openCreateModal = () => {\n setCreateForm(INITIAL_CREATE_FORM);\n setMutationError(null);\n setModal({ kind: 'create' });\n };\n\n const openEditModal = (discount: BillingDiscount) => {\n setEditForm({\n maxUses: discount.maxUses != null ? String(discount.maxUses) : '',\n validUntil: discount.validUntil\n ? new Date(discount.validUntil).toISOString().slice(0, 16)\n : '',\n });\n setMutationError(null);\n setModal({ kind: 'edit', discount });\n };\n\n const openDeleteModal = (discount: BillingDiscount) => {\n setMutationError(null);\n setModal({ kind: 'delete', discount });\n };\n\n const closeModal = () => {\n setModal({ kind: 'closed' });\n setMutationError(null);\n };\n\n // ── Mutation handlers ───────────────────────────────────────────────\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n setMutationError(null);\n\n if (!createForm.code.trim()) {\n setMutationError('Discount code is required.');\n return;\n }\n\n const valueNum = Number(createForm.value);\n if (Number.isNaN(valueNum) || valueNum <= 0) {\n setMutationError('Value must be a positive number.');\n return;\n }\n\n if (createForm.type === 'PERCENTAGE' && valueNum > 100) {\n setMutationError('Percentage value cannot exceed 100.');\n return;\n }\n\n try {\n await createDiscount({\n variables: {\n code: createForm.code.trim().toUpperCase(),\n type: createForm.type,\n value: valueNum,\n currency: createForm.type === 'FIXED' ? createForm.currency : null,\n maxUses: createForm.maxUses ? Number(createForm.maxUses) : null,\n validFrom: createForm.validFrom ? new Date(createForm.validFrom).toISOString() : null,\n validUntil: createForm.validUntil ? new Date(createForm.validUntil).toISOString() : null,\n },\n });\n await refetch();\n closeModal();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to create discount.');\n }\n };\n\n const handleUpdate = async (e: FormEvent) => {\n e.preventDefault();\n if (modal.kind !== 'edit') return;\n setMutationError(null);\n\n try {\n await updateDiscount({\n variables: {\n id: modal.discount.id,\n maxUses: editForm.maxUses ? Number(editForm.maxUses) : null,\n validUntil: editForm.validUntil ? new Date(editForm.validUntil).toISOString() : null,\n },\n });\n await refetch();\n closeModal();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to update discount.');\n }\n };\n\n const handleDelete = async () => {\n if (modal.kind !== 'delete') return;\n setMutationError(null);\n void nativeImpact('medium');\n\n try {\n await deleteDiscount({ variables: { id: modal.discount.id } });\n void nativeNotify('success');\n await refetch();\n closeModal();\n } catch (err) {\n void nativeNotify('error');\n setMutationError(err instanceof Error ? err.message : 'Failed to delete discount.');\n }\n };\n\n const handleBulkDelete = async () => {\n if (selectedDiscounts.length === 0) return;\n\n setMutationError(null);\n setShowBulkDeleteConfirm(false);\n await Promise.allSettled(\n selectedDiscounts.map((discount) => deleteDiscount({ variables: { id: discount.id } }))\n );\n await refetch();\n setSelectedDiscountIds(new Set());\n };\n\n const toggleDiscountSelection = (discountId: string, checked: boolean) => {\n setSelectedDiscountIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(discountId);\n } else {\n next.delete(discountId);\n }\n return next;\n });\n setShowBulkDeleteConfirm(false);\n };\n\n const toggleAllDiscounts = (checked: boolean) => {\n setSelectedDiscountIds((prev) => {\n const next = new Set(prev);\n filteredDiscounts.forEach((discount) => {\n if (checked) {\n next.add(discount.id);\n } else {\n next.delete(discount.id);\n }\n });\n return next;\n });\n setShowBulkDeleteConfirm(false);\n };\n\n // ── Loading state ───────────────────────────────────────────────────\n\n if (loading && discounts.length === 0) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"border border-border-subtle rounded-lg p-4 space-y-2\">\n <div className=\"h-4 w-24 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-6 w-16 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))}\n </div>\n <div className=\"space-y-3\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"border border-border-subtle rounded-lg p-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-3 w-48 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // ── Error state ─────────────────────────────────────────────────────\n\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Failed to load discounts</h2>\n <p className=\"text-sm text-text-muted\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n }\n\n // ── Main render ─────────────────────────────────────────────────────\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n Discount Codes\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n Create, view, and manage discount codes for billing\n </p>\n </div>\n <button\n type=\"button\"\n onClick={openCreateModal}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 4v16m8-8H4\" />\n </svg>\n Create Discount\n </button>\n </div>\n\n {/* Summary Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n <div className=\"rounded-card border border-border-subtle bg-bg-surface p-5 shadow-[var(--shadow-elevation-1)]\">\n <p className=\"text-sm text-text-secondary\">Total Discounts</p>\n <p className=\"text-2xl font-semibold text-text-primary tabular-nums mt-1\">\n {totalDiscounts}\n </p>\n </div>\n <div className=\"rounded-card border border-border-subtle bg-bg-surface p-5 shadow-[var(--shadow-elevation-1)]\">\n <p className=\"text-sm text-text-secondary\">Active</p>\n <p className=\"text-2xl font-semibold text-status-success-text tabular-nums mt-1\">\n {activeDiscounts}\n </p>\n </div>\n <div className=\"rounded-card border border-border-subtle bg-bg-surface p-5 shadow-[var(--shadow-elevation-1)]\">\n <p className=\"text-sm text-text-secondary\">Expired</p>\n <p className=\"text-2xl font-semibold text-status-warning-text tabular-nums mt-1\">\n {expiredDiscounts}\n </p>\n </div>\n </div>\n\n {/* Search */}\n <div className=\"flex flex-col sm:flex-row flex-wrap items-stretch sm:items-center gap-3 sm:gap-4 pb-4 border-b border-border-subtle\">\n <div className=\"relative flex-1 min-w-0 w-full sm:max-w-md\">\n <svg\n className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n <input\n type=\"text\"\n placeholder=\"Search by discount code...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n </div>\n </div>\n\n {filteredDiscounts.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllDiscounts(event.target.checked)}\n aria-label=\"Select all discounts\"\n className=\"size-4 rounded border-border-subtle text-text-link\"\n />\n <span>Select all visible</span>\n </label>\n\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-muted\">{selectedDiscountIds.size} selected</span>\n {selectedDiscountIds.size > 0 && (\n <button\n type=\"button\"\n onClick={() => {\n setSelectedDiscountIds(new Set());\n setShowBulkDeleteConfirm(false);\n }}\n className=\"text-sm text-text-muted hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => setShowBulkDeleteConfirm(true)}\n disabled={selectedDiscounts.length === 0 || isDeleting}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium border border-status-error-border text-status-error-text rounded-md hover:bg-status-error-bg-subtle transition-colors disabled:opacity-50\"\n >\n Delete Selected{selectedDiscounts.length > 0 ? ` (${selectedDiscounts.length})` : ''}\n </button>\n </div>\n </div>\n )}\n\n {showBulkDeleteConfirm && selectedDiscounts.length > 0 && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Delete {selectedDiscounts.length} selected discount\n {selectedDiscounts.length === 1 ? '' : 's'}?\n </p>\n <p className=\"text-sm text-text-muted\">This action cannot be undone.</p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setShowBulkDeleteConfirm(false)}\n className=\"px-3 py-2 text-sm font-medium text-text-muted hover:text-text-primary\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={isDeleting}\n className=\"inline-flex items-center gap-2 rounded-button bg-action-danger-bg px-3 py-2 text-sm font-medium text-action-danger-text hover:bg-action-danger-bgHover disabled:opacity-50\"\n >\n Confirm Delete\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Discounts Table */}\n {filteredDiscounts.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-16 border border-dashed border-border-subtle rounded-card bg-bg-surface\">\n <div className=\"size-12 rounded-full bg-[var(--color-accent-soft)] flex items-center justify-center mb-4\">\n <svg\n className=\"size-6 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A2 2 0 013 12V7a4 4 0 014-4z\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-text-primary\">No discount codes found</h3>\n <p className=\"text-sm text-text-muted mt-1\">\n {searchQuery\n ? 'No discount codes match your search.'\n : 'Create your first discount code to get started.'}\n </p>\n {!searchQuery && (\n <button\n type=\"button\"\n onClick={openCreateModal}\n className=\"mt-4 px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Create your first discount\n </button>\n )}\n </div>\n ) : (\n <div className=\"border border-border-subtle rounded-card overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead className=\"sticky top-0 z-10 bg-bg-sunken border-b border-border-subtle\">\n <tr>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted w-12\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllDiscounts(event.target.checked)}\n aria-label=\"Select all discounts\"\n className=\"size-4 rounded border-border-subtle text-action-primary-bg focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n />\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Code\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Type\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Value\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted hidden md:table-cell\">\n Uses\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted hidden lg:table-cell\">\n Valid From\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted hidden sm:table-cell\">\n Valid Until\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Status\n </th>\n <th className=\"text-right px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Actions\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-subtle\">\n {filteredDiscounts.map((discount: BillingDiscount) => (\n <DiscountRow\n key={discount.id}\n discount={discount}\n selected={selectedDiscountIds.has(discount.id)}\n onToggleSelect={toggleDiscountSelection}\n onEdit={openEditModal}\n onDelete={openDeleteModal}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n {/* Create Modal */}\n {modal.kind === 'create' && (\n <ModalOverlay onClose={closeModal}>\n <form onSubmit={handleCreate} className=\"space-y-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.discounts.createDiscountCode', 'Create Discount Code')}\n </h2>\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <svg\n className=\"size-5 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n\n {mutationError && (\n <div className=\"p-3 text-sm text-status-error-text bg-status-error-bg-subtle border border-status-error-border rounded-md\">\n {mutationError}\n </div>\n )}\n\n {/* Code */}\n <div>\n <label\n htmlFor=\"create-code\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.code', 'Code')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"create-code\"\n type=\"text\"\n required\n value={createForm.code}\n onChange={(e) => setCreateForm((prev) => ({ ...prev, code: e.target.value }))}\n placeholder={tr('billing.discounts.codePlaceholder', 'e.g. SUMMER25')}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent uppercase\"\n />\n </div>\n\n {/* Type + Value row */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"create-type\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.type', 'Type')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <select\n id=\"create-type\"\n value={createForm.type}\n onChange={(e) =>\n setCreateForm((prev) => ({\n ...prev,\n type: e.target.value as DiscountType,\n }))\n }\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n >\n <option value=\"PERCENTAGE\">\n {tr('billing.discounts.typePercentage', 'Percentage')}\n </option>\n <option value=\"FIXED\">\n {tr('billing.discounts.typeFixedAmount', 'Fixed Amount')}\n </option>\n </select>\n </div>\n <div>\n <label\n htmlFor=\"create-value\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.value', 'Value')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <input\n id=\"create-value\"\n type=\"number\"\n required\n min=\"0.01\"\n max={createForm.type === 'PERCENTAGE' ? '100' : undefined}\n step=\"0.01\"\n value={createForm.value}\n onChange={(e) => setCreateForm((prev) => ({ ...prev, value: e.target.value }))}\n placeholder={createForm.type === 'PERCENTAGE' ? '10' : '25.00'}\n className=\"w-full px-3 py-2 pr-8 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n <span className=\"absolute right-3 top-1/2 -translate-y-1/2 text-sm text-text-muted\">\n {createForm.type === 'PERCENTAGE' ? '%' : createForm.currency}\n </span>\n </div>\n </div>\n </div>\n\n {/* Currency (only for FIXED) */}\n {createForm.type === 'FIXED' && (\n <div>\n <label\n htmlFor=\"create-currency\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.currency', 'Currency')}\n </label>\n <select\n id=\"create-currency\"\n value={createForm.currency}\n onChange={(e) => setCreateForm((prev) => ({ ...prev, currency: e.target.value }))}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n >\n <option value=\"USD\">USD</option>\n <option value=\"EUR\">EUR</option>\n <option value=\"GBP\">GBP</option>\n <option value=\"INR\">INR</option>\n <option value=\"CAD\">CAD</option>\n <option value=\"AUD\">AUD</option>\n </select>\n </div>\n )}\n\n {/* Max Uses */}\n <div>\n <label\n htmlFor=\"create-max-uses\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.maxUses', 'Max Uses')}\n </label>\n <input\n id=\"create-max-uses\"\n type=\"number\"\n min=\"1\"\n step=\"1\"\n value={createForm.maxUses}\n onChange={(e) => setCreateForm((prev) => ({ ...prev, maxUses: e.target.value }))}\n placeholder={tr('billing.discounts.unlimited', 'Unlimited')}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n {tr('billing.discounts.unlimitedHelp', 'Leave empty for unlimited uses.')}\n </p>\n </div>\n\n {/* Valid From / Valid Until row */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"create-valid-from\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.validFrom', 'Valid From')}\n </label>\n <input\n id=\"create-valid-from\"\n type=\"datetime-local\"\n value={createForm.validFrom}\n onChange={(e) =>\n setCreateForm((prev) => ({ ...prev, validFrom: e.target.value }))\n }\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n </div>\n <div>\n <label\n htmlFor=\"create-valid-until\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.validUntil', 'Valid Until')}\n </label>\n <input\n id=\"create-valid-until\"\n type=\"datetime-local\"\n value={createForm.validUntil}\n onChange={(e) =>\n setCreateForm((prev) => ({ ...prev, validUntil: e.target.value }))\n }\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 pt-2 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n {tr('billing.discounts.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isCreating}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isCreating\n ? tr('billing.discounts.creating', 'Creating...')\n : tr('billing.discounts.createDiscount', 'Create Discount')}\n </button>\n </div>\n </form>\n </ModalOverlay>\n )}\n\n {/* Edit Modal */}\n {modal.kind === 'edit' && (\n <ModalOverlay onClose={closeModal}>\n <form onSubmit={handleUpdate} className=\"space-y-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Edit Discount</h2>\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <svg\n className=\"size-5 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n\n {mutationError && (\n <div className=\"p-3 text-sm text-status-error-text bg-status-error-bg-subtle border border-status-error-border rounded-md\">\n {mutationError}\n </div>\n )}\n\n {/* Read-only info */}\n <div className=\"p-3 bg-bg-sunken/50 border border-border-subtle rounded-md space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Code</span>\n <span className=\"font-mono font-medium text-text-primary\">\n {modal.discount.code}\n </span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Type</span>\n <span className=\"text-text-primary\">\n {modal.discount.type === 'PERCENTAGE' ? 'Percentage' : 'Fixed Amount'}\n </span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Value</span>\n <span className=\"text-text-primary\">{formatDiscountValue(modal.discount)}</span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Used</span>\n <span className=\"text-text-primary\">{modal.discount.usedCount} times</span>\n </div>\n </div>\n\n {/* Editable: Max Uses */}\n <div>\n <label\n htmlFor=\"edit-max-uses\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Max Uses\n </label>\n <input\n id=\"edit-max-uses\"\n type=\"number\"\n min=\"1\"\n step=\"1\"\n value={editForm.maxUses}\n onChange={(e) => setEditForm((prev) => ({ ...prev, maxUses: e.target.value }))}\n placeholder=\"Unlimited\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n <p className=\"text-xs text-text-muted mt-1\">Leave empty for unlimited uses.</p>\n </div>\n\n {/* Editable: Valid Until */}\n <div>\n <label\n htmlFor=\"edit-valid-until\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Valid Until\n </label>\n <input\n id=\"edit-valid-until\"\n type=\"datetime-local\"\n value={editForm.validUntil}\n onChange={(e) => setEditForm((prev) => ({ ...prev, validUntil: e.target.value }))}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 pt-2 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isUpdating}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isUpdating ? 'Saving...' : 'Save Changes'}\n </button>\n </div>\n </form>\n </ModalOverlay>\n )}\n\n {/* Delete Confirmation Modal */}\n {modal.kind === 'delete' && (\n <ModalOverlay onClose={closeModal}>\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Delete Discount</h2>\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <svg\n className=\"size-5 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n\n {mutationError && (\n <div className=\"p-3 text-sm text-status-error-text bg-status-error-bg-subtle border border-status-error-border rounded-md\">\n {mutationError}\n </div>\n )}\n\n <div className=\"flex items-start gap-3\">\n <div className=\"size-10 rounded-full bg-status-error-bg-subtle flex items-center justify-center shrink-0\">\n <svg\n className=\"size-5 text-status-error-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div>\n <p className=\"text-sm text-text-primary\">\n Are you sure you want to delete the discount code{' '}\n <span className=\"font-mono font-semibold\">{modal.discount.code}</span>?\n </p>\n <p className=\"text-sm text-text-muted mt-1\">\n This action cannot be undone. The discount has been used{' '}\n <span className=\"font-medium\">{modal.discount.usedCount}</span> time\n {modal.discount.usedCount !== 1 ? 's' : ''}.\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center justify-end gap-3 pt-2 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"px-4 py-2 text-sm font-medium bg-action-danger-bg text-action-danger-text rounded-button hover:bg-action-danger-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isDeleting ? 'Deleting...' : 'Delete Discount'}\n </button>\n </div>\n </div>\n </ModalOverlay>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Discount Row Component\n// ============================================================================\n\ninterface DiscountRowProps {\n discount: BillingDiscount;\n selected: boolean;\n onToggleSelect: (discountId: string, checked: boolean) => void;\n onEdit: (discount: BillingDiscount) => void;\n onDelete: (discount: BillingDiscount) => void;\n}\n\nconst DiscountRow: FC<DiscountRowProps> = ({\n discount,\n selected,\n onToggleSelect,\n onEdit,\n onDelete,\n}) => {\n const expired = isExpired(discount);\n\n return (\n <tr className={`hover:bg-bg-sunken/30 transition-colors ${selected ? 'bg-bg-sunken/20' : ''}`}>\n <td className=\"p-4\">\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(event) => onToggleSelect(discount.id, event.target.checked)}\n aria-label={`Select discount ${discount.code}`}\n className=\"size-4 rounded border-border-subtle text-text-link\"\n />\n </td>\n {/* Code */}\n <td className=\"p-4\">\n <span className=\"font-mono font-medium text-text-primary\">{discount.code}</span>\n </td>\n\n {/* Type */}\n <td className=\"p-4\">\n <span className=\"text-sm text-text-muted\">\n {discount.type === 'PERCENTAGE' ? 'Percentage' : 'Fixed'}\n </span>\n </td>\n\n {/* Value */}\n <td className=\"p-4\">\n <span className=\"text-sm font-medium text-text-primary\">\n {formatDiscountValue(discount)}\n </span>\n </td>\n\n {/* Uses */}\n <td className=\"p-4 hidden md:table-cell\">\n <span className=\"text-sm text-text-muted\">\n {discount.usedCount}\n {discount.maxUses != null ? ` / ${discount.maxUses}` : ' / \\u221E'}\n </span>\n </td>\n\n {/* Valid From */}\n <td className=\"p-4 text-sm text-text-muted hidden lg:table-cell\">\n {discount.validFrom ? formatDate(discount.validFrom, 'short') : '\\u2014'}\n </td>\n\n {/* Valid Until */}\n <td className=\"p-4 text-sm text-text-muted hidden sm:table-cell\">\n {discount.validUntil ? formatDate(discount.validUntil, 'short') : '\\u2014'}\n </td>\n\n {/* Status */}\n <td className=\"p-4\">\n {expired ? (\n <span className=\"inline-flex px-2 py-1 text-xs font-medium rounded bg-status-warning-bg-subtle text-status-warning-text\">\n Expired\n </span>\n ) : (\n <span className=\"inline-flex px-2 py-1 text-xs font-medium rounded bg-status-success-bg-subtle text-status-success-text\">\n Active\n </span>\n )}\n </td>\n\n {/* Actions */}\n <td className=\"p-4 text-right\">\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => onEdit(discount)}\n className=\"px-3 py-1.5 text-sm font-medium text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n title=\"Edit discount\"\n >\n {/* Pencil icon */}\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => onDelete(discount)}\n className=\"px-3 py-1.5 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle rounded-md transition-colors\"\n title=\"Delete discount\"\n >\n {/* Trash icon */}\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n </button>\n </div>\n </td>\n </tr>\n );\n};\n\n// ============================================================================\n// Modal Overlay Component\n// ============================================================================\n\ninterface ModalOverlayProps {\n onClose: () => void;\n children: React.ReactNode;\n}\n\nconst ModalOverlay: FC<ModalOverlayProps> = ({ onClose, children }) => {\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <div className=\"absolute inset-0 bg-bg-overlay\" onClick={onClose} />\n\n {/* Dialog */}\n <div className=\"relative w-full max-w-lg max-h-[85vh] overflow-y-auto bg-bg-elevated border border-border-subtle rounded-card shadow-[var(--shadow-elevation-4)] p-6 mx-4\">\n {children}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"DiscountsListPage.js","names":[],"sources":["../../../../../src/billing/modules/discounts/pages/DiscountsListPage.tsx"],"sourcesContent":["/**\n * Discounts Module - Discounts List Page\n * Admin page for creating, viewing, and managing discount codes\n */\n\nimport { useMemo, useState, type FC, type FormEvent } from 'react';\nimport {\n useGetDiscountsQuery,\n useCreateDiscountMutation,\n useUpdateDiscountMutation,\n useDeleteDiscountMutation,\n} from '../../../../generated/global-operations';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { AccessDenied } from '../../../shared/components';\nimport { formatCurrency, formatDate } from '../../../shared/utils/format';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { nativeImpact, nativeNotify } from '../../../../utils/nativeBridge';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype DiscountType = 'PERCENTAGE' | 'FIXED';\n\ninterface BillingDiscount {\n id: string;\n code: string;\n type: DiscountType;\n value: number;\n currency: string | null;\n maxUses: number | null;\n usedCount: number;\n validFrom: string | null;\n validUntil: string | null;\n metadata: Record<string, unknown> | null;\n context: Record<string, unknown> | null;\n createdAt: string;\n updatedAt: string;\n}\n\ninterface CreateDiscountForm {\n code: string;\n type: DiscountType;\n value: string;\n currency: string;\n maxUses: string;\n validFrom: string;\n validUntil: string;\n}\n\ninterface EditDiscountForm {\n maxUses: string;\n validUntil: string;\n}\n\ntype ModalState =\n | { kind: 'closed' }\n | { kind: 'create' }\n | { kind: 'edit'; discount: BillingDiscount }\n | { kind: 'delete'; discount: BillingDiscount };\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction isExpired(discount: BillingDiscount): boolean {\n if (!discount.validUntil) return false;\n return new Date(discount.validUntil) < new Date();\n}\n\nfunction formatDiscountValue(discount: BillingDiscount): string {\n if (discount.type === 'PERCENTAGE') {\n return `${discount.value}%`;\n }\n return formatCurrency(discount.value, discount.currency ?? 'USD');\n}\n\nconst INITIAL_CREATE_FORM: CreateDiscountForm = {\n code: '',\n type: 'PERCENTAGE',\n value: '',\n currency: 'USD',\n maxUses: '',\n validFrom: '',\n validUntil: '',\n};\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nexport const DiscountsListPage: 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 permissions = useBillingPermissions();\n\n const { data, loading, error, refetch } = useGetDiscountsQuery();\n\n const [createDiscount, { loading: isCreating }] = useCreateDiscountMutation();\n const [updateDiscount, { loading: isUpdating }] = useUpdateDiscountMutation();\n const [deleteDiscount, { loading: isDeleting }] = useDeleteDiscountMutation();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [modal, setModal] = useState<ModalState>({ kind: 'closed' });\n const [createForm, setCreateForm] = useState<CreateDiscountForm>(INITIAL_CREATE_FORM);\n const [editForm, setEditForm] = useState<EditDiscountForm>({ maxUses: '', validUntil: '' });\n const [mutationError, setMutationError] = useState<string | null>(null);\n const [selectedDiscountIds, setSelectedDiscountIds] = useState<Set<string>>(new Set());\n const [showBulkDeleteConfirm, setShowBulkDeleteConfirm] = useState(false);\n\n const discounts: BillingDiscount[] = (data?.getDiscounts ?? []) as BillingDiscount[];\n\n // Filtered discounts\n const filteredDiscounts = discounts.filter((discount: BillingDiscount) => {\n if (!searchQuery) return true;\n return discount.code.toLowerCase().includes(searchQuery.toLowerCase());\n });\n\n const selectedDiscounts = useMemo(\n () => filteredDiscounts.filter((discount) => selectedDiscountIds.has(discount.id)),\n [filteredDiscounts, selectedDiscountIds]\n );\n\n const allVisibleSelected =\n filteredDiscounts.length > 0 &&\n filteredDiscounts.every((discount) => selectedDiscountIds.has(discount.id));\n const partiallySelected =\n filteredDiscounts.some((discount) => selectedDiscountIds.has(discount.id)) &&\n !allVisibleSelected;\n\n // Permission check\n if (!permissions.canManageDiscounts) {\n return <AccessDenied message=\"You don't have permission to manage discount codes.\" />;\n }\n\n // Summary stats\n const totalDiscounts = discounts.length;\n const activeDiscounts = discounts.filter((d: BillingDiscount) => !isExpired(d)).length;\n const expiredDiscounts = discounts.filter((d: BillingDiscount) => isExpired(d)).length;\n\n // ── Modal handlers ──────────────────────────────────────────────────\n\n const openCreateModal = () => {\n setCreateForm(INITIAL_CREATE_FORM);\n setMutationError(null);\n setModal({ kind: 'create' });\n };\n\n const openEditModal = (discount: BillingDiscount) => {\n setEditForm({\n maxUses: discount.maxUses != null ? String(discount.maxUses) : '',\n validUntil: discount.validUntil\n ? new Date(discount.validUntil).toISOString().slice(0, 16)\n : '',\n });\n setMutationError(null);\n setModal({ kind: 'edit', discount });\n };\n\n const openDeleteModal = (discount: BillingDiscount) => {\n setMutationError(null);\n setModal({ kind: 'delete', discount });\n };\n\n const closeModal = () => {\n setModal({ kind: 'closed' });\n setMutationError(null);\n };\n\n // ── Mutation handlers ───────────────────────────────────────────────\n\n const handleCreate = async (e: FormEvent) => {\n e.preventDefault();\n setMutationError(null);\n\n if (!createForm.code.trim()) {\n setMutationError('Discount code is required.');\n return;\n }\n\n const valueNum = Number(createForm.value);\n if (Number.isNaN(valueNum) || valueNum <= 0) {\n setMutationError('Value must be a positive number.');\n return;\n }\n\n if (createForm.type === 'PERCENTAGE' && valueNum > 100) {\n setMutationError('Percentage value cannot exceed 100.');\n return;\n }\n\n try {\n await createDiscount({\n variables: {\n code: createForm.code.trim().toUpperCase(),\n type: createForm.type,\n value: valueNum,\n currency: createForm.type === 'FIXED' ? createForm.currency : null,\n maxUses: createForm.maxUses ? Number(createForm.maxUses) : null,\n validFrom: createForm.validFrom ? new Date(createForm.validFrom).toISOString() : null,\n validUntil: createForm.validUntil ? new Date(createForm.validUntil).toISOString() : null,\n },\n });\n await refetch();\n closeModal();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to create discount.');\n }\n };\n\n const handleUpdate = async (e: FormEvent) => {\n e.preventDefault();\n if (modal.kind !== 'edit') return;\n setMutationError(null);\n\n try {\n await updateDiscount({\n variables: {\n id: modal.discount.id,\n maxUses: editForm.maxUses ? Number(editForm.maxUses) : null,\n validUntil: editForm.validUntil ? new Date(editForm.validUntil).toISOString() : null,\n },\n });\n await refetch();\n closeModal();\n } catch (err) {\n setMutationError(err instanceof Error ? err.message : 'Failed to update discount.');\n }\n };\n\n const handleDelete = async () => {\n if (modal.kind !== 'delete') return;\n setMutationError(null);\n void nativeImpact('medium');\n\n try {\n await deleteDiscount({ variables: { id: modal.discount.id } });\n void nativeNotify('success');\n await refetch();\n closeModal();\n } catch (err) {\n void nativeNotify('error');\n setMutationError(err instanceof Error ? err.message : 'Failed to delete discount.');\n }\n };\n\n const handleBulkDelete = async () => {\n if (selectedDiscounts.length === 0) return;\n\n setMutationError(null);\n setShowBulkDeleteConfirm(false);\n await Promise.allSettled(\n selectedDiscounts.map((discount) => deleteDiscount({ variables: { id: discount.id } }))\n );\n await refetch();\n setSelectedDiscountIds(new Set());\n };\n\n const toggleDiscountSelection = (discountId: string, checked: boolean) => {\n setSelectedDiscountIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(discountId);\n } else {\n next.delete(discountId);\n }\n return next;\n });\n setShowBulkDeleteConfirm(false);\n };\n\n const toggleAllDiscounts = (checked: boolean) => {\n setSelectedDiscountIds((prev) => {\n const next = new Set(prev);\n filteredDiscounts.forEach((discount) => {\n if (checked) {\n next.add(discount.id);\n } else {\n next.delete(discount.id);\n }\n });\n return next;\n });\n setShowBulkDeleteConfirm(false);\n };\n\n // ── Loading state ───────────────────────────────────────────────────\n\n if (loading && discounts.length === 0) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n {[1, 2, 3].map((i) => (\n <div key={i} className=\"border border-border-subtle rounded-lg p-4 space-y-2\">\n <div className=\"h-4 w-24 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-6 w-16 bg-bg-sunken animate-pulse rounded\" />\n </div>\n ))}\n </div>\n <div className=\"space-y-3\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"border border-border-subtle rounded-lg p-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"size-10 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-32 bg-bg-sunken animate-pulse rounded\" />\n <div className=\"h-3 w-48 bg-bg-sunken animate-pulse rounded\" />\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // ── Error state ─────────────────────────────────────────────────────\n\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"size-12 mx-auto rounded-full bg-status-error-bg-subtle flex items-center justify-center\">\n <svg\n className=\"size-6 text-status-error-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Failed to load discounts</h2>\n <p className=\"text-sm text-text-muted\">{error.message}</p>\n <button\n type=\"button\"\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n }\n\n // ── Main render ─────────────────────────────────────────────────────\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl md:text-3xl font-semibold tracking-tight text-text-primary\">\n Discount Codes\n </h1>\n <p className=\"text-sm text-text-secondary mt-1\">\n Create, view, and manage discount codes for billing\n </p>\n </div>\n <button\n type=\"button\"\n onClick={openCreateModal}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 4v16m8-8H4\" />\n </svg>\n Create Discount\n </button>\n </div>\n\n {/* Summary Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-3 gap-4\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-5 shadow-[var(--shadow-elevation-1)]\">\n <p className=\"text-sm text-text-secondary\">Total Discounts</p>\n <p className=\"text-2xl font-semibold text-text-primary tabular-nums mt-1\">\n {totalDiscounts}\n </p>\n </div>\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-5 shadow-[var(--shadow-elevation-1)]\">\n <p className=\"text-sm text-text-secondary\">Active</p>\n <p className=\"text-2xl font-semibold text-status-success-text tabular-nums mt-1\">\n {activeDiscounts}\n </p>\n </div>\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-5 shadow-[var(--shadow-elevation-1)]\">\n <p className=\"text-sm text-text-secondary\">Expired</p>\n <p className=\"text-2xl font-semibold text-status-warning-text tabular-nums mt-1\">\n {expiredDiscounts}\n </p>\n </div>\n </div>\n\n {/* Search */}\n <div className=\"flex flex-col sm:flex-row flex-wrap items-stretch sm:items-center gap-3 sm:gap-4 pb-4 border-b border-border-subtle\">\n <div className=\"relative flex-1 min-w-0 w-full sm:max-w-md\">\n <svg\n className=\"absolute left-3 top-1/2 -translate-y-1/2 size-4 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z\"\n />\n </svg>\n <input\n type=\"text\"\n placeholder=\"Search by discount code...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n </div>\n </div>\n\n {filteredDiscounts.length > 0 && (\n <div className=\"flex flex-col gap-3 rounded-lg border border-border-subtle bg-bg-surface p-4 sm:flex-row sm:items-center sm:justify-between\">\n <label className=\"flex items-center gap-2 text-sm text-text-primary\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllDiscounts(event.target.checked)}\n aria-label=\"Select all discounts\"\n className=\"size-4 rounded border-border-subtle text-text-link\"\n />\n <span>Select all visible</span>\n </label>\n\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-text-muted\">{selectedDiscountIds.size} selected</span>\n {selectedDiscountIds.size > 0 && (\n <button\n type=\"button\"\n onClick={() => {\n setSelectedDiscountIds(new Set());\n setShowBulkDeleteConfirm(false);\n }}\n className=\"text-sm text-text-muted hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => setShowBulkDeleteConfirm(true)}\n disabled={selectedDiscounts.length === 0 || isDeleting}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium border border-status-error-border text-status-error-text rounded-md hover:bg-status-error-bg-subtle transition-colors disabled:opacity-50\"\n >\n Delete Selected{selectedDiscounts.length > 0 ? ` (${selectedDiscounts.length})` : ''}\n </button>\n </div>\n </div>\n )}\n\n {showBulkDeleteConfirm && selectedDiscounts.length > 0 && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-4\">\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n Delete {selectedDiscounts.length} selected discount\n {selectedDiscounts.length === 1 ? '' : 's'}?\n </p>\n <p className=\"text-sm text-text-muted\">This action cannot be undone.</p>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setShowBulkDeleteConfirm(false)}\n className=\"px-3 py-2 text-sm font-medium text-text-muted hover:text-text-primary\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleBulkDelete}\n disabled={isDeleting}\n className=\"inline-flex items-center gap-2 rounded-button bg-action-danger-bg px-3 py-2 text-sm font-medium text-action-danger-text hover:bg-action-danger-bgHover disabled:opacity-50\"\n >\n Confirm Delete\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Discounts Table */}\n {filteredDiscounts.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-16 border border-dashed border-border-seam rounded-card bg-bg-surface\">\n <div className=\"size-12 rounded-full bg-[var(--color-accent-soft)] flex items-center justify-center mb-4\">\n <svg\n className=\"size-6 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A2 2 0 013 12V7a4 4 0 014-4z\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-text-primary\">No discount codes found</h3>\n <p className=\"text-sm text-text-muted mt-1\">\n {searchQuery\n ? 'No discount codes match your search.'\n : 'Create your first discount code to get started.'}\n </p>\n {!searchQuery && (\n <button\n type=\"button\"\n onClick={openCreateModal}\n className=\"mt-4 px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors\"\n >\n Create your first discount\n </button>\n )}\n </div>\n ) : (\n <div className=\"border border-border-seam rounded-card overflow-hidden shadow-[var(--shadow-elevation-1)]\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead className=\"sticky top-0 z-10 bg-bg-sunken border-b border-border-subtle\">\n <tr>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted w-12\">\n <input\n type=\"checkbox\"\n checked={allVisibleSelected}\n ref={(input) => {\n if (input) {\n input.indeterminate = partiallySelected && !allVisibleSelected;\n }\n }}\n onChange={(event) => toggleAllDiscounts(event.target.checked)}\n aria-label=\"Select all discounts\"\n className=\"size-4 rounded border-border-subtle text-action-primary-bg focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n />\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Code\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Type\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Value\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted hidden md:table-cell\">\n Uses\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted hidden lg:table-cell\">\n Valid From\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted hidden sm:table-cell\">\n Valid Until\n </th>\n <th className=\"text-left px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Status\n </th>\n <th className=\"text-right px-4 py-3 text-xs font-medium uppercase tracking-wide text-text-muted\">\n Actions\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border-subtle\">\n {filteredDiscounts.map((discount: BillingDiscount) => (\n <DiscountRow\n key={discount.id}\n discount={discount}\n selected={selectedDiscountIds.has(discount.id)}\n onToggleSelect={toggleDiscountSelection}\n onEdit={openEditModal}\n onDelete={openDeleteModal}\n />\n ))}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n {/* Create Modal */}\n {modal.kind === 'create' && (\n <ModalOverlay onClose={closeModal}>\n <form onSubmit={handleCreate} className=\"space-y-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('billing.discounts.createDiscountCode', 'Create Discount Code')}\n </h2>\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <svg\n className=\"size-5 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n\n {mutationError && (\n <div className=\"p-3 text-sm text-status-error-text bg-status-error-bg-subtle border border-status-error-border rounded-md\">\n {mutationError}\n </div>\n )}\n\n {/* Code */}\n <div>\n <label\n htmlFor=\"create-code\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.code', 'Code')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <input\n id=\"create-code\"\n type=\"text\"\n required\n value={createForm.code}\n onChange={(e) => setCreateForm((prev) => ({ ...prev, code: e.target.value }))}\n placeholder={tr('billing.discounts.codePlaceholder', 'e.g. SUMMER25')}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent uppercase\"\n />\n </div>\n\n {/* Type + Value row */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"create-type\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.type', 'Type')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <select\n id=\"create-type\"\n value={createForm.type}\n onChange={(e) =>\n setCreateForm((prev) => ({\n ...prev,\n type: e.target.value as DiscountType,\n }))\n }\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n >\n <option value=\"PERCENTAGE\">\n {tr('billing.discounts.typePercentage', 'Percentage')}\n </option>\n <option value=\"FIXED\">\n {tr('billing.discounts.typeFixedAmount', 'Fixed Amount')}\n </option>\n </select>\n </div>\n <div>\n <label\n htmlFor=\"create-value\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.value', 'Value')}{' '}\n <span className=\"text-status-error-text\">*</span>\n </label>\n <div className=\"relative\">\n <input\n id=\"create-value\"\n type=\"number\"\n required\n min=\"0.01\"\n max={createForm.type === 'PERCENTAGE' ? '100' : undefined}\n step=\"0.01\"\n value={createForm.value}\n onChange={(e) => setCreateForm((prev) => ({ ...prev, value: e.target.value }))}\n placeholder={createForm.type === 'PERCENTAGE' ? '10' : '25.00'}\n className=\"w-full px-3 py-2 pr-8 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n <span className=\"absolute right-3 top-1/2 -translate-y-1/2 text-sm text-text-muted\">\n {createForm.type === 'PERCENTAGE' ? '%' : createForm.currency}\n </span>\n </div>\n </div>\n </div>\n\n {/* Currency (only for FIXED) */}\n {createForm.type === 'FIXED' && (\n <div>\n <label\n htmlFor=\"create-currency\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.currency', 'Currency')}\n </label>\n <select\n id=\"create-currency\"\n value={createForm.currency}\n onChange={(e) => setCreateForm((prev) => ({ ...prev, currency: e.target.value }))}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n >\n <option value=\"USD\">USD</option>\n <option value=\"EUR\">EUR</option>\n <option value=\"GBP\">GBP</option>\n <option value=\"INR\">INR</option>\n <option value=\"CAD\">CAD</option>\n <option value=\"AUD\">AUD</option>\n </select>\n </div>\n )}\n\n {/* Max Uses */}\n <div>\n <label\n htmlFor=\"create-max-uses\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.maxUses', 'Max Uses')}\n </label>\n <input\n id=\"create-max-uses\"\n type=\"number\"\n min=\"1\"\n step=\"1\"\n value={createForm.maxUses}\n onChange={(e) => setCreateForm((prev) => ({ ...prev, maxUses: e.target.value }))}\n placeholder={tr('billing.discounts.unlimited', 'Unlimited')}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n {tr('billing.discounts.unlimitedHelp', 'Leave empty for unlimited uses.')}\n </p>\n </div>\n\n {/* Valid From / Valid Until row */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label\n htmlFor=\"create-valid-from\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.validFrom', 'Valid From')}\n </label>\n <input\n id=\"create-valid-from\"\n type=\"datetime-local\"\n value={createForm.validFrom}\n onChange={(e) =>\n setCreateForm((prev) => ({ ...prev, validFrom: e.target.value }))\n }\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n </div>\n <div>\n <label\n htmlFor=\"create-valid-until\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n {tr('billing.discounts.validUntil', 'Valid Until')}\n </label>\n <input\n id=\"create-valid-until\"\n type=\"datetime-local\"\n value={createForm.validUntil}\n onChange={(e) =>\n setCreateForm((prev) => ({ ...prev, validUntil: e.target.value }))\n }\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 pt-2 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n {tr('billing.discounts.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isCreating}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isCreating\n ? tr('billing.discounts.creating', 'Creating...')\n : tr('billing.discounts.createDiscount', 'Create Discount')}\n </button>\n </div>\n </form>\n </ModalOverlay>\n )}\n\n {/* Edit Modal */}\n {modal.kind === 'edit' && (\n <ModalOverlay onClose={closeModal}>\n <form onSubmit={handleUpdate} className=\"space-y-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Edit Discount</h2>\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <svg\n className=\"size-5 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n\n {mutationError && (\n <div className=\"p-3 text-sm text-status-error-text bg-status-error-bg-subtle border border-status-error-border rounded-md\">\n {mutationError}\n </div>\n )}\n\n {/* Read-only info */}\n <div className=\"p-3 bg-bg-sunken/50 border border-border-subtle rounded-md space-y-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Code</span>\n <span className=\"font-mono font-medium text-text-primary\">\n {modal.discount.code}\n </span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Type</span>\n <span className=\"text-text-primary\">\n {modal.discount.type === 'PERCENTAGE' ? 'Percentage' : 'Fixed Amount'}\n </span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Value</span>\n <span className=\"text-text-primary\">{formatDiscountValue(modal.discount)}</span>\n </div>\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-muted\">Used</span>\n <span className=\"text-text-primary\">{modal.discount.usedCount} times</span>\n </div>\n </div>\n\n {/* Editable: Max Uses */}\n <div>\n <label\n htmlFor=\"edit-max-uses\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Max Uses\n </label>\n <input\n id=\"edit-max-uses\"\n type=\"number\"\n min=\"1\"\n step=\"1\"\n value={editForm.maxUses}\n onChange={(e) => setEditForm((prev) => ({ ...prev, maxUses: e.target.value }))}\n placeholder=\"Unlimited\"\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n <p className=\"text-xs text-text-muted mt-1\">Leave empty for unlimited uses.</p>\n </div>\n\n {/* Editable: Valid Until */}\n <div>\n <label\n htmlFor=\"edit-valid-until\"\n className=\"block text-sm font-medium text-text-primary mb-1\"\n >\n Valid Until\n </label>\n <input\n id=\"edit-valid-until\"\n type=\"datetime-local\"\n value={editForm.validUntil}\n onChange={(e) => setEditForm((prev) => ({ ...prev, validUntil: e.target.value }))}\n className=\"w-full px-3 py-2 text-sm border border-border-subtle rounded-input bg-bg-surface text-text-primary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] focus:border-transparent\"\n />\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 pt-2 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isUpdating}\n className=\"px-4 py-2 text-sm font-medium bg-action-primary-bg text-action-primary-text rounded-button hover:bg-action-primary-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isUpdating ? 'Saving...' : 'Save Changes'}\n </button>\n </div>\n </form>\n </ModalOverlay>\n )}\n\n {/* Delete Confirmation Modal */}\n {modal.kind === 'delete' && (\n <ModalOverlay onClose={closeModal}>\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between mb-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Delete Discount</h2>\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <svg\n className=\"size-5 text-text-muted\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n </div>\n\n {mutationError && (\n <div className=\"p-3 text-sm text-status-error-text bg-status-error-bg-subtle border border-status-error-border rounded-md\">\n {mutationError}\n </div>\n )}\n\n <div className=\"flex items-start gap-3\">\n <div className=\"size-10 rounded-full bg-status-error-bg-subtle flex items-center justify-center shrink-0\">\n <svg\n className=\"size-5 text-status-error-text\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n />\n </svg>\n </div>\n <div>\n <p className=\"text-sm text-text-primary\">\n Are you sure you want to delete the discount code{' '}\n <span className=\"font-mono font-semibold\">{modal.discount.code}</span>?\n </p>\n <p className=\"text-sm text-text-muted mt-1\">\n This action cannot be undone. The discount has been used{' '}\n <span className=\"font-medium\">{modal.discount.usedCount}</span> time\n {modal.discount.usedCount !== 1 ? 's' : ''}.\n </p>\n </div>\n </div>\n\n <div className=\"flex items-center justify-end gap-3 pt-2 border-t border-border-subtle\">\n <button\n type=\"button\"\n onClick={closeModal}\n className=\"px-4 py-2 text-sm font-medium border border-border-subtle text-text-primary rounded-button hover:bg-bg-sunken transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleDelete}\n disabled={isDeleting}\n className=\"px-4 py-2 text-sm font-medium bg-action-danger-bg text-action-danger-text rounded-button hover:bg-action-danger-bgHover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n {isDeleting ? 'Deleting...' : 'Delete Discount'}\n </button>\n </div>\n </div>\n </ModalOverlay>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Discount Row Component\n// ============================================================================\n\ninterface DiscountRowProps {\n discount: BillingDiscount;\n selected: boolean;\n onToggleSelect: (discountId: string, checked: boolean) => void;\n onEdit: (discount: BillingDiscount) => void;\n onDelete: (discount: BillingDiscount) => void;\n}\n\nconst DiscountRow: FC<DiscountRowProps> = ({\n discount,\n selected,\n onToggleSelect,\n onEdit,\n onDelete,\n}) => {\n const expired = isExpired(discount);\n\n return (\n <tr className={`hover:bg-bg-sunken/30 transition-colors ${selected ? 'bg-bg-sunken/20' : ''}`}>\n <td className=\"p-4\">\n <input\n type=\"checkbox\"\n checked={selected}\n onChange={(event) => onToggleSelect(discount.id, event.target.checked)}\n aria-label={`Select discount ${discount.code}`}\n className=\"size-4 rounded border-border-subtle text-text-link\"\n />\n </td>\n {/* Code */}\n <td className=\"p-4\">\n <span className=\"font-mono font-medium text-text-primary\">{discount.code}</span>\n </td>\n\n {/* Type */}\n <td className=\"p-4\">\n <span className=\"text-sm text-text-muted\">\n {discount.type === 'PERCENTAGE' ? 'Percentage' : 'Fixed'}\n </span>\n </td>\n\n {/* Value */}\n <td className=\"p-4\">\n <span className=\"text-sm font-medium text-text-primary\">\n {formatDiscountValue(discount)}\n </span>\n </td>\n\n {/* Uses */}\n <td className=\"p-4 hidden md:table-cell\">\n <span className=\"text-sm text-text-muted\">\n {discount.usedCount}\n {discount.maxUses != null ? ` / ${discount.maxUses}` : ' / \\u221E'}\n </span>\n </td>\n\n {/* Valid From */}\n <td className=\"p-4 text-sm text-text-muted hidden lg:table-cell\">\n {discount.validFrom ? formatDate(discount.validFrom, 'short') : '\\u2014'}\n </td>\n\n {/* Valid Until */}\n <td className=\"p-4 text-sm text-text-muted hidden sm:table-cell\">\n {discount.validUntil ? formatDate(discount.validUntil, 'short') : '\\u2014'}\n </td>\n\n {/* Status */}\n <td className=\"p-4\">\n {expired ? (\n <span className=\"inline-flex px-2 py-1 text-xs font-medium rounded bg-status-warning-bg-subtle text-status-warning-text\">\n Expired\n </span>\n ) : (\n <span className=\"inline-flex px-2 py-1 text-xs font-medium rounded bg-status-success-bg-subtle text-status-success-text\">\n Active\n </span>\n )}\n </td>\n\n {/* Actions */}\n <td className=\"p-4 text-right\">\n <div className=\"flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => onEdit(discount)}\n className=\"px-3 py-1.5 text-sm font-medium text-text-primary hover:bg-bg-sunken rounded-button transition-colors\"\n title=\"Edit discount\"\n >\n {/* Pencil icon */}\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n onClick={() => onDelete(discount)}\n className=\"px-3 py-1.5 text-sm font-medium text-status-error-text hover:bg-status-error-bg-subtle rounded-md transition-colors\"\n title=\"Delete discount\"\n >\n {/* Trash icon */}\n <svg className=\"size-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16\"\n />\n </svg>\n </button>\n </div>\n </td>\n </tr>\n );\n};\n\n// ============================================================================\n// Modal Overlay Component\n// ============================================================================\n\ninterface ModalOverlayProps {\n onClose: () => void;\n children: React.ReactNode;\n}\n\nconst ModalOverlay: FC<ModalOverlayProps> = ({ onClose, children }) => {\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <div className=\"absolute inset-0 bg-bg-overlay\" onClick={onClose} />\n\n {/* Dialog */}\n <div className=\"relative w-full max-w-lg max-h-[85vh] overflow-y-auto bg-bg-elevated border border-border-seam rounded-card shadow-[var(--shadow-elevation-4)] p-6 mx-4\">\n {children}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -149,7 +149,7 @@ function B({ account: e }) {
149
149
  function V({ request: e, onApprove: t, onReject: n, onMarkPaid: r }) {
150
150
  let i = e.status === "REQUESTED" || e.status === "REJECTED", a = e.status === "REQUESTED" || e.status === "APPROVED", c = e.status === "APPROVED" || e.status === "PROCESSING";
151
151
  return /* @__PURE__ */ A("div", {
152
- className: "rounded-card border border-border-subtle bg-bg-surface p-5 space-y-4 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:shadow-[var(--shadow-elevation-2)]",
152
+ className: "rounded-card border border-border-seam bg-bg-surface p-5 space-y-4 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:shadow-[var(--shadow-elevation-2)]",
153
153
  children: [
154
154
  /* @__PURE__ */ A("div", {
155
155
  className: "flex flex-wrap items-start justify-between gap-3",
@@ -264,7 +264,7 @@ function H({ summary: e }) {
264
264
  className: "grid gap-3 sm:grid-cols-3",
265
265
  children: [
266
266
  /* @__PURE__ */ A("div", {
267
- className: "rounded-card border border-border-subtle bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]",
267
+ className: "rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]",
268
268
  children: [
269
269
  /* @__PURE__ */ k("div", {
270
270
  className: "text-xs text-text-muted uppercase tracking-wide",
@@ -281,7 +281,7 @@ function H({ summary: e }) {
281
281
  ]
282
282
  }),
283
283
  /* @__PURE__ */ A("div", {
284
- className: "rounded-card border border-border-subtle bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]",
284
+ className: "rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]",
285
285
  children: [/* @__PURE__ */ k("div", {
286
286
  className: "text-xs text-text-muted uppercase tracking-wide",
287
287
  children: "Processing"
@@ -291,7 +291,7 @@ function H({ summary: e }) {
291
291
  })]
292
292
  }),
293
293
  /* @__PURE__ */ A("div", {
294
- className: "rounded-card border border-border-subtle bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]",
294
+ className: "rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]",
295
295
  children: [
296
296
  /* @__PURE__ */ k("div", {
297
297
  className: "text-xs text-text-muted uppercase tracking-wide",
@@ -1 +1 @@
1
- {"version":3,"file":"PayoutAdminPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/PayoutAdminPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type FC } from 'react';\nimport {\n AlertTriangle,\n Banknote,\n CheckCircle2,\n ChevronDown,\n ChevronUp,\n Clock,\n CreditCard,\n RefreshCw,\n ReceiptText,\n XCircle,\n} from 'lucide-react';\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, EmptyState, PageHeader, ServerError } from '../../../shared/components';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport {\n approvePayoutRequest,\n fetchAdminPayoutRequests,\n fetchAdminPayoutSummary,\n markPayoutPaid,\n rejectPayoutRequest,\n type PayoutAdminSummary,\n type PayoutRequest,\n} from '../api';\n\ntype Tab = 'pending' | 'approved' | 'history';\n\ninterface ActionDialogState {\n type: 'approve' | 'reject' | 'mark_paid';\n requestId: string;\n amount: number;\n currency: string;\n accountLabel: string;\n}\n\nconst STATUS_LABEL: Record<string, string> = {\n REQUESTED: 'Requested',\n APPROVED: 'Approved',\n REJECTED: 'Rejected',\n PROCESSING: 'Processing',\n PAID: 'Paid',\n FAILED: 'Failed',\n CANCELED: 'Canceled',\n};\n\nconst STATUS_COLORS: Record<string, string> = {\n REQUESTED: 'bg-status-warning-bg-subtle text-status-warning-text',\n APPROVED: 'bg-status-info-bg-subtle text-status-info-text',\n REJECTED: 'bg-status-error-bg-subtle text-status-error-text',\n PROCESSING: 'bg-status-info-bg-subtle text-status-info-text',\n PAID: 'bg-status-success-bg-subtle text-status-success-text',\n FAILED: 'bg-status-error-bg-subtle text-status-error-text',\n CANCELED: 'bg-bg-sunken text-text-muted',\n};\n\nfunction StatusBadge({ status }: { status: string }) {\n return (\n <span\n className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${STATUS_COLORS[status] ?? 'bg-bg-sunken text-text-muted'}`}\n >\n {STATUS_LABEL[status] ?? status}\n </span>\n );\n}\n\nfunction AccountDetails({ account }: { account: NonNullable<PayoutRequest['payoutAccount']> }) {\n const [expanded, setExpanded] = useState(false);\n const details = account.accountDetails as Record<string, string | null> | null;\n\n return (\n <div className=\"rounded-md border border-border-subtle bg-bg-sunken/40 p-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 text-text-primary font-medium\">\n <CreditCard className=\"size-4 text-text-muted\" />\n {account.displayName ?? account.label}\n <span className=\"text-xs text-text-muted font-normal\">\n ({account.type === 'UPI' ? 'UPI' : 'Bank'} · {account.country ?? 'Unknown'} ·{' '}\n {account.currency})\n </span>\n </div>\n {details && (\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"text-text-muted hover:text-text-primary\"\n >\n {expanded ? <ChevronUp className=\"size-4\" /> : <ChevronDown className=\"size-4\" />}\n </button>\n )}\n </div>\n {expanded && details && (\n <div className=\"mt-2 grid gap-1 text-xs\">\n {details.accountHolderName && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Account holder</span>\n <span className=\"text-text-primary\">{details.accountHolderName}</span>\n </div>\n )}\n {details.bankName && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Bank</span>\n <span className=\"text-text-primary\">{details.bankName}</span>\n </div>\n )}\n {account.accountLast4 && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Account (last 4)</span>\n <span className=\"text-text-primary font-mono\">••••{account.accountLast4}</span>\n </div>\n )}\n {details.ifsc && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">IFSC</span>\n <span className=\"text-text-primary font-mono\">{details.ifsc}</span>\n </div>\n )}\n {details.routingNumberLast4 && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Routing (last 4)</span>\n <span className=\"text-text-primary font-mono\">••••{details.routingNumberLast4}</span>\n </div>\n )}\n {details.upiIdMasked && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">UPI ID</span>\n <span className=\"text-text-primary font-mono\">{details.upiIdMasked}</span>\n </div>\n )}\n {details.swift && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">SWIFT</span>\n <span className=\"text-text-primary font-mono\">{details.swift}</span>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction RequestCard({\n request,\n onApprove,\n onReject,\n onMarkPaid,\n}: {\n request: PayoutRequest;\n onApprove: (r: PayoutRequest) => void;\n onReject: (r: PayoutRequest) => void;\n onMarkPaid: (r: PayoutRequest) => void;\n}) {\n const canApprove = request.status === 'REQUESTED' || request.status === 'REJECTED';\n const canReject = request.status === 'REQUESTED' || request.status === 'APPROVED';\n const canMarkPaid = request.status === 'APPROVED' || request.status === 'PROCESSING';\n\n return (\n <div className=\"rounded-card border border-border-subtle bg-bg-surface p-5 space-y-4 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:shadow-[var(--shadow-elevation-2)]\">\n {/* Header row */}\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xl font-semibold text-text-primary\">\n {formatCurrency(request.requestedAmount, request.currency)}\n </span>\n <StatusBadge status={request.status} />\n </div>\n <div className=\"text-xs text-text-muted space-x-2\">\n <span>Requested {formatDateTime(request.createdAt)}</span>\n {request.requesterUserId && (\n <>\n <span>·</span>\n <span>by {request.requesterUserId}</span>\n </>\n )}\n <span>·</span>\n <span className=\"font-mono text-xs\">{request.id.slice(0, 8)}…</span>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {canApprove && (\n <button\n type=\"button\"\n onClick={() => onApprove(request)}\n className=\"inline-flex items-center gap-1.5 rounded-button bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text\"\n >\n <CheckCircle2 className=\"size-3.5\" />\n Approve\n </button>\n )}\n {canMarkPaid && (\n <button\n type=\"button\"\n onClick={() => onMarkPaid(request)}\n className=\"inline-flex items-center gap-1.5 rounded-button bg-status-success-bg-subtle border border-status-success-border px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle/70\"\n >\n <Banknote className=\"size-3.5\" />\n Mark as Paid\n </button>\n )}\n {canReject && (\n <button\n type=\"button\"\n onClick={() => onReject(request)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text\"\n >\n <XCircle className=\"size-3.5\" />\n Reject\n </button>\n )}\n </div>\n </div>\n\n {/* Payout account */}\n {request.payoutAccount && <AccountDetails account={request.payoutAccount} />}\n\n {/* User notes */}\n {request.userNotes && (\n <div className=\"rounded-md bg-bg-sunken/40 border border-border-subtle px-3 py-2 text-sm\">\n <span className=\"text-xs font-medium text-text-muted uppercase tracking-wide\">\n User note\n </span>\n <p className=\"mt-1 text-text-primary\">{request.userNotes}</p>\n </div>\n )}\n\n {/* Internal notes / failure / ref */}\n {(request.internalNotes ?? request.failureReason ?? request.externalReference) && (\n <div className=\"grid gap-1 text-xs text-text-muted\">\n {request.externalReference && (\n <div className=\"flex gap-2\">\n <ReceiptText className=\"size-3.5 mt-px shrink-0\" />\n <span>\n Ref:{' '}\n <span className=\"font-mono text-text-primary\">{request.externalReference}</span>\n </span>\n </div>\n )}\n {request.internalNotes && (\n <div className=\"flex gap-2\">\n <span className=\"shrink-0\">Internal note:</span>\n <span className=\"text-text-primary\">{request.internalNotes}</span>\n </div>\n )}\n {request.failureReason && (\n <div className=\"flex gap-2 text-status-error-text\">\n <AlertTriangle className=\"size-3.5 mt-px shrink-0\" />\n <span>{request.failureReason}</span>\n </div>\n )}\n </div>\n )}\n\n {/* Timestamps */}\n {(request.paidAt ?? request.processedAt) && (\n <div className=\"flex flex-wrap gap-4 text-xs text-text-muted\">\n {request.processedAt && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n Processed {formatDateTime(request.processedAt)}\n </span>\n )}\n {request.paidAt && (\n <span className=\"flex items-center gap-1\">\n <CheckCircle2 className=\"size-3\" />\n Paid {formatDateTime(request.paidAt)}\n </span>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction SummaryBar({ summary }: { summary: PayoutAdminSummary }) {\n return (\n <div className=\"grid gap-3 sm:grid-cols-3\">\n <div className=\"rounded-card border border-border-subtle bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Pending payout</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalRequestedAmount, 'USD')}\n </div>\n <div className=\"text-xs text-text-muted mt-0.5\">{summary.pendingCount} requests</div>\n </div>\n <div className=\"rounded-card border border-border-subtle bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Processing</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalProcessingAmount, 'USD')}\n </div>\n </div>\n <div className=\"rounded-card border border-border-subtle bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Total paid</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalPaidAmount, 'USD')}\n </div>\n {summary.failedCount > 0 && (\n <div className=\"text-xs text-status-error-text mt-0.5\">{summary.failedCount} failed</div>\n )}\n </div>\n </div>\n );\n}\n\nfunction ActionDialog({\n dialog,\n onClose,\n onConfirm,\n}: {\n dialog: ActionDialogState;\n onClose: () => void;\n onConfirm: (data: {\n notes?: string;\n reason?: string;\n externalReference?: string;\n }) => Promise<void>;\n}) {\n const [notes, setNotes] = useState('');\n const [reason, setReason] = useState('');\n const [txnRef, setTxnRef] = useState('');\n const [submitting, setSubmitting] = useState(false);\n\n const handleSubmit = async () => {\n if (dialog.type === 'reject' && !reason.trim()) return;\n setSubmitting(true);\n await onConfirm({\n notes: notes.trim() || undefined,\n reason: reason.trim() || undefined,\n externalReference: txnRef.trim() || undefined,\n });\n setSubmitting(false);\n };\n\n const titles = {\n approve: 'Approve withdrawal request',\n reject: 'Reject withdrawal request',\n mark_paid: 'Mark as paid',\n };\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !submitting) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{titles[dialog.type]}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {formatCurrency(dialog.amount, dialog.currency)} · {dialog.accountLabel}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n {dialog.type === 'mark_paid' && (\n <div className=\"space-y-3\">\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Transaction reference / UTR number\n </label>\n <input\n type=\"text\"\n value={txnRef}\n onChange={(e) => setTxnRef(e.target.value)}\n placeholder=\"e.g. UTR123456789012, TXN_abc123\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Enter the bank reference number so the user can verify the transfer.\n </p>\n </div>\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Internal notes (optional)\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={2}\n placeholder=\"Any internal notes about this payment…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm 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 </div>\n )}\n\n {dialog.type === 'approve' && (\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Internal notes (optional)\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={3}\n placeholder=\"e.g. Verified bank details, approved for transfer…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm 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\n {dialog.type === 'reject' && (\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Reason for rejection <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n rows={3}\n placeholder=\"e.g. Invalid bank details provided, please re-submit with correct IFSC…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm 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\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={submitting}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n disabled={submitting || (dialog.type === 'reject' && !reason.trim())}\n className={`rounded-md px-4 py-2 text-sm font-medium disabled:opacity-50 ${\n dialog.type === 'reject'\n ? 'bg-action-danger-bg text-action-danger-text'\n : 'bg-action-primary-bg text-action-primary-text'\n }`}\n >\n {submitting\n ? 'Saving…'\n : dialog.type === 'approve'\n ? 'Approve'\n : dialog.type === 'reject'\n ? 'Reject'\n : 'Confirm Payment'}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nexport const PayoutAdminPage: FC = () => {\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n const [summary, setSummary] = useState<PayoutAdminSummary | null>(null);\n const [requests, setRequests] = useState<PayoutRequest[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [activeTab, setActiveTab] = useState<Tab>('pending');\n const [dialog, setDialog] = useState<ActionDialogState | null>(null);\n\n const requestContext = useMemo(\n () => ({ apiGatewayUrl, authToken, orgId }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [loadedSummary, loadedRequests] = await Promise.all([\n fetchAdminPayoutSummary(requestContext),\n fetchAdminPayoutRequests(requestContext),\n ]);\n setSummary(loadedSummary);\n setRequests(loadedRequests);\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load payout queue'));\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [requestContext]);\n\n if (!permissions.isBillingAdmin) {\n return <AccessDenied message=\"You don't have permission to manage payouts.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Payout queue unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n const pending = requests.filter((r) => r.status === 'REQUESTED');\n const approved = requests.filter((r) => r.status === 'APPROVED' || r.status === 'PROCESSING');\n const history = requests.filter((r) =>\n ['PAID', 'REJECTED', 'FAILED', 'CANCELED'].includes(r.status)\n );\n\n const tabItems: { id: Tab; label: string; count: number }[] = [\n { id: 'pending', label: 'Pending', count: pending.length },\n { id: 'approved', label: 'Approved', count: approved.length },\n { id: 'history', label: 'History', count: history.length },\n ];\n\n const visibleRequests =\n activeTab === 'pending' ? pending : activeTab === 'approved' ? approved : history;\n\n const openDialog = (type: ActionDialogState['type'], request: PayoutRequest) => {\n setDialog({\n type,\n requestId: request.id,\n amount: request.requestedAmount,\n currency: request.currency,\n accountLabel:\n request.payoutAccount?.displayName ?? request.payoutAccount?.label ?? 'Unknown account',\n });\n };\n\n const handleConfirm = async (data: {\n notes?: string;\n reason?: string;\n externalReference?: string;\n }) => {\n if (!dialog) return;\n\n try {\n if (dialog.type === 'approve') {\n await approvePayoutRequest(dialog.requestId, data.notes ?? null, requestContext);\n emit('billing.payout.approved', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n });\n } else if (dialog.type === 'reject') {\n await rejectPayoutRequest(dialog.requestId, data.reason ?? '', requestContext);\n emit('billing.payout.rejected', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n reason: data.reason,\n });\n } else if (dialog.type === 'mark_paid') {\n await markPayoutPaid(\n dialog.requestId,\n data.externalReference ?? null,\n data.notes ?? null,\n requestContext\n );\n emit('billing.payout.processed', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n });\n }\n setDialog(null);\n await loadData();\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Action failed'));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Payout queue\"\n description=\"Review withdrawal requests, verify bank details, and mark payments as paid once transferred.\"\n actions={\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n }\n />\n\n {summary && !loading && <SummaryBar summary={summary} />}\n\n {loading && (\n <div className=\"grid gap-3 sm:grid-cols-3\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-20 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n )}\n\n {error && !isServerError(error) && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-4 text-sm text-status-error-text\">\n {error.message}\n </div>\n )}\n\n {/* Tabs */}\n <div className=\"border-b border-border-subtle\">\n <div className=\"flex gap-0\">\n {tabItems.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n onClick={() => setActiveTab(tab.id)}\n className={`flex items-center gap-2 border-b-2 px-4 py-2.5 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-action-primary-bg text-text-primary'\n : 'border-transparent text-text-muted hover:text-text-primary'\n }`}\n >\n {tab.label}\n {tab.count > 0 && (\n <span\n className={`rounded-full px-1.5 py-0.5 text-xs ${\n activeTab === tab.id\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {tab.count}\n </span>\n )}\n </button>\n ))}\n </div>\n </div>\n\n {/* How to pay — shown only on the Approved tab */}\n {activeTab === 'approved' && approved.length > 0 && (\n <div className=\"rounded-card border border-status-info-border bg-status-info-bg-subtle px-4 py-3 text-sm text-status-info-text\">\n <strong>How to pay:</strong> Click the account row to expand bank/UPI details → transfer\n the exact amount via your bank → click <strong>Mark as Paid</strong> and enter the UTR /\n transaction reference number.\n </div>\n )}\n\n {/* Request cards */}\n {loading ? (\n <div className=\"space-y-4\">\n {[1, 2].map((i) => (\n <div\n key={i}\n className=\"h-40 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n ) : visibleRequests.length === 0 ? (\n <EmptyState\n icon={<CheckCircle2 className=\"size-8\" />}\n title={\n activeTab === 'pending'\n ? 'No pending requests'\n : activeTab === 'approved'\n ? 'No approved requests awaiting payment'\n : 'No history yet'\n }\n description={\n activeTab === 'pending'\n ? 'New withdrawal requests will appear here.'\n : activeTab === 'approved'\n ? 'Approve pending requests first, then send payment and mark as paid.'\n : 'Completed and rejected requests will appear here.'\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {visibleRequests.map((request) => (\n <RequestCard\n key={request.id}\n request={request}\n onApprove={(r) => openDialog('approve', r)}\n onReject={(r) => openDialog('reject', r)}\n onMarkPaid={(r) => openDialog('mark_paid', r)}\n />\n ))}\n </div>\n )}\n\n {dialog && (\n <ActionDialog dialog={dialog} onClose={() => setDialog(null)} onConfirm={handleConfirm} />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"PayoutAdminPage.js","names":[],"sources":["../../../../../src/billing/modules/earnings/pages/PayoutAdminPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState, type FC } from 'react';\nimport {\n AlertTriangle,\n Banknote,\n CheckCircle2,\n ChevronDown,\n ChevronUp,\n Clock,\n CreditCard,\n RefreshCw,\n ReceiptText,\n XCircle,\n} from 'lucide-react';\n\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport { useBillingEventEmitter } from '../../../hooks/useBillingEventEmitter';\nimport { useBilling } from '../../../providers/BillingProvider';\nimport { AccessDenied, EmptyState, PageHeader, ServerError } from '../../../shared/components';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport {\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n} from '@burdenoff/fe-libs/ui';\nimport {\n approvePayoutRequest,\n fetchAdminPayoutRequests,\n fetchAdminPayoutSummary,\n markPayoutPaid,\n rejectPayoutRequest,\n type PayoutAdminSummary,\n type PayoutRequest,\n} from '../api';\n\ntype Tab = 'pending' | 'approved' | 'history';\n\ninterface ActionDialogState {\n type: 'approve' | 'reject' | 'mark_paid';\n requestId: string;\n amount: number;\n currency: string;\n accountLabel: string;\n}\n\nconst STATUS_LABEL: Record<string, string> = {\n REQUESTED: 'Requested',\n APPROVED: 'Approved',\n REJECTED: 'Rejected',\n PROCESSING: 'Processing',\n PAID: 'Paid',\n FAILED: 'Failed',\n CANCELED: 'Canceled',\n};\n\nconst STATUS_COLORS: Record<string, string> = {\n REQUESTED: 'bg-status-warning-bg-subtle text-status-warning-text',\n APPROVED: 'bg-status-info-bg-subtle text-status-info-text',\n REJECTED: 'bg-status-error-bg-subtle text-status-error-text',\n PROCESSING: 'bg-status-info-bg-subtle text-status-info-text',\n PAID: 'bg-status-success-bg-subtle text-status-success-text',\n FAILED: 'bg-status-error-bg-subtle text-status-error-text',\n CANCELED: 'bg-bg-sunken text-text-muted',\n};\n\nfunction StatusBadge({ status }: { status: string }) {\n return (\n <span\n className={`inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${STATUS_COLORS[status] ?? 'bg-bg-sunken text-text-muted'}`}\n >\n {STATUS_LABEL[status] ?? status}\n </span>\n );\n}\n\nfunction AccountDetails({ account }: { account: NonNullable<PayoutRequest['payoutAccount']> }) {\n const [expanded, setExpanded] = useState(false);\n const details = account.accountDetails as Record<string, string | null> | null;\n\n return (\n <div className=\"rounded-md border border-border-subtle bg-bg-sunken/40 p-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2 text-text-primary font-medium\">\n <CreditCard className=\"size-4 text-text-muted\" />\n {account.displayName ?? account.label}\n <span className=\"text-xs text-text-muted font-normal\">\n ({account.type === 'UPI' ? 'UPI' : 'Bank'} · {account.country ?? 'Unknown'} ·{' '}\n {account.currency})\n </span>\n </div>\n {details && (\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"text-text-muted hover:text-text-primary\"\n >\n {expanded ? <ChevronUp className=\"size-4\" /> : <ChevronDown className=\"size-4\" />}\n </button>\n )}\n </div>\n {expanded && details && (\n <div className=\"mt-2 grid gap-1 text-xs\">\n {details.accountHolderName && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Account holder</span>\n <span className=\"text-text-primary\">{details.accountHolderName}</span>\n </div>\n )}\n {details.bankName && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Bank</span>\n <span className=\"text-text-primary\">{details.bankName}</span>\n </div>\n )}\n {account.accountLast4 && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Account (last 4)</span>\n <span className=\"text-text-primary font-mono\">••••{account.accountLast4}</span>\n </div>\n )}\n {details.ifsc && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">IFSC</span>\n <span className=\"text-text-primary font-mono\">{details.ifsc}</span>\n </div>\n )}\n {details.routingNumberLast4 && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">Routing (last 4)</span>\n <span className=\"text-text-primary font-mono\">••••{details.routingNumberLast4}</span>\n </div>\n )}\n {details.upiIdMasked && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">UPI ID</span>\n <span className=\"text-text-primary font-mono\">{details.upiIdMasked}</span>\n </div>\n )}\n {details.swift && (\n <div className=\"flex gap-2\">\n <span className=\"text-text-muted w-32 shrink-0\">SWIFT</span>\n <span className=\"text-text-primary font-mono\">{details.swift}</span>\n </div>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction RequestCard({\n request,\n onApprove,\n onReject,\n onMarkPaid,\n}: {\n request: PayoutRequest;\n onApprove: (r: PayoutRequest) => void;\n onReject: (r: PayoutRequest) => void;\n onMarkPaid: (r: PayoutRequest) => void;\n}) {\n const canApprove = request.status === 'REQUESTED' || request.status === 'REJECTED';\n const canReject = request.status === 'REQUESTED' || request.status === 'APPROVED';\n const canMarkPaid = request.status === 'APPROVED' || request.status === 'PROCESSING';\n\n return (\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-5 space-y-4 shadow-[var(--shadow-elevation-1)] transition-all duration-200 hover:shadow-[var(--shadow-elevation-2)]\">\n {/* Header row */}\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xl font-semibold text-text-primary\">\n {formatCurrency(request.requestedAmount, request.currency)}\n </span>\n <StatusBadge status={request.status} />\n </div>\n <div className=\"text-xs text-text-muted space-x-2\">\n <span>Requested {formatDateTime(request.createdAt)}</span>\n {request.requesterUserId && (\n <>\n <span>·</span>\n <span>by {request.requesterUserId}</span>\n </>\n )}\n <span>·</span>\n <span className=\"font-mono text-xs\">{request.id.slice(0, 8)}…</span>\n </div>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n {canApprove && (\n <button\n type=\"button\"\n onClick={() => onApprove(request)}\n className=\"inline-flex items-center gap-1.5 rounded-button bg-action-primary-bg px-3 py-1.5 text-xs font-medium text-action-primary-text\"\n >\n <CheckCircle2 className=\"size-3.5\" />\n Approve\n </button>\n )}\n {canMarkPaid && (\n <button\n type=\"button\"\n onClick={() => onMarkPaid(request)}\n className=\"inline-flex items-center gap-1.5 rounded-button bg-status-success-bg-subtle border border-status-success-border px-3 py-1.5 text-xs font-medium text-status-success-text transition-colors hover:bg-status-success-bg-subtle/70\"\n >\n <Banknote className=\"size-3.5\" />\n Mark as Paid\n </button>\n )}\n {canReject && (\n <button\n type=\"button\"\n onClick={() => onReject(request)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-border-subtle px-3 py-1.5 text-xs font-medium text-status-error-text\"\n >\n <XCircle className=\"size-3.5\" />\n Reject\n </button>\n )}\n </div>\n </div>\n\n {/* Payout account */}\n {request.payoutAccount && <AccountDetails account={request.payoutAccount} />}\n\n {/* User notes */}\n {request.userNotes && (\n <div className=\"rounded-md bg-bg-sunken/40 border border-border-subtle px-3 py-2 text-sm\">\n <span className=\"text-xs font-medium text-text-muted uppercase tracking-wide\">\n User note\n </span>\n <p className=\"mt-1 text-text-primary\">{request.userNotes}</p>\n </div>\n )}\n\n {/* Internal notes / failure / ref */}\n {(request.internalNotes ?? request.failureReason ?? request.externalReference) && (\n <div className=\"grid gap-1 text-xs text-text-muted\">\n {request.externalReference && (\n <div className=\"flex gap-2\">\n <ReceiptText className=\"size-3.5 mt-px shrink-0\" />\n <span>\n Ref:{' '}\n <span className=\"font-mono text-text-primary\">{request.externalReference}</span>\n </span>\n </div>\n )}\n {request.internalNotes && (\n <div className=\"flex gap-2\">\n <span className=\"shrink-0\">Internal note:</span>\n <span className=\"text-text-primary\">{request.internalNotes}</span>\n </div>\n )}\n {request.failureReason && (\n <div className=\"flex gap-2 text-status-error-text\">\n <AlertTriangle className=\"size-3.5 mt-px shrink-0\" />\n <span>{request.failureReason}</span>\n </div>\n )}\n </div>\n )}\n\n {/* Timestamps */}\n {(request.paidAt ?? request.processedAt) && (\n <div className=\"flex flex-wrap gap-4 text-xs text-text-muted\">\n {request.processedAt && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n Processed {formatDateTime(request.processedAt)}\n </span>\n )}\n {request.paidAt && (\n <span className=\"flex items-center gap-1\">\n <CheckCircle2 className=\"size-3\" />\n Paid {formatDateTime(request.paidAt)}\n </span>\n )}\n </div>\n )}\n </div>\n );\n}\n\nfunction SummaryBar({ summary }: { summary: PayoutAdminSummary }) {\n return (\n <div className=\"grid gap-3 sm:grid-cols-3\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Pending payout</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalRequestedAmount, 'USD')}\n </div>\n <div className=\"text-xs text-text-muted mt-0.5\">{summary.pendingCount} requests</div>\n </div>\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Processing</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalProcessingAmount, 'USD')}\n </div>\n </div>\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-4 shadow-[var(--shadow-elevation-1)]\">\n <div className=\"text-xs text-text-muted uppercase tracking-wide\">Total paid</div>\n <div className=\"mt-1 text-lg font-semibold text-text-primary\">\n {formatCurrency(summary.totalPaidAmount, 'USD')}\n </div>\n {summary.failedCount > 0 && (\n <div className=\"text-xs text-status-error-text mt-0.5\">{summary.failedCount} failed</div>\n )}\n </div>\n </div>\n );\n}\n\nfunction ActionDialog({\n dialog,\n onClose,\n onConfirm,\n}: {\n dialog: ActionDialogState;\n onClose: () => void;\n onConfirm: (data: {\n notes?: string;\n reason?: string;\n externalReference?: string;\n }) => Promise<void>;\n}) {\n const [notes, setNotes] = useState('');\n const [reason, setReason] = useState('');\n const [txnRef, setTxnRef] = useState('');\n const [submitting, setSubmitting] = useState(false);\n\n const handleSubmit = async () => {\n if (dialog.type === 'reject' && !reason.trim()) return;\n setSubmitting(true);\n await onConfirm({\n notes: notes.trim() || undefined,\n reason: reason.trim() || undefined,\n externalReference: txnRef.trim() || undefined,\n });\n setSubmitting(false);\n };\n\n const titles = {\n approve: 'Approve withdrawal request',\n reject: 'Reject withdrawal request',\n mark_paid: 'Mark as paid',\n };\n\n return (\n <ResponsiveDialog\n open\n onOpenChange={(open) => {\n if (!open && !submitting) onClose();\n }}\n >\n <ResponsiveDialogContent className=\"sm:max-w-md\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>{titles[dialog.type]}</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n {formatCurrency(dialog.amount, dialog.currency)} · {dialog.accountLabel}\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n {dialog.type === 'mark_paid' && (\n <div className=\"space-y-3\">\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Transaction reference / UTR number\n </label>\n <input\n type=\"text\"\n value={txnRef}\n onChange={(e) => setTxnRef(e.target.value)}\n placeholder=\"e.g. UTR123456789012, TXN_abc123\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm text-text-primary placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n <p className=\"text-xs text-text-muted mt-1\">\n Enter the bank reference number so the user can verify the transfer.\n </p>\n </div>\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Internal notes (optional)\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={2}\n placeholder=\"Any internal notes about this payment…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm 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 </div>\n )}\n\n {dialog.type === 'approve' && (\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Internal notes (optional)\n </label>\n <textarea\n value={notes}\n onChange={(e) => setNotes(e.target.value)}\n rows={3}\n placeholder=\"e.g. Verified bank details, approved for transfer…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm 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\n {dialog.type === 'reject' && (\n <div>\n <label className=\"text-xs font-medium text-text-primary block mb-1\">\n Reason for rejection <span className=\"text-status-error-text\">*</span>\n </label>\n <textarea\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n rows={3}\n placeholder=\"e.g. Invalid bank details provided, please re-submit with correct IFSC…\"\n className=\"w-full rounded-input border border-border-subtle bg-bg-surface px-3 py-2 text-sm 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\n <ResponsiveDialogFooter>\n <button\n type=\"button\"\n onClick={onClose}\n disabled={submitting}\n className=\"rounded-md border border-border-subtle px-4 py-2 text-sm font-medium text-text-primary disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void handleSubmit()}\n disabled={submitting || (dialog.type === 'reject' && !reason.trim())}\n className={`rounded-md px-4 py-2 text-sm font-medium disabled:opacity-50 ${\n dialog.type === 'reject'\n ? 'bg-action-danger-bg text-action-danger-text'\n : 'bg-action-primary-bg text-action-primary-text'\n }`}\n >\n {submitting\n ? 'Saving…'\n : dialog.type === 'approve'\n ? 'Approve'\n : dialog.type === 'reject'\n ? 'Reject'\n : 'Confirm Payment'}\n </button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nexport const PayoutAdminPage: FC = () => {\n const permissions = useBillingPermissions();\n const { emit } = useBillingEventEmitter();\n const { apiGatewayUrl, authToken, orgId } = useBilling();\n\n const [summary, setSummary] = useState<PayoutAdminSummary | null>(null);\n const [requests, setRequests] = useState<PayoutRequest[]>([]);\n const [loading, setLoading] = useState(true);\n const [error, setError] = useState<Error | null>(null);\n const [activeTab, setActiveTab] = useState<Tab>('pending');\n const [dialog, setDialog] = useState<ActionDialogState | null>(null);\n\n const requestContext = useMemo(\n () => ({ apiGatewayUrl, authToken, orgId }),\n [apiGatewayUrl, authToken, orgId]\n );\n\n const loadData = async () => {\n setLoading(true);\n setError(null);\n try {\n const [loadedSummary, loadedRequests] = await Promise.all([\n fetchAdminPayoutSummary(requestContext),\n fetchAdminPayoutRequests(requestContext),\n ]);\n setSummary(loadedSummary);\n setRequests(loadedRequests);\n } catch (loadError) {\n setError(loadError instanceof Error ? loadError : new Error('Failed to load payout queue'));\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n void loadData();\n }, [requestContext]);\n\n if (!permissions.isBillingAdmin) {\n return <AccessDenied message=\"You don't have permission to manage payouts.\" />;\n }\n\n if (error && isServerError(error)) {\n return (\n <ServerError\n title=\"Payout queue unavailable\"\n message={error.message}\n onRetry={() => void loadData()}\n showRetry\n />\n );\n }\n\n const pending = requests.filter((r) => r.status === 'REQUESTED');\n const approved = requests.filter((r) => r.status === 'APPROVED' || r.status === 'PROCESSING');\n const history = requests.filter((r) =>\n ['PAID', 'REJECTED', 'FAILED', 'CANCELED'].includes(r.status)\n );\n\n const tabItems: { id: Tab; label: string; count: number }[] = [\n { id: 'pending', label: 'Pending', count: pending.length },\n { id: 'approved', label: 'Approved', count: approved.length },\n { id: 'history', label: 'History', count: history.length },\n ];\n\n const visibleRequests =\n activeTab === 'pending' ? pending : activeTab === 'approved' ? approved : history;\n\n const openDialog = (type: ActionDialogState['type'], request: PayoutRequest) => {\n setDialog({\n type,\n requestId: request.id,\n amount: request.requestedAmount,\n currency: request.currency,\n accountLabel:\n request.payoutAccount?.displayName ?? request.payoutAccount?.label ?? 'Unknown account',\n });\n };\n\n const handleConfirm = async (data: {\n notes?: string;\n reason?: string;\n externalReference?: string;\n }) => {\n if (!dialog) return;\n\n try {\n if (dialog.type === 'approve') {\n await approvePayoutRequest(dialog.requestId, data.notes ?? null, requestContext);\n emit('billing.payout.approved', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n });\n } else if (dialog.type === 'reject') {\n await rejectPayoutRequest(dialog.requestId, data.reason ?? '', requestContext);\n emit('billing.payout.rejected', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n reason: data.reason,\n });\n } else if (dialog.type === 'mark_paid') {\n await markPayoutPaid(\n dialog.requestId,\n data.externalReference ?? null,\n data.notes ?? null,\n requestContext\n );\n emit('billing.payout.processed', {\n route: '/billing/payouts',\n entityId: dialog.requestId,\n source: 'payout-admin',\n });\n }\n setDialog(null);\n await loadData();\n } catch (err) {\n setError(err instanceof Error ? err : new Error('Action failed'));\n }\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title=\"Payout queue\"\n description=\"Review withdrawal requests, verify bank details, and mark payments as paid once transferred.\"\n actions={\n <button\n type=\"button\"\n onClick={() => void loadData()}\n className=\"inline-flex items-center gap-2 rounded-md border border-border-subtle px-3 py-2 text-sm font-medium text-text-primary\"\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n }\n />\n\n {summary && !loading && <SummaryBar summary={summary} />}\n\n {loading && (\n <div className=\"grid gap-3 sm:grid-cols-3\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-20 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n )}\n\n {error && !isServerError(error) && (\n <div className=\"rounded-lg border border-status-error-border bg-status-error-bg-subtle p-4 text-sm text-status-error-text\">\n {error.message}\n </div>\n )}\n\n {/* Tabs */}\n <div className=\"border-b border-border-subtle\">\n <div className=\"flex gap-0\">\n {tabItems.map((tab) => (\n <button\n key={tab.id}\n type=\"button\"\n onClick={() => setActiveTab(tab.id)}\n className={`flex items-center gap-2 border-b-2 px-4 py-2.5 text-sm font-medium transition-colors ${\n activeTab === tab.id\n ? 'border-action-primary-bg text-text-primary'\n : 'border-transparent text-text-muted hover:text-text-primary'\n }`}\n >\n {tab.label}\n {tab.count > 0 && (\n <span\n className={`rounded-full px-1.5 py-0.5 text-xs ${\n activeTab === tab.id\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-muted'\n }`}\n >\n {tab.count}\n </span>\n )}\n </button>\n ))}\n </div>\n </div>\n\n {/* How to pay — shown only on the Approved tab */}\n {activeTab === 'approved' && approved.length > 0 && (\n <div className=\"rounded-card border border-status-info-border bg-status-info-bg-subtle px-4 py-3 text-sm text-status-info-text\">\n <strong>How to pay:</strong> Click the account row to expand bank/UPI details → transfer\n the exact amount via your bank → click <strong>Mark as Paid</strong> and enter the UTR /\n transaction reference number.\n </div>\n )}\n\n {/* Request cards */}\n {loading ? (\n <div className=\"space-y-4\">\n {[1, 2].map((i) => (\n <div\n key={i}\n className=\"h-40 animate-pulse rounded-lg border border-border-subtle bg-bg-surface\"\n />\n ))}\n </div>\n ) : visibleRequests.length === 0 ? (\n <EmptyState\n icon={<CheckCircle2 className=\"size-8\" />}\n title={\n activeTab === 'pending'\n ? 'No pending requests'\n : activeTab === 'approved'\n ? 'No approved requests awaiting payment'\n : 'No history yet'\n }\n description={\n activeTab === 'pending'\n ? 'New withdrawal requests will appear here.'\n : activeTab === 'approved'\n ? 'Approve pending requests first, then send payment and mark as paid.'\n : 'Completed and rejected requests will appear here.'\n }\n />\n ) : (\n <div className=\"space-y-4\">\n {visibleRequests.map((request) => (\n <RequestCard\n key={request.id}\n request={request}\n onApprove={(r) => openDialog('approve', r)}\n onReject={(r) => openDialog('reject', r)}\n onMarkPaid={(r) => openDialog('mark_paid', r)}\n />\n ))}\n </div>\n )}\n\n {dialog && (\n <ActionDialog dialog={dialog} onClose={() => setDialog(null)} onConfirm={handleConfirm} />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -81,7 +81,7 @@ var u = () => {
81
81
  }),
82
82
  /* @__PURE__ */ c("form", {
83
83
  onSubmit: O,
84
- className: "border border-border-subtle rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
84
+ className: "border border-border-seam rounded-card bg-bg-surface overflow-hidden shadow-[var(--shadow-elevation-1)]",
85
85
  children: [
86
86
  /* @__PURE__ */ s("div", {
87
87
  className: "p-4 border-b border-border-subtle bg-bg-sunken/30",