diff --git a/src/apps/wallet-admin/src/home/tabs/payments/PaymentsListView.spec.tsx b/src/apps/wallet-admin/src/home/tabs/payments/PaymentsListView.spec.tsx
index de61dffad..127bd1134 100644
--- a/src/apps/wallet-admin/src/home/tabs/payments/PaymentsListView.spec.tsx
+++ b/src/apps/wallet-admin/src/home/tabs/payments/PaymentsListView.spec.tsx
@@ -221,10 +221,10 @@ describe('PaymentsListView', () => {
jest.clearAllMocks()
})
- it('defaults the engagement approver view to the On Hold (Admin) status filter', async () => {
+ it('defaults the approver view to the On Hold (Admin) status filter and both allowed categories', async () => {
render(
,
)
@@ -232,14 +232,13 @@ describe('PaymentsListView', () => {
expect(mockedGetPayments)
.toHaveBeenLastCalledWith(10, 0, {
- category: ['ENGAGEMENT_PAYMENT'],
+ categories: ['TASK_PAYMENT', 'ENGAGEMENT_PAYMENT'],
status: ['ON_HOLD_ADMIN'],
})
expect(mockFilterBar)
.toHaveBeenCalled()
expect(mockFilterBar.mock.calls.at(-1)?.[0].selectedValueOverrides)
.toEqual(expect.objectContaining({
- category: 'ENGAGEMENT_PAYMENT',
status: 'ON_HOLD_ADMIN',
}))
})
@@ -266,7 +265,7 @@ describe('PaymentsListView', () => {
it('applies the default approver status after switching from admin view', async () => {
render(
,
)
@@ -275,19 +274,18 @@ describe('PaymentsListView', () => {
expect(mockedGetPayments)
.toHaveBeenLastCalledWith(10, 0, {})
- fireEvent.click(screen.getByRole('button', { name: 'Engagement Approver View' }))
+ fireEvent.click(screen.getByRole('button', { name: 'Approver View' }))
await screen.findByText('No payments match your filters.')
expect(mockedGetPayments)
.toHaveBeenLastCalledWith(10, 0, {
- category: ['ENGAGEMENT_PAYMENT'],
+ categories: ['TASK_PAYMENT', 'ENGAGEMENT_PAYMENT'],
status: ['ON_HOLD_ADMIN'],
})
expect(mockFilterBar.mock.calls.at(-1)?.[0].selectedValueOverrides)
.toEqual(expect.objectContaining({
- category: 'ENGAGEMENT_PAYMENT',
status: 'ON_HOLD_ADMIN',
}))
})
@@ -314,7 +312,7 @@ describe('PaymentsListView', () => {
it('lets an explicit status filter override the default approver status', async () => {
render(
,
)
@@ -327,14 +325,13 @@ describe('PaymentsListView', () => {
await waitFor(() => {
expect(mockedGetPayments)
.toHaveBeenLastCalledWith(10, 0, {
- category: ['ENGAGEMENT_PAYMENT'],
+ categories: ['TASK_PAYMENT', 'ENGAGEMENT_PAYMENT'],
status: ['PAID'],
})
})
expect(mockFilterBar.mock.calls.at(-1)?.[0].selectedValueOverrides)
.toEqual(expect.objectContaining({
- category: 'ENGAGEMENT_PAYMENT',
status: 'PAID',
}))
})
@@ -367,7 +364,7 @@ describe('PaymentsListView', () => {
}))
})
- it('lets engagement approvers reject selected on hold admin payments with an audit note', async () => {
+ it('lets approvers reject selected on hold admin payments with an audit note', async () => {
mockedGetPayments.mockResolvedValue(paymentsResponse as any)
mockedGetMemberHandle.mockResolvedValue(new Map([
[111, 'sathya22in'],
@@ -376,7 +373,7 @@ describe('PaymentsListView', () => {
render(
,
)
diff --git a/src/apps/wallet-admin/src/home/tabs/payments/PaymentsListView.tsx b/src/apps/wallet-admin/src/home/tabs/payments/PaymentsListView.tsx
index af3992aef..7a7430579 100644
--- a/src/apps/wallet-admin/src/home/tabs/payments/PaymentsListView.tsx
+++ b/src/apps/wallet-admin/src/home/tabs/payments/PaymentsListView.tsx
@@ -19,11 +19,13 @@ import PaymentsTable from '../../../lib/components/payments-table/PaymentTable'
import styles from './Payments.module.scss'
-type PaymentRoleView = 'admin' | 'engagementApprover' | 'wiproTaasAdmin'
+type PaymentRoleView = 'admin' | 'paymentApprover' | 'wiproTaasAdmin'
type SelectedPaymentAction = 'approve' | 'reject'
+const taskPaymentCategory = 'TASK_PAYMENT'
const engagementPaymentCategory = 'ENGAGEMENT_PAYMENT'
const restrictedRoleDefaultStatus = 'ON_HOLD_ADMIN'
+const approverAllowedCategories = [taskPaymentCategory, engagementPaymentCategory]
const taasPaymentCategory = 'TAAS_PAYMENT'
const topgearPaymentCategory = 'TOPGEAR_PAYMENT'
const defaultPageSize = 10
@@ -172,8 +174,8 @@ const PaymentsListView: FC = (props: PaymentsListViewProp
const isWiproTaasAdmin = hasRole('Wipro TaaS Admin')
const hasPaymentAdminRole = hasRole('Payment Admin')
const isPaymentAdmin = hasPaymentAdminRole || isWiproTaasAdmin
- const isEngagementPaymentApprover = hasRole('Engagement Payment Approver')
- const canToggleRoleView = isPaymentAdmin && (isEngagementPaymentApprover)
+ const isPaymentApprover = hasRole('Payment Approver')
+ const canToggleRoleView = isPaymentAdmin && isPaymentApprover
const [confirmFlow, setConfirmFlow] = React.useState(undefined)
const [isConfirmFormValid, setIsConfirmFormValid] = React.useState(false)
const [winnings, setWinnings] = React.useState>([])
@@ -181,21 +183,17 @@ const PaymentsListView: FC = (props: PaymentsListViewProp
const selectedPaymentsCount = Object.keys(selectedPayments).length
const [isLoading, setIsLoading] = React.useState(false)
const [paymentRoleView, setPaymentRoleView] = React.useState(
- isPaymentAdmin ? 'admin' : 'engagementApprover',
+ isPaymentAdmin ? 'admin' : 'paymentApprover',
)
- const isEngagementApproverView = isEngagementPaymentApprover && (
- !isPaymentAdmin || paymentRoleView === 'engagementApprover'
+ const isApproverView = isPaymentApprover && (
+ !isPaymentAdmin || paymentRoleView === 'paymentApprover'
)
- const restrictedCategory = isEngagementApproverView
- ? engagementPaymentCategory
- : (isWiproTaasAdmin && !hasPaymentAdminRole ? taasPaymentCategory : undefined)
- const restrictedDefaultStatus = isEngagementApproverView ? restrictedRoleDefaultStatus : undefined
- const isRestrictedApproverView = isEngagementApproverView
+ const restrictedCategory = isWiproTaasAdmin && !hasPaymentAdminRole ? taasPaymentCategory : undefined
+ const restrictedDefaultStatus = isApproverView ? restrictedRoleDefaultStatus : undefined
+ const isRestrictedApproverView = isApproverView
const [filters, setFilters] = React.useState>({})
- // Remove the old hasSelectedStatusFilter declaration as we handle it directly below
-
const appliedFilters = React.useMemo>(() => {
// Strip 'all' sentinel values — never forward them to the API
const activeFilters = Object.fromEntries(
@@ -203,50 +201,77 @@ const PaymentsListView: FC = (props: PaymentsListViewProp
.filter(([, v]) => v.length > 0 && v[0] !== 'all'),
)
- if (!restrictedCategory) {
- return activeFilters
+ if (restrictedCategory) {
+ // WiproTaasAdmin scoped to a single category
+ let statusFilter: Record = {}
+ if (filters.status && filters.status[0] !== 'all') {
+ statusFilter = { status: activeFilters.status }
+ }
+
+ return {
+ ...activeFilters,
+ category: [restrictedCategory],
+ ...statusFilter,
+ }
}
- // Determine the correct status filter to append
- let statusFilter: Record = {}
+ if (isApproverView) {
+ // Payment Approver: restrict to allowed categories, default status ON_HOLD_ADMIN
+ let statusFilter: Record = {}
+ if (filters.status && filters.status[0] !== 'all') {
+ statusFilter = { status: activeFilters.status }
+ } else if (!filters.status && restrictedDefaultStatus) {
+ statusFilter = { status: [restrictedDefaultStatus] }
+ }
- if (filters.status && filters.status[0] !== 'all') {
- // 1. User explicitly chose a specific status (e.g. 'OWED')
- statusFilter = { status: activeFilters.status }
- } else if (!filters.status && restrictedDefaultStatus) {
- // 2. Initial load (filters.status is undefined), apply restricted default
- statusFilter = { status: [restrictedDefaultStatus] }
- }
- // 3. If user explicitly selected 'all' (filters.status[0] === 'all'),
- // statusFilter remains empty, allowing the API to return all statuses.
+ let categoryFilter: Record = {}
+ if (
+ activeFilters.category
+ && approverAllowedCategories.includes(activeFilters.category[0])
+ ) {
+ categoryFilter = { category: activeFilters.category }
+ } else if (!filters.category || filters.category[0] === 'all') {
+ categoryFilter = { categories: ([] as string[]).concat(approverAllowedCategories) }
+ }
- return {
- ...activeFilters,
- category: [restrictedCategory],
- ...statusFilter,
+ const rest = { ...activeFilters }
+ delete rest.category
+
+ return {
+ ...rest,
+ ...categoryFilter,
+ ...statusFilter,
+ }
}
- }, [filters, restrictedCategory, restrictedDefaultStatus])
+
+ return activeFilters
+ }, [filters, restrictedCategory, restrictedDefaultStatus, isApproverView])
const hasActiveFilters = React.useMemo(
() => Object.entries(appliedFilters)
- .some(([key, value]) => key !== 'category' && value.length > 0),
+ .some(([key, value]) => key !== 'category' && key !== 'categories' && value.length > 0),
[appliedFilters],
)
const selectedValueOverrides = React.useMemo>(() => {
- if (!restrictedCategory) {
- return {} as Record
+ if (restrictedCategory) {
+ const statusOverride = filters.status?.[0] !== 'all' ? filters.status?.[0] : undefined
+
+ return {
+ category: restrictedCategory,
+ ...(statusOverride ? { status: statusOverride } : {}),
+ }
}
- // Reflect the user's explicit status choice in the dropdown display.
- // Do not inject restrictedDefaultStatus here — it applies to the API query
- // via appliedFilters but must not override the dropdown's "All" default.
- const statusOverride = filters.status?.[0] !== 'all' ? filters.status?.[0] : undefined
+ if (isApproverView) {
+ const statusOverride = filters.status?.[0] !== 'all' ? filters.status?.[0] : undefined
- return {
- category: restrictedCategory,
- ...(statusOverride ? { status: statusOverride } : {}),
+ return {
+ ...(statusOverride ? { status: statusOverride } : {}),
+ }
}
- }, [filters.status, restrictedCategory])
+
+ return {} as Record
+ }, [filters.status, restrictedCategory, isApproverView])
const defaultDropdownValues = React.useMemo>(() => {
const defaults: Record = {}
@@ -561,14 +586,14 @@ const PaymentsListView: FC = (props: PaymentsListViewProp
>
Admin View
- {isEngagementPaymentApprover && (
+ {isPaymentApprover && (
)}
@@ -638,7 +663,27 @@ const PaymentsListView: FC = (props: PaymentsListViewProp
],
type: 'dropdown',
},
- ...(isRestrictedApproverView || (isWiproTaasAdmin && !hasPaymentAdminRole) ? [] : [
+ ...(isWiproTaasAdmin && !hasPaymentAdminRole ? [] : isApproverView ? [
+ {
+ key: 'category',
+ label: 'Payment Type',
+ options: [
+ {
+ label: 'All',
+ value: 'all',
+ },
+ {
+ label: 'Task Payments',
+ value: taskPaymentCategory,
+ },
+ {
+ label: 'Engagement Payments',
+ value: engagementPaymentCategory,
+ },
+ ],
+ type: 'dropdown',
+ },
+ ] as Filter[] : [
{
key: 'category',
label: 'Type',
diff --git a/src/apps/wallet-admin/src/lib/services/wallet.ts b/src/apps/wallet-admin/src/lib/services/wallet.ts
index 7d8ff6653..78445ebae 100644
--- a/src/apps/wallet-admin/src/lib/services/wallet.ts
+++ b/src/apps/wallet-admin/src/lib/services/wallet.ts
@@ -357,10 +357,12 @@ export async function exportSearchResults(
): Promise {
const url = `${baseUrl}/admin/winnings/export`
- const filteredFilters: Record = {}
+ const filteredFilters: Record = {}
for (const key in filters) {
- if (filters[key].length > 0 && key !== 'pageSize') {
+ if (['categories'].includes(key)) {
+ filteredFilters[key] = filters[key]
+ } else if (filters[key].length > 0 && key !== 'pageSize') {
filteredFilters[key] = filters[key][0]
}
}
@@ -397,10 +399,12 @@ export async function fetchWinnings(
winnings: WinningDetail[],
pagination: PaginationInfo
}> {
- const filteredFilters: Record = {}
+ const filteredFilters: Record = {}
for (const key in filters) {
- if (filters[key].length > 0 && key !== 'pageSize') {
+ if (['categories'].includes(key)) {
+ filteredFilters[key] = filters[key]
+ } else if (filters[key].length > 0 && key !== 'pageSize') {
filteredFilters[key] = filters[key][0]
}
}