Skip to content

Commit

Permalink
Add refund button
Browse files Browse the repository at this point in the history
  • Loading branch information
Nnachevvv committed Oct 28, 2023
1 parent d0382b6 commit 5b5edd2
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 2 deletions.
10 changes: 10 additions & 0 deletions public/locales/bg/donations.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"edit": "Дарението беше редактирано успешно!",
"delete": "Дарението беше изтрито успешно!",
"deleteAll": "Даренията бяха изтрити успешно!",
"refundSuccess": "Беше създадена успешна заявка за връщане на парите към Stripe",
"editDonor": "Дарителят беше редактиран успешно!",
"error": "Възникна грешка! Моля опитайте отново по-късно.",
"requiredError": "Полето е задължително."
Expand All @@ -60,5 +61,14 @@
"sortBy": "Сортиране",
"minAmount": "Мин сума",
"maxAmount": "Макс сума"
},
"refund": {
"icon": "Връщане на дарение",
"title": "Направи заявка за връщане на дарение",
"confirmation": "Сигурни ли сте, че искате да възстановите сумата от дарение:",
"number": "Номер:",
"amount": "Сума:",
"email": "Е-mail на дарител:",
"confirm-button": "Възстановяване парите от даренеие"
}
}
1 change: 1 addition & 0 deletions public/locales/bg/profile.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"initial": "започнато",
"waiting": "чакащо",
"succeeded": "успешно",
"refund": "възстановено",
"cancelled": "отменено"
}
},
Expand Down
2 changes: 2 additions & 0 deletions src/components/admin/donations/DonationsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import AdminLayout from 'components/common/navigation/AdminLayout'
import Grid from './grid/Grid'
import GridAppbar from './grid/GridAppbar'
import GridFilters from './grid/GridFilters'
import { RefundStoreImpl } from './store/RefundStore'

export const ModalStore = new ModalStoreImpl()
export const RefundStore = new RefundStoreImpl()

