@burdenoff/microfe-billing 2026.515.6 → 2026.516.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.
- package/dist/billing/modules/billing/pages/InvoicesListPage.js +1 -1
- package/dist/billing/modules/billing/pages/InvoicesListPage.js.map +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js +1 -1
- package/dist/billing/modules/billing/pages/RefundsPage.js.map +1 -1
- package/dist/billing/modules/checkout/components/OrderSummary.js +4 -4
- package/dist/billing/modules/checkout/components/OrderSummary.js.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.d.ts.map +1 -1
- package/dist/billing/modules/checkout/pages/CheckoutPage.js +2 -2
- package/dist/billing/modules/checkout/pages/CheckoutPage.js.map +1 -1
- package/dist/billing/modules/dashboard/pages/OverviewPage.js +2 -2
- package/dist/billing/modules/dashboard/pages/OverviewPage.js.map +1 -1
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js +2 -2
- package/dist/billing/modules/discounts/pages/DiscountsListPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaPage.js +1 -1
- package/dist/billing/modules/quota/pages/QuotaPage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.d.ts.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js +101 -44
- package/dist/billing/modules/quota/pages/QuotaTemplateCreatePage.js.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.d.ts.map +1 -1
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js +105 -46
- package/dist/billing/modules/quota/pages/QuotaTemplateEditPage.js.map +1 -1
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js +3 -3
- package/dist/billing/modules/subscriptions/pages/SubscriptionsListPage.js.map +1 -1
- package/dist/billing/modules/usage/pages/UsagePage.js +3 -3
- package/dist/billing/modules/usage/pages/UsagePage.js.map +1 -1
- package/dist/billing/shared/types/graphql.d.ts +2 -0
- package/dist/billing/shared/types/graphql.d.ts.map +1 -1
- package/dist/billing/shared/types/graphql.js.map +1 -1
- package/package.json +1 -1
|
@@ -135,7 +135,7 @@ var b = () => {
|
|
|
135
135
|
children: e === "all" ? "All" : r(e)
|
|
136
136
|
}, e))
|
|
137
137
|
}), /* @__PURE__ */ v("div", {
|
|
138
|
-
className: "relative flex-1 min-w-
|
|
138
|
+
className: "relative flex-1 min-w-0 w-full sm:max-w-md",
|
|
139
139
|
children: [/* @__PURE__ */ _("svg", {
|
|
140
140
|
className: "absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground",
|
|
141
141
|
fill: "none",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InvoicesListPage.js","names":[],"sources":["../../../../../src/billing/modules/billing/pages/InvoicesListPage.tsx"],"sourcesContent":["/**\n * Billing Module - Invoices List Page\n * Displays all invoices for the current tenant\n */\n\nimport { useEffect, useState, useMemo, useRef, type FC } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePaginatedInvoices } from '../hooks/useInvoices';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n useGetBillingAccountsByOrgQuery,\n type GetBillingAccountsByOrgQuery,\n} from '../../../../generated/global-operations';\n\ntype BillingAccountItem = GetBillingAccountsByOrgQuery['getBillingAccountsByOrg'][number];\nimport {\n formatCurrency,\n formatDate,\n formatInvoiceStatus,\n formatInvoiceNumber,\n} from '../../../shared/utils/format';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport { getInvoiceStatusColor, getStatusBadgeClasses } from '../../../shared/utils/status';\nimport type { Invoice, InvoiceStatus } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ntype FilterStatus = 'all' | InvoiceStatus;\n\nexport const InvoicesListPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const [searchParams] = useSearchParams();\n const urlBillingAccountId = searchParams.get('billingAccountId') ?? undefined;\n\n const { data: accountsData } = useGetBillingAccountsByOrgQuery({ fetchPolicy: 'cache-first' });\n const billingAccounts = accountsData?.getBillingAccountsByOrg ?? [];\n\n // undefined = user hasn't explicitly chosen yet → auto-pick default\n // null = user explicitly chose \"All Accounts\"\n // string = user explicitly chose a specific account\n const [explicitAccountId, setExplicitAccountId] = useState<string | null | undefined>(\n urlBillingAccountId\n );\n\n useEffect(() => {\n setExplicitAccountId(urlBillingAccountId);\n }, [urlBillingAccountId]);\n\n const defaultAccountId = useMemo(\n () =>\n (billingAccounts.find((a: BillingAccountItem) => a.isDefault) ?? billingAccounts[0])?.id ??\n undefined,\n [billingAccounts]\n );\n\n // Resolved selection: explicit choice wins; before any choice, use the default\n const selectedAccountId: string | null =\n explicitAccountId !== undefined ? explicitAccountId : (defaultAccountId ?? null);\n\n const [filterStatus, setFilterStatus] = useState<FilterStatus>('all');\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n const [page, setPage] = useState(1);\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (debounceTimer.current) clearTimeout(debounceTimer.current);\n debounceTimer.current = setTimeout(() => {\n setDebouncedSearch(searchQuery);\n setPage(1);\n }, 400);\n return () => {\n if (debounceTimer.current) clearTimeout(debounceTimer.current);\n };\n }, [searchQuery]);\n\n const { invoices, totalPages, isLoading, error, refetch } = usePaginatedInvoices({\n billingAccountId: selectedAccountId ?? undefined,\n status: filterStatus !== 'all' ? (filterStatus as InvoiceStatus) : undefined,\n search: debouncedSearch || undefined,\n page,\n pageSize: 20,\n });\n\n // Permission check\n if (!permissions.canViewInvoices) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"w-12 h-12 mx-auto rounded-full bg-muted flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.accessDenied.title', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground max-w-sm\">\n {tr('billing.invoices.noViewPermission', \"You don't have permission to view invoices.\")}\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (isLoading && invoices.length === 0) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"space-y-3\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"border border-border rounded-lg p-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"h-10 w-10 bg-muted animate-pulse rounded\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-32 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-48 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"w-12 h-12 mx-auto rounded-full bg-destructive/10 flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-destructive\"\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-foreground\">\n {tr('billing.invoices.failedToLoad', 'Failed to load invoices')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">{error.message}</p>\n <button\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.invoices.title', 'Invoices')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {tr('billing.invoices.subtitle', 'View and manage your billing invoices')}\n </p>\n </div>\n {billingAccounts.length > 1 && (\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-muted-foreground whitespace-nowrap\">\n Billing Account\n </label>\n <select\n value={selectedAccountId ?? ''}\n onChange={(e) => setExplicitAccountId(e.target.value || null)}\n className=\"text-sm border border-border rounded-md bg-background text-foreground px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"\">All Accounts</option>\n {billingAccounts.map((account: BillingAccountItem) => (\n <option key={account.id ?? ''} value={account.id ?? ''}>\n {account.name ?? account.email ?? account.id}\n {account.isDefault ? ' (Default)' : ''}\n </option>\n ))}\n </select>\n </div>\n )}\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border\">\n {/* Status Filter */}\n <div className=\"inline-flex rounded-lg border border-border p-1 bg-muted/50 flex-wrap\">\n {(['all', 'OPEN', 'PAID', 'DRAFT', 'VOID'] as FilterStatus[]).map((status) => (\n <button\n key={status}\n onClick={() => {\n setFilterStatus(status);\n setPage(1);\n }}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n filterStatus === status\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {status === 'all' ? 'All' : formatInvoiceStatus(status as InvoiceStatus)}\n </button>\n ))}\n </div>\n\n {/* Search */}\n <div className=\"relative flex-1 min-w-[200px] max-w-md\">\n <svg\n className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"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={tr('billing.invoices.searchPlaceholder', 'Search by invoice number...')}\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 rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n </div>\n </div>\n\n {/* Invoices Table */}\n {!isLoading && invoices.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"w-12 h-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <svg\n className=\"w-6 h-6 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">\n {tr('billing.invoices.noInvoicesFound', 'No invoices found')}\n </h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {filterStatus !== 'all' || debouncedSearch\n ? tr('billing.invoices.noInvoicesFiltered', 'No invoices match your filters.')\n : tr('billing.invoices.noInvoicesYet', \"You don't have any invoices yet.\")}\n </p>\n </div>\n ) : (\n <>\n <div className=\"border border-border rounded-lg overflow-hidden\">\n <table className=\"w-full\">\n <thead className=\"bg-muted/50\">\n <tr>\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground\">\n Invoice\n </th>\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground\">\n Status\n </th>\n {!selectedAccountId && billingAccounts.length > 1 && (\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground hidden lg:table-cell\">\n Account\n </th>\n )}\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground hidden sm:table-cell\">\n Date\n </th>\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground hidden md:table-cell\">\n Due Date\n </th>\n <th className=\"text-right px-4 py-3 text-sm font-medium text-muted-foreground\">\n Amount\n </th>\n <th className=\"text-right px-4 py-3 text-sm font-medium text-muted-foreground\">\n Actions\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {invoices.map((invoice) => (\n <InvoiceRow\n key={invoice.id}\n invoice={invoice}\n onView={() =>\n navigateTo(\n withBillingAccountId(`/invoices/${invoice.id}`, invoice.billingAccountId)\n )\n }\n canPay={permissions.canPayInvoice}\n canVoid={permissions.canVoidInvoice}\n showAccount={!selectedAccountId && billingAccounts.length > 1}\n accountName={\n billingAccounts.find(\n (a: BillingAccountItem) => a.id === invoice.billingAccountId\n )?.name ?? null\n }\n />\n ))}\n </tbody>\n </table>\n </div>\n\n {totalPages > 1 && (\n <div className=\"flex items-center justify-between pt-2\">\n <p className=\"text-sm text-muted-foreground\">\n Page {page} of {totalPages}\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n disabled={page <= 1}\n className=\"px-3 py-1.5 text-sm font-medium border border-border rounded-md bg-background text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n >\n Previous\n </button>\n <button\n onClick={() => setPage((p) => Math.min(totalPages, p + 1))}\n disabled={page >= totalPages}\n className=\"px-3 py-1.5 text-sm font-medium border border-border rounded-md bg-background text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n >\n Next\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Invoice Row Component\n// ============================================================================\n\ninterface InvoiceRowProps {\n invoice: Invoice;\n onView: () => void;\n canPay: boolean;\n canVoid: boolean;\n showAccount?: boolean;\n accountName?: string | null;\n}\n\nconst InvoiceRow: FC<InvoiceRowProps> = ({\n invoice,\n onView,\n canPay,\n canVoid,\n showAccount = false,\n accountName,\n}) => {\n const statusColor = getInvoiceStatusColor(invoice.status);\n const isOpen = invoice.status === ('OPEN' as InvoiceStatus);\n const isDraft = invoice.status === ('DRAFT' as InvoiceStatus);\n\n return (\n <tr className=\"hover:bg-muted/30 transition-colors\">\n <td className=\"px-4 py-4\">\n <button\n onClick={onView}\n className=\"font-medium text-foreground hover:text-primary transition-colors\"\n >\n {formatInvoiceNumber(invoice.invoiceNumber)}\n </button>\n {(invoice.itemDescription ?? invoice.subscription?.plan?.name) && (\n <p className=\"text-sm text-muted-foreground mt-0.5\">\n {invoice.itemDescription ?? invoice.subscription?.plan?.name}\n </p>\n )}\n </td>\n <td className=\"px-4 py-4\">\n <span\n className={`inline-flex px-2 py-1 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {formatInvoiceStatus(invoice.status)}\n </span>\n </td>\n {showAccount && (\n <td className=\"px-4 py-4 hidden lg:table-cell\">\n {accountName ? (\n <span className=\"inline-flex items-center px-2 py-0.5 text-xs font-medium rounded-full bg-muted text-muted-foreground border border-border\">\n {accountName}\n </span>\n ) : (\n <span className=\"text-sm text-muted-foreground\">—</span>\n )}\n </td>\n )}\n <td className=\"px-4 py-4 text-sm text-muted-foreground hidden sm:table-cell\">\n {formatDate(invoice.createdAt, 'short')}\n </td>\n <td className=\"px-4 py-4 text-sm text-muted-foreground hidden md:table-cell\">\n {invoice.dueDate ? formatDate(invoice.dueDate, 'short') : '—'}\n </td>\n <td className=\"px-4 py-4 text-right font-medium text-foreground\">\n {formatCurrency(invoice.total, invoice.currency)}\n </td>\n <td className=\"px-4 py-4 text-right\">\n <div className=\"flex items-center justify-end gap-2\">\n {invoice.invoiceUrl ? (\n <a\n href={invoice.invoiceUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n View\n </a>\n ) : (\n <button\n onClick={onView}\n className=\"px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n View\n </button>\n )}\n {invoice.pdfUrl && (\n <a\n href={invoice.pdfUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"px-3 py-1.5 text-sm font-medium text-primary hover:bg-primary/10 rounded-md transition-colors\"\n >\n PDF\n </a>\n )}\n {isOpen && canPay && (\n <button className=\"px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\">\n Pay\n </button>\n )}\n {(isOpen || isDraft) && canVoid && (\n <button className=\"px-3 py-1.5 text-sm font-medium text-destructive hover:bg-destructive/10 rounded-md transition-colors\">\n Void\n </button>\n )}\n </div>\n </td>\n </tr>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InvoicesListPage.js","names":[],"sources":["../../../../../src/billing/modules/billing/pages/InvoicesListPage.tsx"],"sourcesContent":["/**\n * Billing Module - Invoices List Page\n * Displays all invoices for the current tenant\n */\n\nimport { useEffect, useState, useMemo, useRef, type FC } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport { useBillingNavigate } from '../../../hooks/useBillingNavigate';\nimport { usePaginatedInvoices } from '../hooks/useInvoices';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n useGetBillingAccountsByOrgQuery,\n type GetBillingAccountsByOrgQuery,\n} from '../../../../generated/global-operations';\n\ntype BillingAccountItem = GetBillingAccountsByOrgQuery['getBillingAccountsByOrg'][number];\nimport {\n formatCurrency,\n formatDate,\n formatInvoiceStatus,\n formatInvoiceNumber,\n} from '../../../shared/utils/format';\nimport { withBillingAccountId } from '../../../shared/utils/navigation';\nimport { getInvoiceStatusColor, getStatusBadgeClasses } from '../../../shared/utils/status';\nimport type { Invoice, InvoiceStatus } from '../../../shared/types';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ntype FilterStatus = 'all' | InvoiceStatus;\n\nexport const InvoicesListPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const translated = t(key);\n return translated === key ? fallback : translated;\n };\n const navigateTo = useBillingNavigate();\n const permissions = useBillingPermissions();\n const [searchParams] = useSearchParams();\n const urlBillingAccountId = searchParams.get('billingAccountId') ?? undefined;\n\n const { data: accountsData } = useGetBillingAccountsByOrgQuery({ fetchPolicy: 'cache-first' });\n const billingAccounts = accountsData?.getBillingAccountsByOrg ?? [];\n\n // undefined = user hasn't explicitly chosen yet → auto-pick default\n // null = user explicitly chose \"All Accounts\"\n // string = user explicitly chose a specific account\n const [explicitAccountId, setExplicitAccountId] = useState<string | null | undefined>(\n urlBillingAccountId\n );\n\n useEffect(() => {\n setExplicitAccountId(urlBillingAccountId);\n }, [urlBillingAccountId]);\n\n const defaultAccountId = useMemo(\n () =>\n (billingAccounts.find((a: BillingAccountItem) => a.isDefault) ?? billingAccounts[0])?.id ??\n undefined,\n [billingAccounts]\n );\n\n // Resolved selection: explicit choice wins; before any choice, use the default\n const selectedAccountId: string | null =\n explicitAccountId !== undefined ? explicitAccountId : (defaultAccountId ?? null);\n\n const [filterStatus, setFilterStatus] = useState<FilterStatus>('all');\n const [searchQuery, setSearchQuery] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n const [page, setPage] = useState(1);\n const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (debounceTimer.current) clearTimeout(debounceTimer.current);\n debounceTimer.current = setTimeout(() => {\n setDebouncedSearch(searchQuery);\n setPage(1);\n }, 400);\n return () => {\n if (debounceTimer.current) clearTimeout(debounceTimer.current);\n };\n }, [searchQuery]);\n\n const { invoices, totalPages, isLoading, error, refetch } = usePaginatedInvoices({\n billingAccountId: selectedAccountId ?? undefined,\n status: filterStatus !== 'all' ? (filterStatus as InvoiceStatus) : undefined,\n search: debouncedSearch || undefined,\n page,\n pageSize: 20,\n });\n\n // Permission check\n if (!permissions.canViewInvoices) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-2\">\n <div className=\"w-12 h-12 mx-auto rounded-full bg-muted flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\"\n />\n </svg>\n </div>\n <h2 className=\"text-lg font-semibold text-foreground\">\n {tr('billing.accessDenied.title', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground max-w-sm\">\n {tr('billing.invoices.noViewPermission', \"You don't have permission to view invoices.\")}\n </p>\n </div>\n </div>\n );\n }\n\n // Loading state\n if (isLoading && invoices.length === 0) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"space-y-3\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"border border-border rounded-lg p-4\">\n <div className=\"flex items-center gap-4\">\n <div className=\"h-10 w-10 bg-muted animate-pulse rounded\" />\n <div className=\"flex-1 space-y-2\">\n <div className=\"h-4 w-32 bg-muted animate-pulse rounded\" />\n <div className=\"h-3 w-48 bg-muted animate-pulse rounded\" />\n </div>\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n // Error state\n if (error) {\n return (\n <div className=\"flex items-center justify-center h-full min-h-[400px]\">\n <div className=\"text-center space-y-4\">\n <div className=\"w-12 h-12 mx-auto rounded-full bg-destructive/10 flex items-center justify-center\">\n <svg\n className=\"w-6 h-6 text-destructive\"\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-foreground\">\n {tr('billing.invoices.failedToLoad', 'Failed to load invoices')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">{error.message}</p>\n <button\n onClick={() => refetch()}\n className=\"px-4 py-2 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-bold text-foreground\">\n {tr('billing.invoices.title', 'Invoices')}\n </h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {tr('billing.invoices.subtitle', 'View and manage your billing invoices')}\n </p>\n </div>\n {billingAccounts.length > 1 && (\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm text-muted-foreground whitespace-nowrap\">\n Billing Account\n </label>\n <select\n value={selectedAccountId ?? ''}\n onChange={(e) => setExplicitAccountId(e.target.value || null)}\n className=\"text-sm border border-border rounded-md bg-background text-foreground px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary\"\n >\n <option value=\"\">All Accounts</option>\n {billingAccounts.map((account: BillingAccountItem) => (\n <option key={account.id ?? ''} value={account.id ?? ''}>\n {account.name ?? account.email ?? account.id}\n {account.isDefault ? ' (Default)' : ''}\n </option>\n ))}\n </select>\n </div>\n )}\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-wrap items-center gap-4 pb-4 border-b border-border\">\n {/* Status Filter */}\n <div className=\"inline-flex rounded-lg border border-border p-1 bg-muted/50 flex-wrap\">\n {(['all', 'OPEN', 'PAID', 'DRAFT', 'VOID'] as FilterStatus[]).map((status) => (\n <button\n key={status}\n onClick={() => {\n setFilterStatus(status);\n setPage(1);\n }}\n className={`px-3 py-1.5 text-sm font-medium rounded-md transition-colors ${\n filterStatus === status\n ? 'bg-background text-foreground shadow-sm'\n : 'text-muted-foreground hover:text-foreground'\n }`}\n >\n {status === 'all' ? 'All' : formatInvoiceStatus(status as InvoiceStatus)}\n </button>\n ))}\n </div>\n\n {/* Search */}\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 w-4 h-4 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"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={tr('billing.invoices.searchPlaceholder', 'Search by invoice number...')}\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 rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\"\n />\n </div>\n </div>\n\n {/* Invoices Table */}\n {!isLoading && invoices.length === 0 ? (\n <div className=\"flex flex-col items-center justify-center py-12 border border-dashed border-border rounded-lg\">\n <div className=\"w-12 h-12 rounded-full bg-muted flex items-center justify-center mb-4\">\n <svg\n className=\"w-6 h-6 text-muted-foreground\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z\"\n />\n </svg>\n </div>\n <h3 className=\"text-lg font-medium text-foreground\">\n {tr('billing.invoices.noInvoicesFound', 'No invoices found')}\n </h3>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {filterStatus !== 'all' || debouncedSearch\n ? tr('billing.invoices.noInvoicesFiltered', 'No invoices match your filters.')\n : tr('billing.invoices.noInvoicesYet', \"You don't have any invoices yet.\")}\n </p>\n </div>\n ) : (\n <>\n <div className=\"border border-border rounded-lg overflow-hidden\">\n <table className=\"w-full\">\n <thead className=\"bg-muted/50\">\n <tr>\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground\">\n Invoice\n </th>\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground\">\n Status\n </th>\n {!selectedAccountId && billingAccounts.length > 1 && (\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground hidden lg:table-cell\">\n Account\n </th>\n )}\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground hidden sm:table-cell\">\n Date\n </th>\n <th className=\"text-left px-4 py-3 text-sm font-medium text-muted-foreground hidden md:table-cell\">\n Due Date\n </th>\n <th className=\"text-right px-4 py-3 text-sm font-medium text-muted-foreground\">\n Amount\n </th>\n <th className=\"text-right px-4 py-3 text-sm font-medium text-muted-foreground\">\n Actions\n </th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {invoices.map((invoice) => (\n <InvoiceRow\n key={invoice.id}\n invoice={invoice}\n onView={() =>\n navigateTo(\n withBillingAccountId(`/invoices/${invoice.id}`, invoice.billingAccountId)\n )\n }\n canPay={permissions.canPayInvoice}\n canVoid={permissions.canVoidInvoice}\n showAccount={!selectedAccountId && billingAccounts.length > 1}\n accountName={\n billingAccounts.find(\n (a: BillingAccountItem) => a.id === invoice.billingAccountId\n )?.name ?? null\n }\n />\n ))}\n </tbody>\n </table>\n </div>\n\n {totalPages > 1 && (\n <div className=\"flex items-center justify-between pt-2\">\n <p className=\"text-sm text-muted-foreground\">\n Page {page} of {totalPages}\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => setPage((p) => Math.max(1, p - 1))}\n disabled={page <= 1}\n className=\"px-3 py-1.5 text-sm font-medium border border-border rounded-md bg-background text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n >\n Previous\n </button>\n <button\n onClick={() => setPage((p) => Math.min(totalPages, p + 1))}\n disabled={page >= totalPages}\n className=\"px-3 py-1.5 text-sm font-medium border border-border rounded-md bg-background text-foreground hover:bg-muted disabled:opacity-40 disabled:cursor-not-allowed transition-colors\"\n >\n Next\n </button>\n </div>\n </div>\n )}\n </>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Invoice Row Component\n// ============================================================================\n\ninterface InvoiceRowProps {\n invoice: Invoice;\n onView: () => void;\n canPay: boolean;\n canVoid: boolean;\n showAccount?: boolean;\n accountName?: string | null;\n}\n\nconst InvoiceRow: FC<InvoiceRowProps> = ({\n invoice,\n onView,\n canPay,\n canVoid,\n showAccount = false,\n accountName,\n}) => {\n const statusColor = getInvoiceStatusColor(invoice.status);\n const isOpen = invoice.status === ('OPEN' as InvoiceStatus);\n const isDraft = invoice.status === ('DRAFT' as InvoiceStatus);\n\n return (\n <tr className=\"hover:bg-muted/30 transition-colors\">\n <td className=\"px-4 py-4\">\n <button\n onClick={onView}\n className=\"font-medium text-foreground hover:text-primary transition-colors\"\n >\n {formatInvoiceNumber(invoice.invoiceNumber)}\n </button>\n {(invoice.itemDescription ?? invoice.subscription?.plan?.name) && (\n <p className=\"text-sm text-muted-foreground mt-0.5\">\n {invoice.itemDescription ?? invoice.subscription?.plan?.name}\n </p>\n )}\n </td>\n <td className=\"px-4 py-4\">\n <span\n className={`inline-flex px-2 py-1 text-xs font-medium rounded ${getStatusBadgeClasses(statusColor)}`}\n >\n {formatInvoiceStatus(invoice.status)}\n </span>\n </td>\n {showAccount && (\n <td className=\"px-4 py-4 hidden lg:table-cell\">\n {accountName ? (\n <span className=\"inline-flex items-center px-2 py-0.5 text-xs font-medium rounded-full bg-muted text-muted-foreground border border-border\">\n {accountName}\n </span>\n ) : (\n <span className=\"text-sm text-muted-foreground\">—</span>\n )}\n </td>\n )}\n <td className=\"px-4 py-4 text-sm text-muted-foreground hidden sm:table-cell\">\n {formatDate(invoice.createdAt, 'short')}\n </td>\n <td className=\"px-4 py-4 text-sm text-muted-foreground hidden md:table-cell\">\n {invoice.dueDate ? formatDate(invoice.dueDate, 'short') : '—'}\n </td>\n <td className=\"px-4 py-4 text-right font-medium text-foreground\">\n {formatCurrency(invoice.total, invoice.currency)}\n </td>\n <td className=\"px-4 py-4 text-right\">\n <div className=\"flex items-center justify-end gap-2\">\n {invoice.invoiceUrl ? (\n <a\n href={invoice.invoiceUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n View\n </a>\n ) : (\n <button\n onClick={onView}\n className=\"px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted rounded-md transition-colors\"\n >\n View\n </button>\n )}\n {invoice.pdfUrl && (\n <a\n href={invoice.pdfUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"px-3 py-1.5 text-sm font-medium text-primary hover:bg-primary/10 rounded-md transition-colors\"\n >\n PDF\n </a>\n )}\n {isOpen && canPay && (\n <button className=\"px-3 py-1.5 text-sm font-medium bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\">\n Pay\n </button>\n )}\n {(isOpen || isDraft) && canVoid && (\n <button className=\"px-3 py-1.5 text-sm font-medium text-destructive hover:bg-destructive/10 rounded-md transition-colors\">\n Void\n </button>\n )}\n </div>\n </td>\n </tr>\n );\n};\n"],"mappings":"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"}
|
|
@@ -885,7 +885,7 @@ var L = {
|
|
|
885
885
|
children: /* @__PURE__ */ P("div", {
|
|
886
886
|
className: "overflow-x-auto",
|
|
887
887
|
children: /* @__PURE__ */ F("table", {
|
|
888
|
-
className: "w-full min-w-[
|
|
888
|
+
className: "w-full min-w-[400px]",
|
|
889
889
|
children: [/* @__PURE__ */ P("thead", {
|
|
890
890
|
className: "bg-muted/50",
|
|
891
891
|
children: /* @__PURE__ */ F("tr", { children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RefundsPage.js","names":[],"sources":["../../../../../src/billing/modules/billing/pages/RefundsPage.tsx"],"sourcesContent":["/**\n * Billing Module - Refunds Page\n * Admin page for managing refund records\n */\n\nimport { useState, useRef, useEffect, useMemo, useCallback, type FC } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport {\n RefreshCw,\n Plus,\n MoreHorizontal,\n Eye,\n Play,\n SkipForward,\n RotateCcw,\n ChevronLeft,\n ChevronRight,\n X,\n Search,\n Filter,\n AlertTriangle,\n CheckCircle,\n Clock,\n XCircle,\n Loader2,\n Ban,\n Download,\n // Calendar - available but not currently used\n} from 'lucide-react';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n useRefunds,\n type RefundRecord,\n type RefundRecordStatus,\n type RefundSource,\n type RefundRecordFilters,\n} from '../hooks';\nimport {\n GetTransactionDocument,\n type GetTransactionQuery,\n} from '../../../../generated/global-operations';\nimport { AccessDenied, PageHeader, EmptyState, ServerError } from '../../../shared/components';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst STATUS_CONFIG: Record<\n RefundRecordStatus,\n { label: string; color: string; icon: FC<{ className?: string }> }\n> = {\n PENDING: { label: 'Pending', color: 'yellow', icon: Clock },\n PROCESSING: { label: 'Processing', color: 'blue', icon: Loader2 },\n COMPLETED: { label: 'Completed', color: 'green', icon: CheckCircle },\n FAILED: { label: 'Failed', color: 'red', icon: XCircle },\n SKIPPED: { label: 'Skipped', color: 'gray', icon: Ban },\n};\n\nconst SOURCE_LABELS: Record<RefundSource, string> = {\n MANUAL: 'Manual',\n WEBHOOK_RECOVERY: 'Webhook Recovery',\n SUBSCRIPTION_CANCEL: 'Subscription Cancel',\n DUPLICATE_PAYMENT: 'Duplicate Payment',\n};\n\nconst PROVIDER_OPTIONS = ['stripe', 'razorpay'];\n\n// ============================================================================\n// Helper Components\n// ============================================================================\n\nconst getStatusBadgeClasses = (color: string): string => {\n const classes: Record<string, string> = {\n yellow: `${statusTokens.warning.bg} ${statusTokens.warning.text}`,\n blue: `${statusTokens.info.bg} ${statusTokens.info.text}`,\n green: `${statusTokens.success.bg} ${statusTokens.success.text}`,\n red: `${statusTokens.error.bg} ${statusTokens.error.text}`,\n gray: `${statusTokens.neutral.bg} ${statusTokens.neutral.text}`,\n };\n return classes[color] || classes.gray;\n};\n\n// Actions Dropdown\ninterface RefundActionsDropdownProps {\n record: RefundRecord;\n onViewDetails: () => void;\n onProcess: () => void;\n onSkip: () => void;\n onRetry: () => void;\n isProcessing: boolean;\n}\n\nconst RefundActionsDropdown: FC<RefundActionsDropdownProps> = ({\n record,\n onViewDetails,\n onProcess,\n onSkip,\n onRetry,\n isProcessing,\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n const canProcess = record.status === 'PENDING';\n const canSkip = record.status === 'PENDING';\n const canRetry = record.status === 'FAILED';\n\n return (\n <div className=\"relative\" ref={dropdownRef}>\n <button\n onClick={() => setIsOpen(!isOpen)}\n disabled={isProcessing}\n className=\"p-1.5 rounded-md hover:bg-muted/50 transition-colors disabled:opacity-50\"\n aria-label=\"Refund actions\"\n >\n {isProcessing ? (\n <Loader2 className=\"w-4 h-4 animate-spin text-muted-foreground\" />\n ) : (\n <MoreHorizontal className=\"w-4 h-4 text-muted-foreground\" />\n )}\n </button>\n\n {isOpen && (\n <div className=\"absolute right-0 top-full mt-1 z-50 min-w-[160px] bg-popover border border-border rounded-md shadow-lg py-1\">\n <button\n onClick={() => {\n onViewDetails();\n setIsOpen(false);\n }}\n className=\"w-full flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-muted/50 transition-colors\"\n >\n <Eye className=\"w-4 h-4\" />\n View Details\n </button>\n\n {canProcess && (\n <button\n onClick={() => {\n onProcess();\n setIsOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm ${statusTokens.success.text} hover:bg-muted/50 transition-colors`}\n >\n <Play className=\"w-4 h-4\" />\n Process Refund\n </button>\n )}\n\n {canSkip && (\n <button\n onClick={() => {\n onSkip();\n setIsOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm ${statusTokens.warning.text} hover:bg-muted/50 transition-colors`}\n >\n <SkipForward className=\"w-4 h-4\" />\n Skip Refund\n </button>\n )}\n\n {canRetry && (\n <button\n onClick={() => {\n onRetry();\n setIsOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm ${statusTokens.info.text} hover:bg-muted/50 transition-colors`}\n >\n <RotateCcw className=\"w-4 h-4\" />\n Retry Refund\n </button>\n )}\n </div>\n )}\n </div>\n );\n};\n\n// Create Refund Modal\ninterface CreateRefundModalProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: CreateRefundFormData) => Promise<void>;\n isSubmitting: boolean;\n}\n\ninterface CreateRefundFormData {\n billingAccountId?: string;\n provider: string;\n gatewayPaymentId: string;\n gatewayOrderId?: string;\n amount: number;\n currency: string;\n purchaseType?: string;\n purchaseDetail?: string;\n reason?: string;\n notes?: string;\n transactionId?: string;\n}\n\ninterface LookedUpTransaction {\n id: string;\n billingAccountId: string;\n amount: number;\n totalAmount: number; // amount + tax (what was actually charged)\n currency: string;\n description: string | null;\n razorpayPaymentId: string | null;\n stripePaymentId: string | null;\n metadata: Record<string, unknown> | null;\n}\n\nconst EMPTY_FORM: CreateRefundFormData = {\n provider: 'stripe',\n gatewayPaymentId: '',\n amount: 0,\n currency: 'USD',\n};\n\nconst CreateRefundModal: FC<CreateRefundModalProps> = ({\n isOpen,\n onClose,\n onSubmit,\n isSubmitting,\n}) => {\n const client = useApolloClient();\n const [transactionId, setTransactionId] = useState('');\n const [lookedUp, setLookedUp] = useState<LookedUpTransaction | null>(null);\n const [lookupError, setLookupError] = useState<string | null>(null);\n const [isLookingUp, setIsLookingUp] = useState(false);\n const [formData, setFormData] = useState<CreateRefundFormData>(EMPTY_FORM);\n\n const reset = () => {\n setTransactionId('');\n setLookedUp(null);\n setLookupError(null);\n setFormData(EMPTY_FORM);\n };\n\n const handleClose = () => {\n reset();\n onClose();\n };\n\n const lookupTransaction = useCallback(\n async (id: string) => {\n const trimmed = id.trim();\n if (!trimmed) return;\n setIsLookingUp(true);\n setLookupError(null);\n setLookedUp(null);\n try {\n const { data } = await client.query<GetTransactionQuery>({\n query: GetTransactionDocument,\n variables: { id: trimmed },\n fetchPolicy: 'network-only',\n });\n const txn = data?.getTransaction ?? null;\n if (!txn) {\n setLookupError('Transaction not found. Check the ID and try again.');\n setFormData(EMPTY_FORM);\n return;\n }\n const meta = txn.metadata as Record<string, unknown> | null;\n const totalAmount = typeof meta?.total === 'number' ? meta.total : txn.amount;\n const lookedUpWithTotal: LookedUpTransaction = {\n id: txn.id,\n billingAccountId: txn.billingAccountId,\n amount: txn.amount,\n totalAmount,\n currency: txn.currency,\n description: txn.description ?? null,\n razorpayPaymentId: txn.razorpayPaymentId ?? null,\n stripePaymentId: txn.stripePaymentId ?? null,\n metadata: txn.metadata ?? null,\n };\n setLookedUp(lookedUpWithTotal);\n const provider = txn.razorpayPaymentId ? 'razorpay' : 'stripe';\n const gatewayPaymentId = txn.razorpayPaymentId ?? txn.stripePaymentId ?? '';\n const purchaseType = (meta?.purchaseType as string) ?? '';\n const purchaseDetail = txn.description ?? '';\n setFormData({\n transactionId: txn.id,\n billingAccountId: txn.billingAccountId,\n provider,\n gatewayPaymentId,\n currency: txn.currency,\n amount: totalAmount,\n purchaseType,\n purchaseDetail,\n reason: '',\n notes: '',\n });\n } catch {\n setLookupError('Failed to look up transaction. Ensure the ID is correct.');\n setFormData(EMPTY_FORM);\n } finally {\n setIsLookingUp(false);\n }\n },\n [client]\n );\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n await onSubmit(formData);\n reset();\n };\n\n if (!isOpen) return null;\n\n const maxAmount = lookedUp?.totalAmount ?? undefined;\n const isAmountExceedingMax = maxAmount !== undefined && formData.amount > maxAmount + 0.01;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={handleClose} />\n <div className=\"relative bg-background border border-border rounded-lg shadow-xl max-w-lg w-full mx-4 max-h-[90vh] overflow-auto\">\n <div className=\"flex items-center justify-between p-4 border-b border-border\">\n <h2 className=\"text-lg font-semibold\">Create Manual Refund</h2>\n <button\n onClick={handleClose}\n className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\"\n >\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n\n <form onSubmit={handleSubmit} className=\"p-4 space-y-4\">\n {/* Step 1: Transaction ID lookup */}\n <div>\n <label className=\"block text-sm font-medium mb-1\">Transaction ID *</label>\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={transactionId}\n onChange={(e) => {\n setTransactionId(e.target.value);\n setLookedUp(null);\n setLookupError(null);\n }}\n onBlur={() => lookupTransaction(transactionId)}\n className=\"flex-1 px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"Paste transaction UUID...\"\n required\n />\n <button\n type=\"button\"\n onClick={() => lookupTransaction(transactionId)}\n disabled={!transactionId.trim() || isLookingUp}\n className=\"px-3 py-2 border border-border rounded-md hover:bg-muted/50 transition-colors disabled:opacity-50 flex items-center gap-1 text-sm\"\n >\n {isLookingUp ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Search className=\"w-4 h-4\" />\n )}\n </button>\n </div>\n {lookupError && <p className=\"text-xs text-status-error-text mt-1\">{lookupError}</p>}\n {isLookingUp && (\n <p className=\"text-xs text-muted-foreground mt-1\">Looking up transaction...</p>\n )}\n </div>\n\n {/* Auto-filled fields — shown once transaction is found */}\n {lookedUp && (\n <>\n {/* Transaction summary banner */}\n <div className=\"p-3 bg-status-success-bg-subtle border border-border-subtle rounded-md text-sm space-y-1\">\n <div className=\"flex items-center gap-2 text-status-success-text font-medium\">\n <CheckCircle className=\"w-4 h-4\" />\n Transaction found\n </div>\n <div className=\"text-muted-foreground grid grid-cols-2 gap-x-4 gap-y-0.5 mt-1\">\n <span>\n Account:{' '}\n <span className=\"text-foreground font-mono text-xs\">\n {lookedUp.billingAccountId.slice(0, 16)}…\n </span>\n </span>\n <span>\n Paid:{' '}\n <span className=\"text-foreground\">\n {formatCurrency(lookedUp.totalAmount, lookedUp.currency)}\n </span>\n </span>\n <span>\n Provider:{' '}\n <span className=\"text-foreground capitalize\">{formData.provider}</span>\n </span>\n <span>\n Currency: <span className=\"text-foreground\">{lookedUp.currency}</span>\n </span>\n </div>\n </div>\n\n {/* Read-only auto-filled fields */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Provider\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm capitalize\">\n {formData.provider}\n </div>\n </div>\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Currency\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm\">\n {formData.currency}\n </div>\n </div>\n </div>\n\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Gateway Payment ID\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm font-mono truncate\">\n {formData.gatewayPaymentId}\n </div>\n </div>\n\n {formData.purchaseType && (\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Purchase Type\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm\">\n {formData.purchaseType}\n </div>\n </div>\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Purchase Detail\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm truncate\">\n {formData.purchaseDetail || '—'}\n </div>\n </div>\n </div>\n )}\n\n {/* Editable: Amount (capped at transaction amount) */}\n <div>\n <label className=\"block text-sm font-medium mb-1\">\n Refund Amount *\n <span className=\"text-xs text-muted-foreground ml-1\">\n (max {formatCurrency(maxAmount!, formData.currency)})\n </span>\n </label>\n <input\n type=\"number\"\n step=\"0.01\"\n min=\"0.01\"\n max={maxAmount}\n value={formData.amount}\n onChange={(e) =>\n setFormData({ ...formData, amount: parseFloat(e.target.value) || 0 })\n }\n className={`w-full px-3 py-2 border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary ${\n isAmountExceedingMax ? 'border-status-error-text' : 'border-border'\n }`}\n required\n />\n {isAmountExceedingMax && (\n <p className=\"text-xs text-status-error-text mt-1\">\n Amount cannot exceed the original payment of{' '}\n {formatCurrency(maxAmount!, formData.currency)}\n </p>\n )}\n </div>\n\n {/* Editable: Reason & Notes */}\n <div>\n <label className=\"block text-sm font-medium mb-1\">Reason</label>\n <input\n type=\"text\"\n value={formData.reason || ''}\n onChange={(e) => setFormData({ ...formData, reason: e.target.value })}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"Reason for refund\"\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-1\">Notes</label>\n <textarea\n value={formData.notes || ''}\n onChange={(e) => setFormData({ ...formData, notes: e.target.value })}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary resize-none\"\n rows={2}\n placeholder=\"Internal notes\"\n />\n </div>\n </>\n )}\n\n <div className=\"flex gap-2 pt-2\">\n <button\n type=\"button\"\n onClick={handleClose}\n className=\"flex-1 py-2 px-4 border border-border rounded-md hover:bg-muted/50 transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={\n isSubmitting ||\n !lookedUp ||\n !formData.gatewayPaymentId ||\n formData.amount <= 0 ||\n isAmountExceedingMax\n }\n className=\"flex-1 py-2 px-4 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2\"\n >\n {isSubmitting && <Loader2 className=\"w-4 h-4 animate-spin\" />}\n Create Refund\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\n// Skip Refund Modal\ninterface SkipRefundModalProps {\n isOpen: boolean;\n record: RefundRecord | null;\n onClose: () => void;\n onSubmit: (reason: string) => Promise<void>;\n isSubmitting: boolean;\n}\n\nconst SkipRefundModal: FC<SkipRefundModalProps> = ({\n isOpen,\n record,\n onClose,\n onSubmit,\n isSubmitting,\n}) => {\n const [reason, setReason] = useState('');\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n await onSubmit(reason);\n setReason('');\n };\n\n if (!isOpen || !record) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={onClose} />\n <div className=\"relative bg-background border border-border rounded-lg shadow-xl max-w-md w-full mx-4\">\n <div className=\"flex items-center justify-between p-4 border-b border-border\">\n <h2 className=\"text-lg font-semibold\">Skip Refund</h2>\n <button onClick={onClose} className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\">\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n\n <form onSubmit={handleSubmit} className=\"p-4 space-y-4\">\n <div\n className={`p-3 ${statusTokens.warning.bg} border ${statusTokens.warning.border} rounded-md`}\n >\n <div className=\"flex items-start gap-2\">\n <AlertTriangle className={`w-5 h-5 ${statusTokens.warning.icon} mt-0.5`} />\n <div>\n <p className={`text-sm font-medium ${statusTokens.warning.text}`}>\n You are about to skip this refund\n </p>\n <p className={`text-sm ${statusTokens.warning.text} mt-1`}>\n Amount: {formatCurrency(record.amount, record.currency)}\n </p>\n </div>\n </div>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-1\">Reason for skipping *</label>\n <textarea\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background resize-none\"\n rows={3}\n placeholder=\"Explain why this refund is being skipped...\"\n required\n />\n </div>\n\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"flex-1 py-2 px-4 border border-border rounded-md hover:bg-muted/50 transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting || !reason.trim()}\n className={`flex-1 py-2 px-4 ${statusTokens.warning.dot} text-primary-foreground rounded-md hover:opacity-90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2`}\n >\n {isSubmitting && <Loader2 className=\"w-4 h-4 animate-spin\" />}\n Skip Refund\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\n// Refund Details Modal\ninterface RefundDetailsModalProps {\n record: RefundRecord | null;\n onClose: () => void;\n}\n\nconst RefundDetailsModal: FC<RefundDetailsModalProps> = ({ record, onClose }) => {\n if (!record) return null;\n\n const StatusIcon = STATUS_CONFIG[record.status].icon;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={onClose} />\n <div className=\"relative bg-background border border-border rounded-lg shadow-xl max-w-lg w-full mx-4 max-h-[90vh] overflow-auto\">\n <div className=\"flex items-center justify-between p-4 border-b border-border\">\n <h2 className=\"text-lg font-semibold\">Refund Details</h2>\n <button onClick={onClose} className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\">\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n\n <div className=\"p-4 space-y-4\">\n {/* Status Banner */}\n <div\n className={`flex items-center gap-2 p-3 rounded-md ${getStatusBadgeClasses(STATUS_CONFIG[record.status].color)}`}\n >\n <StatusIcon className=\"w-5 h-5\" />\n <span className=\"font-medium\">{STATUS_CONFIG[record.status].label}</span>\n </div>\n\n {/* Basic Info */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Refund ID</p>\n <p className=\"text-sm font-mono break-all\">{record.id}</p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Source</p>\n <p className=\"text-sm\">{SOURCE_LABELS[record.source]}</p>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Amount</p>\n <p className=\"text-lg font-semibold\">\n {formatCurrency(record.amount, record.currency)}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Provider</p>\n <p className=\"text-sm capitalize\">{record.provider}</p>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Gateway Payment ID</p>\n <p className=\"text-sm font-mono break-all\">{record.gatewayPaymentId}</p>\n </div>\n {record.gatewayOrderId && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Gateway Order ID</p>\n <p className=\"text-sm font-mono break-all\">{record.gatewayOrderId}</p>\n </div>\n )}\n </div>\n\n {record.gatewayRefundId && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Gateway Refund ID</p>\n <p className=\"text-sm font-mono break-all\">{record.gatewayRefundId}</p>\n </div>\n )}\n\n {(record.purchaseType || record.purchaseDetail) && (\n <div className=\"grid grid-cols-2 gap-4\">\n {record.purchaseType && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Purchase Type</p>\n <p className=\"text-sm\">{record.purchaseType}</p>\n </div>\n )}\n {record.purchaseDetail && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Purchase Detail</p>\n <p className=\"text-sm\">{record.purchaseDetail}</p>\n </div>\n )}\n </div>\n )}\n\n {record.reason && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Reason</p>\n <p className=\"text-sm\">{record.reason}</p>\n </div>\n )}\n\n {record.notes && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Notes</p>\n <p className=\"text-sm\">{record.notes}</p>\n </div>\n )}\n\n {record.errorMessage && (\n <div\n className={`p-3 ${statusTokens.error.bg} border ${statusTokens.error.border} rounded-md`}\n >\n <p className={`text-xs ${statusTokens.error.text} mb-1`}>Error Message</p>\n <p className={`text-sm ${statusTokens.error.text}`}>{record.errorMessage}</p>\n </div>\n )}\n\n <div className=\"grid grid-cols-2 gap-4 pt-2 border-t border-border\">\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Created At</p>\n <p className=\"text-sm\">{formatDateTime(record.createdAt)}</p>\n </div>\n {record.processedAt && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Processed At</p>\n <p className=\"text-sm\">{formatDateTime(record.processedAt)}</p>\n </div>\n )}\n </div>\n\n {record.retryCount > 0 && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Retry Count</p>\n <p className=\"text-sm\">{record.retryCount}</p>\n </div>\n )}\n </div>\n\n <div className=\"p-4 border-t border-border\">\n <button\n onClick={onClose}\n className=\"w-full py-2 px-4 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Close\n </button>\n </div>\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\ntype TabStatus = 'PENDING' | 'COMPLETED' | 'FAILED' | 'ALL';\n\nconst TAB_CONFIG: Record<TabStatus, { label: string; status?: RefundRecordStatus }> = {\n PENDING: { label: 'Pending', status: 'PENDING' },\n COMPLETED: { label: 'Completed', status: 'COMPLETED' },\n FAILED: { label: 'Failed', status: 'FAILED' },\n ALL: { label: 'All', status: undefined },\n};\n\nexport const RefundsPage: 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 // State\n const [activeTab, setActiveTab] = useState<TabStatus>('PENDING');\n const [searchQuery, setSearchQuery] = useState('');\n const [filters, setFilters] = useState<RefundRecordFilters>({});\n const [showFilters, setShowFilters] = useState(false);\n const [selectedRecord, setSelectedRecord] = useState<RefundRecord | null>(null);\n const [showCreateModal, setShowCreateModal] = useState(false);\n const [showSkipModal, setShowSkipModal] = useState(false);\n const [recordToSkip, setRecordToSkip] = useState<RefundRecord | null>(null);\n const [actionLoadingId, setActionLoadingId] = useState<string | null>(null);\n\n // Build filters based on active tab - memoized to prevent infinite re-render loop\n // (useRefundRecords has a useEffect on options.filters, so a new object every render would cause\n // infinite updates if tabFilters were not stable)\n const tabFilters = useMemo<RefundRecordFilters>(\n () => ({\n ...filters,\n status: TAB_CONFIG[activeTab].status,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- filters is an object; spread ensures correct deps\n [\n activeTab,\n filters.source,\n filters.provider,\n filters.billingAccountId,\n filters.fromDate,\n filters.toDate,\n ]\n );\n\n // Data hooks - filters automatically applied based on active tab\n const {\n records,\n stats,\n pagination,\n isLoading,\n recordsError,\n unprocessedWebhooks,\n refetch,\n goToNextPage,\n goToPreviousPage,\n setFilters: updateFilters,\n createRefundRecord,\n processRefund,\n skipRefund,\n retryRefund,\n createRefundRecordsFromFailedWebhooks,\n isCreating,\n isProcessing,\n isCreatingFromWebhooks,\n } = useRefunds({ filters: tabFilters });\n\n // Permission check\n if (!permissions.canViewPayments) {\n return <AccessDenied message=\"You don't have permission to view refunds.\" />;\n }\n\n // Error handling\n if (recordsError && isServerError(recordsError)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load refunds. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n // Handlers\n const handleTabChange = (tab: TabStatus) => {\n setActiveTab(tab);\n // Reset filters when changing tabs\n setFilters({});\n setSearchQuery('');\n };\n\n const handleFilterChange = (key: keyof RefundRecordFilters, value: string | undefined) => {\n // Don't allow status filter changes as tabs control status\n if (key === 'status') return;\n\n const newFilters = { ...filters, [key]: value || undefined };\n setFilters(newFilters);\n updateFilters({ ...newFilters, status: TAB_CONFIG[activeTab].status });\n };\n\n const handleCreateRefund = async (data: CreateRefundFormData) => {\n try {\n await createRefundRecord(data);\n setShowCreateModal(false);\n } catch (error) {\n console.error('Failed to create refund:', error);\n }\n };\n\n const handleProcess = async (record: RefundRecord) => {\n setActionLoadingId(record.id);\n try {\n const result = await processRefund(record.id);\n if (!result.success && result.error) {\n console.error('Refund processing failed:', result.error);\n }\n } catch (error) {\n console.error('Failed to process refund:', error);\n } finally {\n setActionLoadingId(null);\n }\n };\n\n const handleSkip = async (reason: string) => {\n if (!recordToSkip) return;\n try {\n await skipRefund(recordToSkip.id, reason);\n setShowSkipModal(false);\n setRecordToSkip(null);\n } catch (error) {\n console.error('Failed to skip refund:', error);\n }\n };\n\n const handleRetry = async (record: RefundRecord) => {\n setActionLoadingId(record.id);\n try {\n const result = await retryRefund(record.id);\n if (!result.success && result.error) {\n console.error('Refund retry failed:', result.error);\n }\n } catch (error) {\n console.error('Failed to retry refund:', error);\n } finally {\n setActionLoadingId(null);\n }\n };\n\n const handleCreateFromWebhooks = async () => {\n try {\n await createRefundRecordsFromFailedWebhooks();\n } catch (error) {\n console.error('Failed to create refunds from webhooks:', error);\n }\n };\n\n // Filter records by search query (client-side)\n const filteredRecords = searchQuery\n ? records.filter(\n (r) =>\n r.gatewayPaymentId.toLowerCase().includes(searchQuery.toLowerCase()) ||\n r.gatewayOrderId?.toLowerCase().includes(searchQuery.toLowerCase()) ||\n r.reason?.toLowerCase().includes(searchQuery.toLowerCase())\n )\n : records;\n\n if (isLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"grid grid-cols-5 gap-4\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-24 bg-muted animate-pulse rounded-lg\" />\n ))}\n </div>\n <div className=\"space-y-2\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-16 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.refunds.title', 'Refund Management')}\n description=\"View and manage refund records\"\n actions={\n <div className=\"flex items-center gap-2\">\n {unprocessedWebhooks.length > 0 && (\n <button\n onClick={handleCreateFromWebhooks}\n disabled={isCreatingFromWebhooks}\n className={`inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md border ${statusTokens.warning.border} ${statusTokens.warning.bg} ${statusTokens.warning.text} hover:opacity-90 transition-colors disabled:opacity-50`}\n >\n {isCreatingFromWebhooks ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Download className=\"w-4 h-4\" />\n )}\n Import {unprocessedWebhooks.length} Failed Webhooks\n </button>\n )}\n <button\n onClick={() => refetch()}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 transition-colors\"\n >\n <RefreshCw className=\"w-4 h-4\" />\n Refresh\n </button>\n <button\n onClick={() => setShowCreateModal(true)}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n Create Manual Refund\n </button>\n </div>\n }\n />\n\n {/* Tabs Navigation */}\n <div className=\"border-b border-border\">\n <div className=\"flex flex-wrap gap-2 sm:gap-0\">\n {(Object.keys(TAB_CONFIG) as TabStatus[]).map((tab) => {\n const isActive = activeTab === tab;\n const count = stats\n ? tab === 'PENDING'\n ? stats.pending\n : tab === 'COMPLETED'\n ? stats.completed\n : tab === 'FAILED'\n ? stats.failed\n : stats.pending +\n stats.processing +\n stats.completed +\n stats.failed +\n stats.skipped\n : 0;\n\n return (\n <button\n key={tab}\n onClick={() => handleTabChange(tab)}\n className={`flex items-center gap-2 px-4 py-3 text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${\n isActive\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground hover:border-border'\n }`}\n >\n {TAB_CONFIG[tab].label}\n {stats && (\n <span\n className={`px-2 py-0.5 text-xs rounded-full ${\n isActive ? 'bg-primary/10 text-primary' : 'bg-muted text-muted-foreground'\n }`}\n >\n {count}\n </span>\n )}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col sm:flex-row gap-4\">\n {/* Search */}\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <input\n type=\"text\"\n placeholder=\"Search by payment ID, order ID, or reason...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 border border-border rounded-md bg-background\"\n />\n </div>\n\n {/* Filter Toggle */}\n <button\n onClick={() => setShowFilters(!showFilters)}\n className={`inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md border transition-colors ${\n showFilters\n ? 'border-primary bg-primary/10 text-primary'\n : 'border-border bg-background hover:bg-muted/50'\n }`}\n >\n <Filter className=\"w-4 h-4\" />\n Filters\n {Object.values(filters).filter(Boolean).length > 0 && (\n <span className=\"px-1.5 py-0.5 text-xs rounded-full bg-primary text-primary-foreground\">\n {Object.values(filters).filter(Boolean).length}\n </span>\n )}\n </button>\n\n {/* Clear Filters */}\n {Object.values(filters).filter(Boolean).length > 0 && (\n <button\n onClick={() => {\n setFilters({});\n updateFilters({});\n }}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md text-muted-foreground hover:text-foreground transition-colors\"\n >\n <X className=\"w-4 h-4\" />\n Clear\n </button>\n )}\n </div>\n\n {/* Expanded Filters */}\n {showFilters && (\n <div className=\"p-4 border border-border rounded-lg bg-muted/30 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n <div>\n <label className=\"block text-sm font-medium mb-1\">Source</label>\n <select\n value={filters.source || ''}\n onChange={(e) => handleFilterChange('source', e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background\"\n >\n <option value=\"\">All Sources</option>\n {Object.entries(SOURCE_LABELS).map(([key, label]) => (\n <option key={key} value={key}>\n {label}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-1\">Provider</label>\n <select\n value={filters.provider || ''}\n onChange={(e) => handleFilterChange('provider', e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background\"\n >\n <option value=\"\">All Providers</option>\n {PROVIDER_OPTIONS.map((p) => (\n <option key={p} value={p}>\n {p.charAt(0).toUpperCase() + p.slice(1)}\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"sm:col-span-2 lg:col-span-1\">\n <label className=\"block text-sm font-medium mb-1\">Date Range</label>\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-2\">\n <input\n type=\"date\"\n value={filters.fromDate ? filters.fromDate.split('T')[0] : ''}\n onChange={(e) =>\n handleFilterChange(\n 'fromDate',\n e.target.value ? new Date(e.target.value).toISOString() : undefined\n )\n }\n className=\"w-full sm:flex-1 px-2 py-2 border border-border rounded-md bg-background text-sm\"\n />\n <span className=\"text-muted-foreground hidden sm:inline\">-</span>\n <input\n type=\"date\"\n value={filters.toDate ? filters.toDate.split('T')[0] : ''}\n onChange={(e) =>\n handleFilterChange(\n 'toDate',\n e.target.value ? new Date(e.target.value).toISOString() : undefined\n )\n }\n className=\"w-full sm:flex-1 px-2 py-2 border border-border rounded-md bg-background text-sm\"\n />\n </div>\n </div>\n </div>\n )}\n\n {/* Table */}\n {filteredRecords.length === 0 ? (\n <EmptyState\n icon={<RefreshCw className=\"w-6 h-6 text-muted-foreground\" />}\n title=\"No refund records found\"\n description={\n Object.values(filters).filter(Boolean).length > 0\n ? 'Try adjusting your filters.'\n : activeTab === 'PENDING'\n ? 'No pending refunds to process.'\n : 'Refund records will appear here when created.'\n }\n />\n ) : (\n <div className=\"border border-border rounded-lg overflow-hidden\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[640px]\">\n <thead className=\"bg-muted/50\">\n <tr>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap\">\n Date\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap\">\n Status\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap hidden sm:table-cell\">\n Source\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap hidden md:table-cell\">\n Provider\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap\">\n Payment ID\n </th>\n <th className=\"text-right text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap\">\n Amount\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap hidden lg:table-cell\">\n Reason\n </th>\n <th className=\"px-2 py-2 w-10\"></th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {filteredRecords.map((record) => {\n const StatusIcon = STATUS_CONFIG[record.status].icon;\n return (\n <tr key={record.id} className=\"hover:bg-muted/30 transition-colors\">\n <td className=\"px-3 py-2 text-sm text-foreground whitespace-nowrap\">\n <div className=\"flex flex-col\">\n <span>{formatDateTime(record.createdAt).split(' ')[0]}</span>\n <span className=\"text-xs text-muted-foreground\">\n {formatDateTime(record.createdAt).split(' ')[1]}\n </span>\n </div>\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n <span\n className={`inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded ${getStatusBadgeClasses(\n STATUS_CONFIG[record.status].color\n )}`}\n >\n <StatusIcon className=\"w-3 h-3\" />\n <span className=\"hidden sm:inline\">\n {STATUS_CONFIG[record.status].label}\n </span>\n </span>\n </td>\n <td className=\"px-3 py-2 text-sm text-muted-foreground whitespace-nowrap hidden sm:table-cell\">\n {SOURCE_LABELS[record.source]}\n </td>\n <td className=\"px-3 py-2 text-sm text-foreground capitalize whitespace-nowrap hidden md:table-cell\">\n {record.provider}\n </td>\n <td className=\"px-3 py-2 text-sm font-mono text-muted-foreground whitespace-nowrap max-w-[120px] sm:max-w-[200px] truncate\">\n {record.gatewayPaymentId}\n </td>\n <td className=\"px-3 py-2 text-sm text-right font-medium whitespace-nowrap\">\n {formatCurrency(record.amount, record.currency)}\n </td>\n <td className=\"px-3 py-2 text-sm text-muted-foreground max-w-[200px] truncate hidden lg:table-cell\">\n {record.reason || '-'}\n </td>\n <td className=\"px-2 py-2 text-center\">\n <RefundActionsDropdown\n record={record}\n onViewDetails={() => setSelectedRecord(record)}\n onProcess={() => handleProcess(record)}\n onSkip={() => {\n setRecordToSkip(record);\n setShowSkipModal(true);\n }}\n onRetry={() => handleRetry(record)}\n isProcessing={actionLoadingId === record.id}\n />\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n {/* Pagination */}\n {pagination.totalCount > 0 && (\n <div className=\"flex flex-col sm:flex-row items-center justify-between gap-4 border-t border-border pt-4\">\n <p className=\"text-sm text-muted-foreground text-center sm:text-left\">\n Showing {pagination.page * pagination.pageSize + 1} to{' '}\n {Math.min((pagination.page + 1) * pagination.pageSize, pagination.totalCount)} of{' '}\n {pagination.totalCount} refunds\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={goToPreviousPage}\n disabled={!pagination.hasPrevious}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n <ChevronLeft className=\"w-4 h-4\" />\n <span className=\"hidden sm:inline\">Previous</span>\n </button>\n <span className=\"text-sm text-muted-foreground px-2\">\n <span className=\"hidden sm:inline\">Page </span>\n {pagination.page + 1} <span className=\"hidden sm:inline\">of</span>\n <span className=\"sm:hidden\">/</span>{' '}\n {Math.ceil(pagination.totalCount / pagination.pageSize)}\n </span>\n <button\n onClick={goToNextPage}\n disabled={!pagination.hasMore}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n <span className=\"hidden sm:inline\">Next</span>\n <ChevronRight className=\"w-4 h-4\" />\n </button>\n </div>\n </div>\n )}\n\n {/* Modals */}\n <CreateRefundModal\n isOpen={showCreateModal}\n onClose={() => setShowCreateModal(false)}\n onSubmit={handleCreateRefund}\n isSubmitting={isCreating}\n />\n\n <SkipRefundModal\n isOpen={showSkipModal}\n record={recordToSkip}\n onClose={() => {\n setShowSkipModal(false);\n setRecordToSkip(null);\n }}\n onSubmit={handleSkip}\n isSubmitting={isProcessing}\n />\n\n <RefundDetailsModal record={selectedRecord} onClose={() => setSelectedRecord(null)} />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RefundsPage.js","names":[],"sources":["../../../../../src/billing/modules/billing/pages/RefundsPage.tsx"],"sourcesContent":["/**\n * Billing Module - Refunds Page\n * Admin page for managing refund records\n */\n\nimport { useState, useRef, useEffect, useMemo, useCallback, type FC } from 'react';\nimport { useApolloClient } from '@apollo/client/react';\nimport {\n RefreshCw,\n Plus,\n MoreHorizontal,\n Eye,\n Play,\n SkipForward,\n RotateCcw,\n ChevronLeft,\n ChevronRight,\n X,\n Search,\n Filter,\n AlertTriangle,\n CheckCircle,\n Clock,\n XCircle,\n Loader2,\n Ban,\n Download,\n // Calendar - available but not currently used\n} from 'lucide-react';\nimport { useBillingPermissions } from '../../../hooks/useBillingPermissions';\nimport {\n useRefunds,\n type RefundRecord,\n type RefundRecordStatus,\n type RefundSource,\n type RefundRecordFilters,\n} from '../hooks';\nimport {\n GetTransactionDocument,\n type GetTransactionQuery,\n} from '../../../../generated/global-operations';\nimport { AccessDenied, PageHeader, EmptyState, ServerError } from '../../../shared/components';\nimport { formatCurrency, formatDateTime, isServerError } from '../../../shared/utils';\nimport { statusTokens } from '../../../shared/utils/tokens';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst STATUS_CONFIG: Record<\n RefundRecordStatus,\n { label: string; color: string; icon: FC<{ className?: string }> }\n> = {\n PENDING: { label: 'Pending', color: 'yellow', icon: Clock },\n PROCESSING: { label: 'Processing', color: 'blue', icon: Loader2 },\n COMPLETED: { label: 'Completed', color: 'green', icon: CheckCircle },\n FAILED: { label: 'Failed', color: 'red', icon: XCircle },\n SKIPPED: { label: 'Skipped', color: 'gray', icon: Ban },\n};\n\nconst SOURCE_LABELS: Record<RefundSource, string> = {\n MANUAL: 'Manual',\n WEBHOOK_RECOVERY: 'Webhook Recovery',\n SUBSCRIPTION_CANCEL: 'Subscription Cancel',\n DUPLICATE_PAYMENT: 'Duplicate Payment',\n};\n\nconst PROVIDER_OPTIONS = ['stripe', 'razorpay'];\n\n// ============================================================================\n// Helper Components\n// ============================================================================\n\nconst getStatusBadgeClasses = (color: string): string => {\n const classes: Record<string, string> = {\n yellow: `${statusTokens.warning.bg} ${statusTokens.warning.text}`,\n blue: `${statusTokens.info.bg} ${statusTokens.info.text}`,\n green: `${statusTokens.success.bg} ${statusTokens.success.text}`,\n red: `${statusTokens.error.bg} ${statusTokens.error.text}`,\n gray: `${statusTokens.neutral.bg} ${statusTokens.neutral.text}`,\n };\n return classes[color] || classes.gray;\n};\n\n// Actions Dropdown\ninterface RefundActionsDropdownProps {\n record: RefundRecord;\n onViewDetails: () => void;\n onProcess: () => void;\n onSkip: () => void;\n onRetry: () => void;\n isProcessing: boolean;\n}\n\nconst RefundActionsDropdown: FC<RefundActionsDropdownProps> = ({\n record,\n onViewDetails,\n onProcess,\n onSkip,\n onRetry,\n isProcessing,\n}) => {\n const [isOpen, setIsOpen] = useState(false);\n const dropdownRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n setIsOpen(false);\n }\n };\n document.addEventListener('mousedown', handleClickOutside);\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, []);\n\n const canProcess = record.status === 'PENDING';\n const canSkip = record.status === 'PENDING';\n const canRetry = record.status === 'FAILED';\n\n return (\n <div className=\"relative\" ref={dropdownRef}>\n <button\n onClick={() => setIsOpen(!isOpen)}\n disabled={isProcessing}\n className=\"p-1.5 rounded-md hover:bg-muted/50 transition-colors disabled:opacity-50\"\n aria-label=\"Refund actions\"\n >\n {isProcessing ? (\n <Loader2 className=\"w-4 h-4 animate-spin text-muted-foreground\" />\n ) : (\n <MoreHorizontal className=\"w-4 h-4 text-muted-foreground\" />\n )}\n </button>\n\n {isOpen && (\n <div className=\"absolute right-0 top-full mt-1 z-50 min-w-[160px] bg-popover border border-border rounded-md shadow-lg py-1\">\n <button\n onClick={() => {\n onViewDetails();\n setIsOpen(false);\n }}\n className=\"w-full flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-muted/50 transition-colors\"\n >\n <Eye className=\"w-4 h-4\" />\n View Details\n </button>\n\n {canProcess && (\n <button\n onClick={() => {\n onProcess();\n setIsOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm ${statusTokens.success.text} hover:bg-muted/50 transition-colors`}\n >\n <Play className=\"w-4 h-4\" />\n Process Refund\n </button>\n )}\n\n {canSkip && (\n <button\n onClick={() => {\n onSkip();\n setIsOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm ${statusTokens.warning.text} hover:bg-muted/50 transition-colors`}\n >\n <SkipForward className=\"w-4 h-4\" />\n Skip Refund\n </button>\n )}\n\n {canRetry && (\n <button\n onClick={() => {\n onRetry();\n setIsOpen(false);\n }}\n className={`w-full flex items-center gap-2 px-3 py-2 text-sm ${statusTokens.info.text} hover:bg-muted/50 transition-colors`}\n >\n <RotateCcw className=\"w-4 h-4\" />\n Retry Refund\n </button>\n )}\n </div>\n )}\n </div>\n );\n};\n\n// Create Refund Modal\ninterface CreateRefundModalProps {\n isOpen: boolean;\n onClose: () => void;\n onSubmit: (data: CreateRefundFormData) => Promise<void>;\n isSubmitting: boolean;\n}\n\ninterface CreateRefundFormData {\n billingAccountId?: string;\n provider: string;\n gatewayPaymentId: string;\n gatewayOrderId?: string;\n amount: number;\n currency: string;\n purchaseType?: string;\n purchaseDetail?: string;\n reason?: string;\n notes?: string;\n transactionId?: string;\n}\n\ninterface LookedUpTransaction {\n id: string;\n billingAccountId: string;\n amount: number;\n totalAmount: number; // amount + tax (what was actually charged)\n currency: string;\n description: string | null;\n razorpayPaymentId: string | null;\n stripePaymentId: string | null;\n metadata: Record<string, unknown> | null;\n}\n\nconst EMPTY_FORM: CreateRefundFormData = {\n provider: 'stripe',\n gatewayPaymentId: '',\n amount: 0,\n currency: 'USD',\n};\n\nconst CreateRefundModal: FC<CreateRefundModalProps> = ({\n isOpen,\n onClose,\n onSubmit,\n isSubmitting,\n}) => {\n const client = useApolloClient();\n const [transactionId, setTransactionId] = useState('');\n const [lookedUp, setLookedUp] = useState<LookedUpTransaction | null>(null);\n const [lookupError, setLookupError] = useState<string | null>(null);\n const [isLookingUp, setIsLookingUp] = useState(false);\n const [formData, setFormData] = useState<CreateRefundFormData>(EMPTY_FORM);\n\n const reset = () => {\n setTransactionId('');\n setLookedUp(null);\n setLookupError(null);\n setFormData(EMPTY_FORM);\n };\n\n const handleClose = () => {\n reset();\n onClose();\n };\n\n const lookupTransaction = useCallback(\n async (id: string) => {\n const trimmed = id.trim();\n if (!trimmed) return;\n setIsLookingUp(true);\n setLookupError(null);\n setLookedUp(null);\n try {\n const { data } = await client.query<GetTransactionQuery>({\n query: GetTransactionDocument,\n variables: { id: trimmed },\n fetchPolicy: 'network-only',\n });\n const txn = data?.getTransaction ?? null;\n if (!txn) {\n setLookupError('Transaction not found. Check the ID and try again.');\n setFormData(EMPTY_FORM);\n return;\n }\n const meta = txn.metadata as Record<string, unknown> | null;\n const totalAmount = typeof meta?.total === 'number' ? meta.total : txn.amount;\n const lookedUpWithTotal: LookedUpTransaction = {\n id: txn.id,\n billingAccountId: txn.billingAccountId,\n amount: txn.amount,\n totalAmount,\n currency: txn.currency,\n description: txn.description ?? null,\n razorpayPaymentId: txn.razorpayPaymentId ?? null,\n stripePaymentId: txn.stripePaymentId ?? null,\n metadata: txn.metadata ?? null,\n };\n setLookedUp(lookedUpWithTotal);\n const provider = txn.razorpayPaymentId ? 'razorpay' : 'stripe';\n const gatewayPaymentId = txn.razorpayPaymentId ?? txn.stripePaymentId ?? '';\n const purchaseType = (meta?.purchaseType as string) ?? '';\n const purchaseDetail = txn.description ?? '';\n setFormData({\n transactionId: txn.id,\n billingAccountId: txn.billingAccountId,\n provider,\n gatewayPaymentId,\n currency: txn.currency,\n amount: totalAmount,\n purchaseType,\n purchaseDetail,\n reason: '',\n notes: '',\n });\n } catch {\n setLookupError('Failed to look up transaction. Ensure the ID is correct.');\n setFormData(EMPTY_FORM);\n } finally {\n setIsLookingUp(false);\n }\n },\n [client]\n );\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n await onSubmit(formData);\n reset();\n };\n\n if (!isOpen) return null;\n\n const maxAmount = lookedUp?.totalAmount ?? undefined;\n const isAmountExceedingMax = maxAmount !== undefined && formData.amount > maxAmount + 0.01;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={handleClose} />\n <div className=\"relative bg-background border border-border rounded-lg shadow-xl max-w-lg w-full mx-4 max-h-[90vh] overflow-auto\">\n <div className=\"flex items-center justify-between p-4 border-b border-border\">\n <h2 className=\"text-lg font-semibold\">Create Manual Refund</h2>\n <button\n onClick={handleClose}\n className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\"\n >\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n\n <form onSubmit={handleSubmit} className=\"p-4 space-y-4\">\n {/* Step 1: Transaction ID lookup */}\n <div>\n <label className=\"block text-sm font-medium mb-1\">Transaction ID *</label>\n <div className=\"flex gap-2\">\n <input\n type=\"text\"\n value={transactionId}\n onChange={(e) => {\n setTransactionId(e.target.value);\n setLookedUp(null);\n setLookupError(null);\n }}\n onBlur={() => lookupTransaction(transactionId)}\n className=\"flex-1 px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"Paste transaction UUID...\"\n required\n />\n <button\n type=\"button\"\n onClick={() => lookupTransaction(transactionId)}\n disabled={!transactionId.trim() || isLookingUp}\n className=\"px-3 py-2 border border-border rounded-md hover:bg-muted/50 transition-colors disabled:opacity-50 flex items-center gap-1 text-sm\"\n >\n {isLookingUp ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Search className=\"w-4 h-4\" />\n )}\n </button>\n </div>\n {lookupError && <p className=\"text-xs text-status-error-text mt-1\">{lookupError}</p>}\n {isLookingUp && (\n <p className=\"text-xs text-muted-foreground mt-1\">Looking up transaction...</p>\n )}\n </div>\n\n {/* Auto-filled fields — shown once transaction is found */}\n {lookedUp && (\n <>\n {/* Transaction summary banner */}\n <div className=\"p-3 bg-status-success-bg-subtle border border-border-subtle rounded-md text-sm space-y-1\">\n <div className=\"flex items-center gap-2 text-status-success-text font-medium\">\n <CheckCircle className=\"w-4 h-4\" />\n Transaction found\n </div>\n <div className=\"text-muted-foreground grid grid-cols-2 gap-x-4 gap-y-0.5 mt-1\">\n <span>\n Account:{' '}\n <span className=\"text-foreground font-mono text-xs\">\n {lookedUp.billingAccountId.slice(0, 16)}…\n </span>\n </span>\n <span>\n Paid:{' '}\n <span className=\"text-foreground\">\n {formatCurrency(lookedUp.totalAmount, lookedUp.currency)}\n </span>\n </span>\n <span>\n Provider:{' '}\n <span className=\"text-foreground capitalize\">{formData.provider}</span>\n </span>\n <span>\n Currency: <span className=\"text-foreground\">{lookedUp.currency}</span>\n </span>\n </div>\n </div>\n\n {/* Read-only auto-filled fields */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Provider\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm capitalize\">\n {formData.provider}\n </div>\n </div>\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Currency\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm\">\n {formData.currency}\n </div>\n </div>\n </div>\n\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Gateway Payment ID\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm font-mono truncate\">\n {formData.gatewayPaymentId}\n </div>\n </div>\n\n {formData.purchaseType && (\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Purchase Type\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm\">\n {formData.purchaseType}\n </div>\n </div>\n <div>\n <label className=\"block text-xs font-medium text-muted-foreground mb-1\">\n Purchase Detail\n </label>\n <div className=\"px-3 py-2 border border-border rounded-md bg-muted/30 text-sm truncate\">\n {formData.purchaseDetail || '—'}\n </div>\n </div>\n </div>\n )}\n\n {/* Editable: Amount (capped at transaction amount) */}\n <div>\n <label className=\"block text-sm font-medium mb-1\">\n Refund Amount *\n <span className=\"text-xs text-muted-foreground ml-1\">\n (max {formatCurrency(maxAmount!, formData.currency)})\n </span>\n </label>\n <input\n type=\"number\"\n step=\"0.01\"\n min=\"0.01\"\n max={maxAmount}\n value={formData.amount}\n onChange={(e) =>\n setFormData({ ...formData, amount: parseFloat(e.target.value) || 0 })\n }\n className={`w-full px-3 py-2 border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary ${\n isAmountExceedingMax ? 'border-status-error-text' : 'border-border'\n }`}\n required\n />\n {isAmountExceedingMax && (\n <p className=\"text-xs text-status-error-text mt-1\">\n Amount cannot exceed the original payment of{' '}\n {formatCurrency(maxAmount!, formData.currency)}\n </p>\n )}\n </div>\n\n {/* Editable: Reason & Notes */}\n <div>\n <label className=\"block text-sm font-medium mb-1\">Reason</label>\n <input\n type=\"text\"\n value={formData.reason || ''}\n onChange={(e) => setFormData({ ...formData, reason: e.target.value })}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary\"\n placeholder=\"Reason for refund\"\n />\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-1\">Notes</label>\n <textarea\n value={formData.notes || ''}\n onChange={(e) => setFormData({ ...formData, notes: e.target.value })}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background text-foreground focus:outline-none focus:ring-2 focus:ring-primary resize-none\"\n rows={2}\n placeholder=\"Internal notes\"\n />\n </div>\n </>\n )}\n\n <div className=\"flex gap-2 pt-2\">\n <button\n type=\"button\"\n onClick={handleClose}\n className=\"flex-1 py-2 px-4 border border-border rounded-md hover:bg-muted/50 transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={\n isSubmitting ||\n !lookedUp ||\n !formData.gatewayPaymentId ||\n formData.amount <= 0 ||\n isAmountExceedingMax\n }\n className=\"flex-1 py-2 px-4 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2\"\n >\n {isSubmitting && <Loader2 className=\"w-4 h-4 animate-spin\" />}\n Create Refund\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\n// Skip Refund Modal\ninterface SkipRefundModalProps {\n isOpen: boolean;\n record: RefundRecord | null;\n onClose: () => void;\n onSubmit: (reason: string) => Promise<void>;\n isSubmitting: boolean;\n}\n\nconst SkipRefundModal: FC<SkipRefundModalProps> = ({\n isOpen,\n record,\n onClose,\n onSubmit,\n isSubmitting,\n}) => {\n const [reason, setReason] = useState('');\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n await onSubmit(reason);\n setReason('');\n };\n\n if (!isOpen || !record) return null;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={onClose} />\n <div className=\"relative bg-background border border-border rounded-lg shadow-xl max-w-md w-full mx-4\">\n <div className=\"flex items-center justify-between p-4 border-b border-border\">\n <h2 className=\"text-lg font-semibold\">Skip Refund</h2>\n <button onClick={onClose} className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\">\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n\n <form onSubmit={handleSubmit} className=\"p-4 space-y-4\">\n <div\n className={`p-3 ${statusTokens.warning.bg} border ${statusTokens.warning.border} rounded-md`}\n >\n <div className=\"flex items-start gap-2\">\n <AlertTriangle className={`w-5 h-5 ${statusTokens.warning.icon} mt-0.5`} />\n <div>\n <p className={`text-sm font-medium ${statusTokens.warning.text}`}>\n You are about to skip this refund\n </p>\n <p className={`text-sm ${statusTokens.warning.text} mt-1`}>\n Amount: {formatCurrency(record.amount, record.currency)}\n </p>\n </div>\n </div>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-1\">Reason for skipping *</label>\n <textarea\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background resize-none\"\n rows={3}\n placeholder=\"Explain why this refund is being skipped...\"\n required\n />\n </div>\n\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={onClose}\n className=\"flex-1 py-2 px-4 border border-border rounded-md hover:bg-muted/50 transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting || !reason.trim()}\n className={`flex-1 py-2 px-4 ${statusTokens.warning.dot} text-primary-foreground rounded-md hover:opacity-90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2`}\n >\n {isSubmitting && <Loader2 className=\"w-4 h-4 animate-spin\" />}\n Skip Refund\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n};\n\n// Refund Details Modal\ninterface RefundDetailsModalProps {\n record: RefundRecord | null;\n onClose: () => void;\n}\n\nconst RefundDetailsModal: FC<RefundDetailsModalProps> = ({ record, onClose }) => {\n if (!record) return null;\n\n const StatusIcon = STATUS_CONFIG[record.status].icon;\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n <div className=\"absolute inset-0 bg-overlay-scrim\" onClick={onClose} />\n <div className=\"relative bg-background border border-border rounded-lg shadow-xl max-w-lg w-full mx-4 max-h-[90vh] overflow-auto\">\n <div className=\"flex items-center justify-between p-4 border-b border-border\">\n <h2 className=\"text-lg font-semibold\">Refund Details</h2>\n <button onClick={onClose} className=\"p-1 rounded-md hover:bg-muted/50 transition-colors\">\n <X className=\"w-5 h-5\" />\n </button>\n </div>\n\n <div className=\"p-4 space-y-4\">\n {/* Status Banner */}\n <div\n className={`flex items-center gap-2 p-3 rounded-md ${getStatusBadgeClasses(STATUS_CONFIG[record.status].color)}`}\n >\n <StatusIcon className=\"w-5 h-5\" />\n <span className=\"font-medium\">{STATUS_CONFIG[record.status].label}</span>\n </div>\n\n {/* Basic Info */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Refund ID</p>\n <p className=\"text-sm font-mono break-all\">{record.id}</p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Source</p>\n <p className=\"text-sm\">{SOURCE_LABELS[record.source]}</p>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Amount</p>\n <p className=\"text-lg font-semibold\">\n {formatCurrency(record.amount, record.currency)}\n </p>\n </div>\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Provider</p>\n <p className=\"text-sm capitalize\">{record.provider}</p>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Gateway Payment ID</p>\n <p className=\"text-sm font-mono break-all\">{record.gatewayPaymentId}</p>\n </div>\n {record.gatewayOrderId && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Gateway Order ID</p>\n <p className=\"text-sm font-mono break-all\">{record.gatewayOrderId}</p>\n </div>\n )}\n </div>\n\n {record.gatewayRefundId && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Gateway Refund ID</p>\n <p className=\"text-sm font-mono break-all\">{record.gatewayRefundId}</p>\n </div>\n )}\n\n {(record.purchaseType || record.purchaseDetail) && (\n <div className=\"grid grid-cols-2 gap-4\">\n {record.purchaseType && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Purchase Type</p>\n <p className=\"text-sm\">{record.purchaseType}</p>\n </div>\n )}\n {record.purchaseDetail && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Purchase Detail</p>\n <p className=\"text-sm\">{record.purchaseDetail}</p>\n </div>\n )}\n </div>\n )}\n\n {record.reason && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Reason</p>\n <p className=\"text-sm\">{record.reason}</p>\n </div>\n )}\n\n {record.notes && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Notes</p>\n <p className=\"text-sm\">{record.notes}</p>\n </div>\n )}\n\n {record.errorMessage && (\n <div\n className={`p-3 ${statusTokens.error.bg} border ${statusTokens.error.border} rounded-md`}\n >\n <p className={`text-xs ${statusTokens.error.text} mb-1`}>Error Message</p>\n <p className={`text-sm ${statusTokens.error.text}`}>{record.errorMessage}</p>\n </div>\n )}\n\n <div className=\"grid grid-cols-2 gap-4 pt-2 border-t border-border\">\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Created At</p>\n <p className=\"text-sm\">{formatDateTime(record.createdAt)}</p>\n </div>\n {record.processedAt && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Processed At</p>\n <p className=\"text-sm\">{formatDateTime(record.processedAt)}</p>\n </div>\n )}\n </div>\n\n {record.retryCount > 0 && (\n <div>\n <p className=\"text-xs text-muted-foreground mb-1\">Retry Count</p>\n <p className=\"text-sm\">{record.retryCount}</p>\n </div>\n )}\n </div>\n\n <div className=\"p-4 border-t border-border\">\n <button\n onClick={onClose}\n className=\"w-full py-2 px-4 bg-primary text-primary-foreground rounded-md hover:bg-primary/90 transition-colors\"\n >\n Close\n </button>\n </div>\n </div>\n </div>\n );\n};\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\ntype TabStatus = 'PENDING' | 'COMPLETED' | 'FAILED' | 'ALL';\n\nconst TAB_CONFIG: Record<TabStatus, { label: string; status?: RefundRecordStatus }> = {\n PENDING: { label: 'Pending', status: 'PENDING' },\n COMPLETED: { label: 'Completed', status: 'COMPLETED' },\n FAILED: { label: 'Failed', status: 'FAILED' },\n ALL: { label: 'All', status: undefined },\n};\n\nexport const RefundsPage: 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 // State\n const [activeTab, setActiveTab] = useState<TabStatus>('PENDING');\n const [searchQuery, setSearchQuery] = useState('');\n const [filters, setFilters] = useState<RefundRecordFilters>({});\n const [showFilters, setShowFilters] = useState(false);\n const [selectedRecord, setSelectedRecord] = useState<RefundRecord | null>(null);\n const [showCreateModal, setShowCreateModal] = useState(false);\n const [showSkipModal, setShowSkipModal] = useState(false);\n const [recordToSkip, setRecordToSkip] = useState<RefundRecord | null>(null);\n const [actionLoadingId, setActionLoadingId] = useState<string | null>(null);\n\n // Build filters based on active tab - memoized to prevent infinite re-render loop\n // (useRefundRecords has a useEffect on options.filters, so a new object every render would cause\n // infinite updates if tabFilters were not stable)\n const tabFilters = useMemo<RefundRecordFilters>(\n () => ({\n ...filters,\n status: TAB_CONFIG[activeTab].status,\n }),\n // eslint-disable-next-line react-hooks/exhaustive-deps -- filters is an object; spread ensures correct deps\n [\n activeTab,\n filters.source,\n filters.provider,\n filters.billingAccountId,\n filters.fromDate,\n filters.toDate,\n ]\n );\n\n // Data hooks - filters automatically applied based on active tab\n const {\n records,\n stats,\n pagination,\n isLoading,\n recordsError,\n unprocessedWebhooks,\n refetch,\n goToNextPage,\n goToPreviousPage,\n setFilters: updateFilters,\n createRefundRecord,\n processRefund,\n skipRefund,\n retryRefund,\n createRefundRecordsFromFailedWebhooks,\n isCreating,\n isProcessing,\n isCreatingFromWebhooks,\n } = useRefunds({ filters: tabFilters });\n\n // Permission check\n if (!permissions.canViewPayments) {\n return <AccessDenied message=\"You don't have permission to view refunds.\" />;\n }\n\n // Error handling\n if (recordsError && isServerError(recordsError)) {\n return (\n <div className=\"p-6\">\n <ServerError\n title=\"Server Unavailable\"\n message=\"Unable to load refunds. The server might be down or experiencing issues.\"\n onRetry={() => refetch()}\n showRetry\n />\n </div>\n );\n }\n\n // Handlers\n const handleTabChange = (tab: TabStatus) => {\n setActiveTab(tab);\n // Reset filters when changing tabs\n setFilters({});\n setSearchQuery('');\n };\n\n const handleFilterChange = (key: keyof RefundRecordFilters, value: string | undefined) => {\n // Don't allow status filter changes as tabs control status\n if (key === 'status') return;\n\n const newFilters = { ...filters, [key]: value || undefined };\n setFilters(newFilters);\n updateFilters({ ...newFilters, status: TAB_CONFIG[activeTab].status });\n };\n\n const handleCreateRefund = async (data: CreateRefundFormData) => {\n try {\n await createRefundRecord(data);\n setShowCreateModal(false);\n } catch (error) {\n console.error('Failed to create refund:', error);\n }\n };\n\n const handleProcess = async (record: RefundRecord) => {\n setActionLoadingId(record.id);\n try {\n const result = await processRefund(record.id);\n if (!result.success && result.error) {\n console.error('Refund processing failed:', result.error);\n }\n } catch (error) {\n console.error('Failed to process refund:', error);\n } finally {\n setActionLoadingId(null);\n }\n };\n\n const handleSkip = async (reason: string) => {\n if (!recordToSkip) return;\n try {\n await skipRefund(recordToSkip.id, reason);\n setShowSkipModal(false);\n setRecordToSkip(null);\n } catch (error) {\n console.error('Failed to skip refund:', error);\n }\n };\n\n const handleRetry = async (record: RefundRecord) => {\n setActionLoadingId(record.id);\n try {\n const result = await retryRefund(record.id);\n if (!result.success && result.error) {\n console.error('Refund retry failed:', result.error);\n }\n } catch (error) {\n console.error('Failed to retry refund:', error);\n } finally {\n setActionLoadingId(null);\n }\n };\n\n const handleCreateFromWebhooks = async () => {\n try {\n await createRefundRecordsFromFailedWebhooks();\n } catch (error) {\n console.error('Failed to create refunds from webhooks:', error);\n }\n };\n\n // Filter records by search query (client-side)\n const filteredRecords = searchQuery\n ? records.filter(\n (r) =>\n r.gatewayPaymentId.toLowerCase().includes(searchQuery.toLowerCase()) ||\n r.gatewayOrderId?.toLowerCase().includes(searchQuery.toLowerCase()) ||\n r.reason?.toLowerCase().includes(searchQuery.toLowerCase())\n )\n : records;\n\n if (isLoading) {\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"h-8 w-48 bg-muted animate-pulse rounded\" />\n <div className=\"grid grid-cols-5 gap-4\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-24 bg-muted animate-pulse rounded-lg\" />\n ))}\n </div>\n <div className=\"space-y-2\">\n {[1, 2, 3, 4, 5].map((i) => (\n <div key={i} className=\"h-16 bg-muted animate-pulse rounded\" />\n ))}\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <PageHeader\n title={tr('billing.refunds.title', 'Refund Management')}\n description=\"View and manage refund records\"\n actions={\n <div className=\"flex items-center gap-2\">\n {unprocessedWebhooks.length > 0 && (\n <button\n onClick={handleCreateFromWebhooks}\n disabled={isCreatingFromWebhooks}\n className={`inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md border ${statusTokens.warning.border} ${statusTokens.warning.bg} ${statusTokens.warning.text} hover:opacity-90 transition-colors disabled:opacity-50`}\n >\n {isCreatingFromWebhooks ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Download className=\"w-4 h-4\" />\n )}\n Import {unprocessedWebhooks.length} Failed Webhooks\n </button>\n )}\n <button\n onClick={() => refetch()}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 transition-colors\"\n >\n <RefreshCw className=\"w-4 h-4\" />\n Refresh\n </button>\n <button\n onClick={() => setShowCreateModal(true)}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n Create Manual Refund\n </button>\n </div>\n }\n />\n\n {/* Tabs Navigation */}\n <div className=\"border-b border-border\">\n <div className=\"flex flex-wrap gap-2 sm:gap-0\">\n {(Object.keys(TAB_CONFIG) as TabStatus[]).map((tab) => {\n const isActive = activeTab === tab;\n const count = stats\n ? tab === 'PENDING'\n ? stats.pending\n : tab === 'COMPLETED'\n ? stats.completed\n : tab === 'FAILED'\n ? stats.failed\n : stats.pending +\n stats.processing +\n stats.completed +\n stats.failed +\n stats.skipped\n : 0;\n\n return (\n <button\n key={tab}\n onClick={() => handleTabChange(tab)}\n className={`flex items-center gap-2 px-4 py-3 text-sm font-medium border-b-2 transition-colors whitespace-nowrap ${\n isActive\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground hover:border-border'\n }`}\n >\n {TAB_CONFIG[tab].label}\n {stats && (\n <span\n className={`px-2 py-0.5 text-xs rounded-full ${\n isActive ? 'bg-primary/10 text-primary' : 'bg-muted text-muted-foreground'\n }`}\n >\n {count}\n </span>\n )}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col sm:flex-row gap-4\">\n {/* Search */}\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground\" />\n <input\n type=\"text\"\n placeholder=\"Search by payment ID, order ID, or reason...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 border border-border rounded-md bg-background\"\n />\n </div>\n\n {/* Filter Toggle */}\n <button\n onClick={() => setShowFilters(!showFilters)}\n className={`inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md border transition-colors ${\n showFilters\n ? 'border-primary bg-primary/10 text-primary'\n : 'border-border bg-background hover:bg-muted/50'\n }`}\n >\n <Filter className=\"w-4 h-4\" />\n Filters\n {Object.values(filters).filter(Boolean).length > 0 && (\n <span className=\"px-1.5 py-0.5 text-xs rounded-full bg-primary text-primary-foreground\">\n {Object.values(filters).filter(Boolean).length}\n </span>\n )}\n </button>\n\n {/* Clear Filters */}\n {Object.values(filters).filter(Boolean).length > 0 && (\n <button\n onClick={() => {\n setFilters({});\n updateFilters({});\n }}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-md text-muted-foreground hover:text-foreground transition-colors\"\n >\n <X className=\"w-4 h-4\" />\n Clear\n </button>\n )}\n </div>\n\n {/* Expanded Filters */}\n {showFilters && (\n <div className=\"p-4 border border-border rounded-lg bg-muted/30 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\">\n <div>\n <label className=\"block text-sm font-medium mb-1\">Source</label>\n <select\n value={filters.source || ''}\n onChange={(e) => handleFilterChange('source', e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background\"\n >\n <option value=\"\">All Sources</option>\n {Object.entries(SOURCE_LABELS).map(([key, label]) => (\n <option key={key} value={key}>\n {label}\n </option>\n ))}\n </select>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium mb-1\">Provider</label>\n <select\n value={filters.provider || ''}\n onChange={(e) => handleFilterChange('provider', e.target.value)}\n className=\"w-full px-3 py-2 border border-border rounded-md bg-background\"\n >\n <option value=\"\">All Providers</option>\n {PROVIDER_OPTIONS.map((p) => (\n <option key={p} value={p}>\n {p.charAt(0).toUpperCase() + p.slice(1)}\n </option>\n ))}\n </select>\n </div>\n\n <div className=\"sm:col-span-2 lg:col-span-1\">\n <label className=\"block text-sm font-medium mb-1\">Date Range</label>\n <div className=\"flex flex-col sm:flex-row items-start sm:items-center gap-2\">\n <input\n type=\"date\"\n value={filters.fromDate ? filters.fromDate.split('T')[0] : ''}\n onChange={(e) =>\n handleFilterChange(\n 'fromDate',\n e.target.value ? new Date(e.target.value).toISOString() : undefined\n )\n }\n className=\"w-full sm:flex-1 px-2 py-2 border border-border rounded-md bg-background text-sm\"\n />\n <span className=\"text-muted-foreground hidden sm:inline\">-</span>\n <input\n type=\"date\"\n value={filters.toDate ? filters.toDate.split('T')[0] : ''}\n onChange={(e) =>\n handleFilterChange(\n 'toDate',\n e.target.value ? new Date(e.target.value).toISOString() : undefined\n )\n }\n className=\"w-full sm:flex-1 px-2 py-2 border border-border rounded-md bg-background text-sm\"\n />\n </div>\n </div>\n </div>\n )}\n\n {/* Table */}\n {filteredRecords.length === 0 ? (\n <EmptyState\n icon={<RefreshCw className=\"w-6 h-6 text-muted-foreground\" />}\n title=\"No refund records found\"\n description={\n Object.values(filters).filter(Boolean).length > 0\n ? 'Try adjusting your filters.'\n : activeTab === 'PENDING'\n ? 'No pending refunds to process.'\n : 'Refund records will appear here when created.'\n }\n />\n ) : (\n <div className=\"border border-border rounded-lg overflow-hidden\">\n <div className=\"overflow-x-auto\">\n <table className=\"w-full min-w-[400px]\">\n <thead className=\"bg-muted/50\">\n <tr>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap\">\n Date\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap\">\n Status\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap hidden sm:table-cell\">\n Source\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap hidden md:table-cell\">\n Provider\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap\">\n Payment ID\n </th>\n <th className=\"text-right text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap\">\n Amount\n </th>\n <th className=\"text-left text-sm font-medium text-muted-foreground px-3 py-2 whitespace-nowrap hidden lg:table-cell\">\n Reason\n </th>\n <th className=\"px-2 py-2 w-10\"></th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {filteredRecords.map((record) => {\n const StatusIcon = STATUS_CONFIG[record.status].icon;\n return (\n <tr key={record.id} className=\"hover:bg-muted/30 transition-colors\">\n <td className=\"px-3 py-2 text-sm text-foreground whitespace-nowrap\">\n <div className=\"flex flex-col\">\n <span>{formatDateTime(record.createdAt).split(' ')[0]}</span>\n <span className=\"text-xs text-muted-foreground\">\n {formatDateTime(record.createdAt).split(' ')[1]}\n </span>\n </div>\n </td>\n <td className=\"px-3 py-2 whitespace-nowrap\">\n <span\n className={`inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded ${getStatusBadgeClasses(\n STATUS_CONFIG[record.status].color\n )}`}\n >\n <StatusIcon className=\"w-3 h-3\" />\n <span className=\"hidden sm:inline\">\n {STATUS_CONFIG[record.status].label}\n </span>\n </span>\n </td>\n <td className=\"px-3 py-2 text-sm text-muted-foreground whitespace-nowrap hidden sm:table-cell\">\n {SOURCE_LABELS[record.source]}\n </td>\n <td className=\"px-3 py-2 text-sm text-foreground capitalize whitespace-nowrap hidden md:table-cell\">\n {record.provider}\n </td>\n <td className=\"px-3 py-2 text-sm font-mono text-muted-foreground whitespace-nowrap max-w-[120px] sm:max-w-[200px] truncate\">\n {record.gatewayPaymentId}\n </td>\n <td className=\"px-3 py-2 text-sm text-right font-medium whitespace-nowrap\">\n {formatCurrency(record.amount, record.currency)}\n </td>\n <td className=\"px-3 py-2 text-sm text-muted-foreground max-w-[200px] truncate hidden lg:table-cell\">\n {record.reason || '-'}\n </td>\n <td className=\"px-2 py-2 text-center\">\n <RefundActionsDropdown\n record={record}\n onViewDetails={() => setSelectedRecord(record)}\n onProcess={() => handleProcess(record)}\n onSkip={() => {\n setRecordToSkip(record);\n setShowSkipModal(true);\n }}\n onRetry={() => handleRetry(record)}\n isProcessing={actionLoadingId === record.id}\n />\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </div>\n )}\n\n {/* Pagination */}\n {pagination.totalCount > 0 && (\n <div className=\"flex flex-col sm:flex-row items-center justify-between gap-4 border-t border-border pt-4\">\n <p className=\"text-sm text-muted-foreground text-center sm:text-left\">\n Showing {pagination.page * pagination.pageSize + 1} to{' '}\n {Math.min((pagination.page + 1) * pagination.pageSize, pagination.totalCount)} of{' '}\n {pagination.totalCount} refunds\n </p>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={goToPreviousPage}\n disabled={!pagination.hasPrevious}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n <ChevronLeft className=\"w-4 h-4\" />\n <span className=\"hidden sm:inline\">Previous</span>\n </button>\n <span className=\"text-sm text-muted-foreground px-2\">\n <span className=\"hidden sm:inline\">Page </span>\n {pagination.page + 1} <span className=\"hidden sm:inline\">of</span>\n <span className=\"sm:hidden\">/</span>{' '}\n {Math.ceil(pagination.totalCount / pagination.pageSize)}\n </span>\n <button\n onClick={goToNextPage}\n disabled={!pagination.hasMore}\n className=\"inline-flex items-center gap-1 px-3 py-1.5 text-sm font-medium rounded-md border border-border bg-background hover:bg-muted/50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors\"\n >\n <span className=\"hidden sm:inline\">Next</span>\n <ChevronRight className=\"w-4 h-4\" />\n </button>\n </div>\n </div>\n )}\n\n {/* Modals */}\n <CreateRefundModal\n isOpen={showCreateModal}\n onClose={() => setShowCreateModal(false)}\n onSubmit={handleCreateRefund}\n isSubmitting={isCreating}\n />\n\n <SkipRefundModal\n isOpen={showSkipModal}\n record={recordToSkip}\n onClose={() => {\n setShowSkipModal(false);\n setRecordToSkip(null);\n }}\n onSubmit={handleSkip}\n isSubmitting={isProcessing}\n />\n\n <RefundDetailsModal record={selectedRecord} onClose={() => setSelectedRecord(null)} />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -134,9 +134,9 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
134
134
|
type: "button",
|
|
135
135
|
onClick: () => M?.(e.id, e.quantity - 1),
|
|
136
136
|
disabled: e.quantity <= 1,
|
|
137
|
-
className: "p-
|
|
137
|
+
className: "p-2 rounded border border-border hover:bg-muted disabled:opacity-50 disabled:cursor-not-allowed min-w-[36px] min-h-[36px] flex items-center justify-center",
|
|
138
138
|
"aria-label": `Decrease ${e.name} quantity`,
|
|
139
|
-
children: /* @__PURE__ */ m(s, { className: "h-3 w-3" })
|
|
139
|
+
children: /* @__PURE__ */ m(s, { className: "h-3.5 w-3.5" })
|
|
140
140
|
}),
|
|
141
141
|
/* @__PURE__ */ m("span", {
|
|
142
142
|
className: "w-8 text-center text-sm font-medium",
|
|
@@ -145,9 +145,9 @@ var x = 100, S = ({ items: S, subtotal: C, discountAmount: w, taxAmount: T, taxR
|
|
|
145
145
|
/* @__PURE__ */ m("button", {
|
|
146
146
|
type: "button",
|
|
147
147
|
onClick: () => M?.(e.id, e.quantity + 1),
|
|
148
|
-
className: "p-
|
|
148
|
+
className: "p-2 rounded border border-border hover:bg-muted min-w-[36px] min-h-[36px] flex items-center justify-center",
|
|
149
149
|
"aria-label": `Increase ${e.name} quantity`,
|
|
150
|
-
children: /* @__PURE__ */ m(l, { className: "h-3 w-3" })
|
|
150
|
+
children: /* @__PURE__ */ m(l, { className: "h-3.5 w-3.5" })
|
|
151
151
|
})
|
|
152
152
|
]
|
|
153
153
|
}) : /* @__PURE__ */ h("span", {
|