Skip to content

feat(wallet-admin): improved management of payments #973

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ workflows:
branches:
only:
- dev
- feat/wallet-admin
- LVT-256

- deployQa:
context: org-global
Expand Down
6 changes: 6 additions & 0 deletions src/apps/wallet-admin/src/home/tabs/WalletAdminTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { PageTitle, TabsNavbar, TabsNavItem } from '~/libs/ui'
import { getHashFromTabId, getTabIdFromHash, WalletAdminTabsConfig, WalletAdminTabViews } from './config'
import { PaymentsTab } from './payments'
import { HomeTab } from './home'
import { TaxFormsTab } from './tax-forms'
import { PaymentMethodsTab } from './payment-methods'
import styles from './WalletAdminTabs.module.scss'

interface WalletHomeProps {
Expand Down Expand Up @@ -44,6 +46,10 @@ const WalletAdminTabs: FC<WalletHomeProps> = (props: WalletHomeProps) => {
{activeTab === WalletAdminTabViews.home && <HomeTab profile={props.profile} />}

{activeTab === WalletAdminTabViews.payments && <PaymentsTab profile={props.profile} />}

{activeTab === WalletAdminTabViews.taxforms && <TaxFormsTab profile={props.profile} />}

{activeTab === WalletAdminTabViews.withdrawalmethods && <PaymentMethodsTab profile={props.profile} />}
</div>
)
}
Expand Down
38 changes: 19 additions & 19 deletions src/apps/wallet-admin/src/home/tabs/config/wallet-tabs-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { TabsNavItem } from '~/libs/ui'
export enum WalletAdminTabViews {
home = '0',
payments = '1',
// taxforms = '2',
// withdrawalmethods = '3',
taxforms = '2',
withdrawalmethods = '3',
}

export const WalletAdminTabsConfig: TabsNavItem[] = [
Expand All @@ -16,14 +16,14 @@ export const WalletAdminTabsConfig: TabsNavItem[] = [
id: WalletAdminTabViews.payments,
title: 'Payments',
},
// {
// id: WalletAdminTabViews.withdrawalmethods,
// title: 'Withdrawal Methods',
// },
// {
// id: WalletAdminTabViews.taxforms,
// title: 'Tax Forms',
// },
{
id: WalletAdminTabViews.withdrawalmethods,
title: 'Payment Providers',
},
{
id: WalletAdminTabViews.taxforms,
title: 'Tax Forms',
},
]

export function getHashFromTabId(tabId: string): string {
Expand All @@ -32,23 +32,23 @@ export function getHashFromTabId(tabId: string): string {
return '#home'
case WalletAdminTabViews.payments:
return '#payments'
// case WalletAdminTabViews.taxforms:
// return '#tax-forms'
// case WalletAdminTabViews.withdrawalmethods:
// return '#withdrawal-methods'
case WalletAdminTabViews.taxforms:
return '#tax-forms'
case WalletAdminTabViews.withdrawalmethods:
return '#payment-providers'
default:
return '#home'
}
}