export default function DocumentsPage() {
const { t } = useTranslation()
Expand Down
38 changes: 36 additions & 2 deletions src/components/admin/donations/grid/Grid.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from 'react'
import { UseQueryResult } from '@tanstack/react-query'
import { useTranslation } from 'next-i18next'
import { Box, Tooltip } from '@mui/material'
import { Box, IconButton, Tooltip } from '@mui/material'
import { Edit } from '@mui/icons-material'
import {
DataGrid,
Expand All @@ -15,7 +15,7 @@ import { useDonationsList } from 'common/hooks/donation'

import DetailsModal from '../modals/DetailsModal'
import DeleteModal from '../modals/DeleteModal'
import { ModalStore } from '../DonationsPage'
import { ModalStore, RefundStore } from '../DonationsPage'
import { getExactDateTime } from 'common/util/date'
import { useRouter } from 'next/router'
import { money } from 'common/util/money'
Expand All @@ -25,6 +25,8 @@ import theme from 'common/theme'
import RenderEditPersonCell from './RenderEditPersonCell'
import { useStores } from '../../../../common/hooks/useStores'
import RenderEditBillingEmailCell from './RenderEditBillingEmailCell'
import RestoreIcon from '@mui/icons-material/Restore'
import RefundModal from '../modals/RefundModal'

interface RenderCellProps {
params: GridRenderCellParams
Expand All @@ -47,6 +49,7 @@ export default observer(function Grid() {
const { t } = useTranslation()
const router = useRouter()
const { isDetailsOpen } = ModalStore
const { isRefundOpen } = RefundStore
const campaignId = router.query.campaignId as string | undefined

const {
Expand Down Expand Up @@ -136,7 +139,37 @@ export default observer(function Grid() {
headerAlign: 'left',
}

const { showRefund, setSelectedRecord } = RefundStore

function refundClickClickHandler(id: string) {
setSelectedRecord({ id })
showRefund()
}

const columns: GridColDef[] = [
{
field: 'actions',
headerName: 'Actions',
type: 'actions',
width: 120,
resizable: false,
renderCell: (params: GridRenderCellParams) => {
return params.row?.status === 'succeeded' ? (
<>
<Tooltip title={t('donations:refund.icon')}>
<IconButton
size="small"
color="primary"
onClick={() => refundClickClickHandler(params.row.id)}>
<RestoreIcon />
</IconButton>
</Tooltip>
</>
) : (
''
)
},
},
{
field: 'createdAt',
headerName: t('donations:date'),
Expand Down Expand Up @@ -256,6 +289,7 @@ export default observer(function Grid() {

{/* making sure we don't sent requests to the API when not needed */}
{isDetailsOpen && <DetailsModal />}
{isRefundOpen && <RefundModal />}
<DeleteModal />
</>
)
Expand Down
89 changes: 89 additions & 0 deletions src/components/admin/donations/modals/RefundModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import React, { useState } from 'react'
import { useTranslation } from 'next-i18next'

import { Dialog, Typography, DialogTitle, DialogContent, Grid, CardContent } from '@mui/material'
import { useRefundStripeDonation } from 'service/donation'
import { AlertStore } from 'stores/AlertStore'
import { UseQueryResult, useMutation } from '@tanstack/react-query'
import SubmitButton from 'components/common/form/SubmitButton'
import { DonationResponse, StripeRefundRequest } from 'gql/donations'
import CloseModalButton from 'components/common/CloseModalButton'
import { useDonation } from 'common/hooks/donation'
import { observer } from 'mobx-react'
import { RefundStore } from '../DonationsPage'
import GenericForm from 'components/common/form/GenericForm'
import { fromMoney } from 'common/util/money'

export default observer(function RefundModal() {
const { t } = useTranslation('donations')
const { isRefundOpen, hideRefund, selectedRecord } = RefundStore
const { data }: UseQueryResult<DonationResponse> = useDonation(selectedRecord.id)

const initialValues: StripeRefundRequest = {
extPaymentIntentId: '',
}

if (data) {
initialValues.extPaymentIntentId = data.extPaymentIntentId
}

const refundMutation = useMutation({
mutationFn: useRefundStripeDonation(),
onError: () => AlertStore.show(t('alerts.error'), 'error'),
onSuccess: () => {
AlertStore.show(t('alerts.refundSuccess'), 'success')
hideRefund()
},
})
const [loading, setLoading] = useState(false)

async function onSubmit(values: StripeRefundRequest) {
setLoading(true)
try {
await refundMutation.mutateAsync(values.extPaymentIntentId)
} finally {
setLoading(false)
}
}

return (
<Dialog
open={isRefundOpen}
onClose={hideRefund}
sx={{ scroll: 'none' }}
fullWidth={true}
maxWidth={'sm'}>
<DialogContent
style={{
overflow: 'hidden',
padding: '2rem',
width: '100%',
}}>
<Grid style={{ display: 'flex', justifyContent: 'end', marginRight: '-4rem' }}>
<CloseModalButton href={''} onClose={hideRefund} />
</Grid>
<DialogTitle style={{ display: 'flex', justifyContent: 'center', width: '100%' }}>
{t('refund.title')}
</DialogTitle>
<Grid container direction="column" component="section">
<GenericForm onSubmit={onSubmit} initialValues={initialValues}>
<CardContent>
<Typography variant="body1">{t('refund.confirmation')}</Typography>
<Typography variant="body1">
{t('refund.number')} {data?.id}
</Typography>
<Typography variant="body1">
{t('refund.amount')} {fromMoney(data?.amount as number)} {data?.currency}
</Typography>
<Typography variant="body1"> {t('refund.email')} {data?.billingEmail}
</Typography>
<Grid item xs={12} marginTop={3}>
<SubmitButton fullWidth label={t('refund.confirm-button')} loading={loading} />
</Grid>
</CardContent>
</GenericForm>
</Grid>
</DialogContent>
</Dialog>
)
})
22 changes: 22 additions & 0 deletions src/gql/donations.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export type DonationResponse = {
updatedAt: DateTime
currency: Currency
amount: number
billingEmail?: string
personId?: UUID
person?: {
id: string
Expand Down Expand Up @@ -171,6 +172,27 @@ export type SecondStep = {
export type ThirdStep = {
message?: string
}

export type StripeRefundRespone = {
id: UUID
object: string
amount: number
balance_transaction: string
charge: string
created: number
currency: string
payment_intent: string
reason: string
receipt_number: string
source_transfer_reversal: string
status: DonationStatus
transfer_reversal: string
}

export type StripeRefundRequest = {
extPaymentIntentId: string
}

export type BankTransactionsFileFormData = {
bankTransactionsFileId: string
}
Expand Down
2 changes: 2 additions & 0 deletions src/service/apiEndpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export const endpoints = {
createCheckoutSession: <Endpoint>{ url: '/donation/create-checkout-session', method: 'POST' },
createPaymentIntent: <Endpoint>{ url: '/donation/create-payment-intent', method: 'POST' },
createBankDonation: <Endpoint>{ url: '/donation/create-bank-payment', method: 'POST' },
refundStripePayment: (id: string) =>
<Endpoint>{ url: `/donation/refund-stripe-payment/${id}`, method: 'POST' },
getDonation: (id: string) => <Endpoint>{ url: `/donation/${id}`, method: 'GET' },
donationsList: (
campaignId?: string,
Expand Down
12 changes: 12 additions & 0 deletions src/service/donation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CheckoutSessionResponse,
DonationBankInput,
DonationResponse,
StripeRefundRespone,
UserDonationInput,
} from 'gql/donations'
import { apiClient } from 'service/apiClient'
Expand Down Expand Up @@ -67,6 +68,17 @@ export function useDeleteDonation(ids: string[]) {
}
}

export const useRefundStripeDonation = () => {
const { data: session } = useSession()
return async (extPaymentId: string) => {
return await apiClient.post<StripeRefundRespone, AxiosResponse<StripeRefundRespone>>(
endpoints.donation.refundStripePayment(extPaymentId).url,
'',
authConfig(session?.accessToken),
)
}
}

export const useUploadBankTransactionsFiles = () => {
const { data: session } = useSession()
return async ({
Expand Down

0 comments on commit 5b5edd2

Please sign in to comment.