export function getTabIdFromHash(hash: string): string {
switch (hash) {
case '#winnings':
case '#payments':
return WalletAdminTabViews.payments
// case '#tax-forms':
// return WalletAdminTabViews.taxforms
// case '#withdrawal-methods':
// return WalletAdminTabViews.withdrawalmethods
case '#tax-forms':
return WalletAdminTabViews.taxforms
case '#payment-providers':
return WalletAdminTabViews.withdrawalmethods
default:
return WalletAdminTabViews.home
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@import '@libs/ui/styles/includes';

.container {
background-color: $black-5;
padding: $sp-6;
margin: $sp-8 0;
border-radius: 6px;

@include ltelg {
padding: $sp-4;
}

.header {
display: flex;
justify-content: flex-start;
gap: 5px;
align-items: center;

@include ltelg {
flex-direction: column;
}
}

.content {
background-color: $tc-white;
border-radius: 4px;
margin-top: $sp-4;
.centered {
height: 200px;
display: flex;
justify-content: space-around;
align-items: center;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
/* eslint-disable max-len */
/* eslint-disable react/jsx-no-bind */
import { toast } from 'react-toastify'
import React, { FC, useCallback, useEffect } from 'react'

import { Collapsible, ConfirmModal, LoadingCircles } from '~/libs/ui'
import { UserProfile } from '~/libs/core'

import { PaymentProvider } from '../../../lib/models/PaymentProvider'
import { deletePaymentProvider, getMemberHandle, getPaymentMethods } from '../../../lib/services/wallet'
import { FilterBar, PaymentMethodTable } from '../../../lib'
import { PaginationInfo } from '../../../lib/models/PaginationInfo'

import styles from './PaymentMethodsTab.module.scss'

interface ListViewProps {
// eslint-disable-next-line react/no-unused-prop-types
profile: UserProfile
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const ListView: FC<ListViewProps> = (props: ListViewProps) => {
const [confirmFlow, setConfirmFlow] = React.useState<{
provider: PaymentProvider
} | undefined>(undefined)
const [isLoading, setIsLoading] = React.useState<boolean>(false)
const [filters, setFilters] = React.useState<Record<string, string[]>>({})
const [paymentMethods, setPaymentMethods] = React.useState<PaymentProvider[]>([])
const [userIds, setUserIds] = React.useState<string[]>([])
const [pagination, setPagination] = React.useState<PaginationInfo>({
currentPage: 1,
pageSize: 10,
totalItems: 0,
totalPages: 0,
})

const fetchPaymentProviders = useCallback(async () => {
if (isLoading) {
return
}

setIsLoading(true)
try {

const paymentMethodsResponse = await getPaymentMethods(pagination.pageSize, (pagination.currentPage - 1) * pagination.pageSize, userIds)
const tmpUserIds = paymentMethodsResponse.paymentMethods.map(provider => provider.userId)
const handleMap = await getMemberHandle(tmpUserIds)

const userPaymentMethods = paymentMethodsResponse.paymentMethods.map((provider: PaymentProvider) => ({ ...provider, handle: handleMap.get(parseInt(provider.userId, 10)) ?? provider.userId }))

setPaymentMethods(userPaymentMethods)
setPagination(paymentMethodsResponse.pagination)
} catch (apiError) {
console.error('Failed to fetch winnings:', apiError)
} finally {
setIsLoading(false)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pagination.pageSize, pagination.currentPage, userIds])

useEffect(() => {
fetchPaymentProviders()
}, [fetchPaymentProviders])

return (
<>
<div className={styles.container}>
<div className={styles.header}>
<h3>Member Payment Providers</h3>
</div>
<div className={styles.content}>
<Collapsible header={<h3>Member Payment Providers Listing</h3>}>
<FilterBar
filters={[
{
key: 'userIds',
label: 'Username/Handle',
type: 'member_autocomplete',
},
{
key: 'pageSize',
label: 'Members per page',
options: [
{
label: '10',
value: '10',
},
{
label: '50',
value: '50',
},
{
label: '100',
value: '100',
},
],
type: 'dropdown',
},
]}
onFilterChange={(key: string, value: string[]) => {
const newPagination = {
...pagination,
currentPage: 1,
}
if (key === 'pageSize') {
newPagination.pageSize = parseInt(value[0], 10)
}

if (key === 'userIds') {
setUserIds(value)
}

setPagination(newPagination)
setFilters({
...filters,
[key]: value,
})
}}
onResetFilters={() => {
setPagination({
...pagination,
currentPage: 1,
pageSize: 10,
})
setFilters({})
}}
/>
{isLoading && <LoadingCircles className={styles.centered} />}
{!isLoading && paymentMethods.length > 0 && (
<PaymentMethodTable
paymentMethods={paymentMethods}
numPages={pagination.totalPages}
currentPage={pagination.currentPage}
onPreviousPageClick={() => {
setPagination({
...pagination,
currentPage: pagination.currentPage - 1,
})
}}
onNextPageClick={() => {
setPagination({
...pagination,
currentPage: pagination.currentPage + 1,
})
}}
onPageClick={(pageNumber: number) => {
setPagination({
...pagination,
currentPage: pageNumber,
})
}}
onDeleteClick={async (provider: PaymentProvider) => {
setConfirmFlow({ provider })
}}
/>
)}
{!isLoading && paymentMethods.length === 0 && (
<div className={styles.centered}>
<p className='body-main'>
{Object.keys(filters).length === 0
? 'Member payment-providers will appear here.'
: 'No payment-provider found for the selected member(s).'}
</p>
</div>
)}
</Collapsible>
</div>
</div>
{confirmFlow && (
<ConfirmModal
title='Delete Confirmation'
action='delete'
onClose={() => {
setConfirmFlow(undefined)
}}
onConfirm={async () => {
const userId = confirmFlow.provider.userId
const providerId = confirmFlow.provider.id!
setConfirmFlow(undefined)

toast.success('Deleting payment provider. Please wait...', { position: 'bottom-right' })
try {
await deletePaymentProvider(userId, providerId)
toast.success('Successfully deleted payment provider.', { position: 'bottom-right' })
} catch (err) {
toast.error('Failed to delete users payment provider. Please try again later', { position: 'bottom-right' })
}

fetchPaymentProviders()
}}
open={confirmFlow !== undefined}
>
<div>
<p>
Are you sure you want to reset the payment provider of the member
{' '}
{confirmFlow.provider.handle}
?
</p>
<br />
<p>This action cannot be undone.</p>
</div>
</ConfirmModal>
)}
</>
)
}

export default ListView
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as PaymentMethodsTab } from './PaymentMethodsTab'
Loading