Skip to content
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

Recurring donation #708

Merged
merged 65 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
020ab20
Created Recurring Donation Page
Marina-yoya Apr 7, 2022
6dee97c
Added Recurring Donation Page
Marina-yoya Apr 7, 2022
9dc347d
added DetailsModal
Marina-yoya Apr 8, 2022
35f9deb
create page added
Marina-yoya Apr 11, 2022
5081edc
added EditPage
Marina-yoya Apr 12, 2022
100c0c0
apiEndpoinds
Marina-yoya Apr 12, 2022
89b9b1f
Marina-yoya Apr 12, 2022
52e9494
added useTranslation
Marina-yoya Apr 14, 2022
cffa49c
change createForm usetranslation
Marina-yoya Apr 14, 2022
1444b37
fixing EditPage
Marina-yoya Apr 15, 2022
2127801
Merge branch 'master' into recurring-donation
dplamenov Apr 21, 2022
aa1f449
Sum the donations instead of displaying the first donation amount
kachar Apr 21, 2022
14f0a05
Improve import paths
kachar Apr 21, 2022
a11459e
Added icons before technology subtitles
Apr 21, 2022
5186b29
Adedd checkmark icons before the list items
Apr 21, 2022
e5ff8fe
Merge pull request #723 from podkrepi-bg/fix-technologies-section
kachar Apr 21, 2022
4c08320
Fixed spacings between category wrappers
Apr 21, 2022
667902f
Merge pull request #724 from podkrepi-bg/fix-technologies-section
kachar Apr 21, 2022
f7656dd
Add form select field common component
dimitur2204 Apr 23, 2022
2af1801
Merge pull request #726 from podkrepi-bg/feature/formik-form-select-f…
kachar Apr 24, 2022
1a7d429
Add person autocomplete component and start base dialog
dimitur2204 Apr 24, 2022
7c35c4f
Show person and cleanup
dimitur2204 Apr 24, 2022
b4de6a1
Dialog full width and showId props on autocomplete
dimitur2204 Apr 25, 2022
66fb3fd
Add person info component
dimitur2204 Apr 25, 2022
abc576f
Styled person info
dimitur2204 Apr 25, 2022
347cbf0
Add select person styled box
dimitur2204 Apr 26, 2022
aacb24a
Add validation
dimitur2204 Apr 26, 2022
fe3efae
Extract FormFieldButton as a component
dimitur2204 Apr 26, 2022
943204c
Add translations for person info component
dimitur2204 Apr 26, 2022
95fedb9
Resolve comments and fix minor issues
dimitur2204 Apr 26, 2022
fbd4968
Merge pull request #728 from podkrepi-bg/feature/725-admin-recurring-…
dimitur2204 Apr 26, 2022
88c6f77
Fix modal store problem
dimitur2204 Apr 26, 2022
40a2fb5
Make only one modal store instance
dimitur2204 Apr 26, 2022
3d84901
Add modal store to GridActions
dimitur2204 Apr 26, 2022
87cb277
Clean up folder structure
dimitur2204 Apr 26, 2022
cd68311
Update imports for RecurringDonationStatusSelect
dimitur2204 Apr 26, 2022
5cece46
Created Recurring Donation Page
Marina-yoya Apr 7, 2022
226739e
Added Recurring Donation Page
Marina-yoya Apr 7, 2022
2b9a809
added DetailsModal
Marina-yoya Apr 8, 2022
0080dde
create page added
Marina-yoya Apr 11, 2022
bf883bf
added EditPage
Marina-yoya Apr 12, 2022
7d6a44b
apiEndpoinds
Marina-yoya Apr 12, 2022
bdc1495
Marina-yoya Apr 12, 2022
4eca962
added useTranslation
Marina-yoya Apr 14, 2022
6b010f2
change createForm usetranslation
Marina-yoya Apr 14, 2022
e88e54f
fixing EditPage
Marina-yoya Apr 15, 2022
17e2421
Add person autocomplete component and start base dialog
dimitur2204 Apr 24, 2022
540e886
Show person and cleanup
dimitur2204 Apr 24, 2022
9304343
Dialog full width and showId props on autocomplete
dimitur2204 Apr 25, 2022
7bdd618
Add person info component
dimitur2204 Apr 25, 2022
3118a93
Styled person info
dimitur2204 Apr 25, 2022
4d327a6
Add select person styled box
dimitur2204 Apr 26, 2022
f709f93
Add validation
dimitur2204 Apr 26, 2022
ee491ff
Extract FormFieldButton as a component
dimitur2204 Apr 26, 2022
0b31bba
Add translations for person info component
dimitur2204 Apr 26, 2022
2a91242
Resolve comments and fix minor issues
dimitur2204 Apr 26, 2022
e2abb07
Fix modal store problem
dimitur2204 Apr 26, 2022
fea3907
Make only one modal store instance
dimitur2204 Apr 26, 2022
0da0a2b
Add modal store to GridActions
dimitur2204 Apr 26, 2022
bee7eba
Clean up folder structure
dimitur2204 Apr 26, 2022
8ae5516
Update imports for RecurringDonationStatusSelect
dimitur2204 Apr 26, 2022
43ad521
Fix apiEndpoints after rebase
dimitur2204 Apr 26, 2022
dea2917
Merge branch 'recurring-donation' of github.com:podkrepi-bg/frontend …
dimitur2204 Apr 26, 2022
82c673c
Fix recurringDonation api endpoints
dimitur2204 Apr 26, 2022
cf42395
Fix formik submitting
dimitur2204 Apr 26, 2022
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
31 changes: 31 additions & 0 deletions public/locales/bg/recurring-donation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"form-heading": "Добави",
"edit-form-heading": "Редактирай",
"recurring-donations": "Всички повтарящи се дарения",
"recurring-donation" : "Повтарящи се дарения",
"extSubscriptionId": "Абонамент",
"extCustomerId": "ID на клиент",
"currency": "Валута",
"amount": "Налични средства",
"status": "Статус",
"personId": "ID на потребител",
"vaultId": "ID на трезор",
"deleteTitle": "Сигурни ли сте?",
"deleteContent": "Това действие ще изтрие елемента завинаги!",
"actions": "Действия",
"alerts": {
"create": "Записът беше създаден успешно!",
"edit": "Записът беше редактиран успешно!",
"delete": "Записът беше изтрит успешно!",
"error": "Възникна грешка! Моля опитайте отново по-късно."
},
"cta": {
"add": "Добави",
"confirm": "Потвърди",
"cancel": "Отказ",
"delete": "Изтрий",
"edit": "Редактирай",
"details": "Детайли",
"submit": "Изпрати"
}
}
34 changes: 34 additions & 0 deletions src/common/hooks/recurringDonation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { KeycloakInstance } from 'keycloak-js'
import { useKeycloak } from '@react-keycloak/ssr'
import { QueryClient, useQuery } from 'react-query'

import { endpoints } from 'service/apiEndpoints'
import { authQueryFnFactory } from 'service/restRequests'
import { RecurringDonationResponse } from 'gql/recurring-donation'

export function useRecurringDonationList() {
const { keycloak } = useKeycloak<KeycloakInstance>()
return useQuery<RecurringDonationResponse[]>(
endpoints.recurringDonation.recurringDonation.url,
authQueryFnFactory<RecurringDonationResponse[]>(keycloak?.token),
)
}

export function useRecurringDonation(id: string) {
const { keycloak } = useKeycloak<KeycloakInstance>()
return useQuery<RecurringDonationResponse>(
endpoints.recurringDonation.getRecurringDonation(id).url,
authQueryFnFactory<RecurringDonationResponse>(keycloak?.token),
)
}

export async function prefetchRecurringDonationById(
client: QueryClient,
id: string,
token?: string,
) {
await client.prefetchQuery<RecurringDonationResponse>(
endpoints.recurringDonation.getRecurringDonation(id).url,
authQueryFnFactory<RecurringDonationResponse>(token),
)
}
5 changes: 5 additions & 0 deletions src/common/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ export const routes = {
create: '/admin/transfers/create',
view: (id: string) => `/admin/transfers/${id}`,
},
recurringDonation: {
index: '/admin/recurring-donation',
create: '/admin/recurring-donation/create',
view: (id: string) => `/admin/recurring-donation/${id}`,
},
},
dev: {
openData: '/open-data',
Expand Down
21 changes: 21 additions & 0 deletions src/components/recurring-donation/CreatePage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useTranslation } from 'next-i18next'
import { Container } from '@mui/material'

import AdminContainer from 'components/admin/navigation/AdminContainer'
import AdminLayout from 'components/admin/navigation/AdminLayout'

import Form from './Form'

export default function CreatePage() {
const { t } = useTranslation('recurring-donation')

return (
<AdminLayout>
<AdminContainer title={t('recurring-donation')}>
<Container maxWidth="md" sx={{ py: 5 }}>
<Form />
</Container>
</AdminContainer>
</AdminLayout>
)
}
21 changes: 21 additions & 0 deletions src/components/recurring-donation/EditPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useTranslation } from 'next-i18next'
import { Container } from '@mui/material'

import AdminContainer from 'components/admin/navigation/AdminContainer'
import AdminLayout from 'components/admin/navigation/AdminLayout'

import Form from './Form'

export default function EditPage() {
const { t } = useTranslation('recurring-donation')

return (
<AdminLayout>
<AdminContainer title={t('recurring-donation')}>
<Container maxWidth="md" sx={{ py: 5 }}>
<Form />
</Container>
</AdminContainer>
</AdminLayout>
)
}
161 changes: 161 additions & 0 deletions src/components/recurring-donation/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import React from 'react'
import { useMutation, useQueryClient, UseQueryResult } from 'react-query'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import Link from 'next/link'
import { AxiosError, AxiosResponse } from 'axios'
import * as yup from 'yup'
import { Box, Button, Grid, Typography } from '@mui/material'

import { RecurringDonationInput, RecurringDonationResponse } from 'gql/recurring-donation'

import { Currency } from 'gql/currency'
import { useRecurringDonation } from 'common/hooks/recurringDonation'
import { routes } from 'common/routes'
import { ApiErrors } from 'service/apiErrors'
import { useCreateRecurringDonation, useEditRecurringDonation } from 'service/recurringDonation'
import { endpoints } from 'service/apiEndpoints'
import { AlertStore } from 'stores/AlertStore'
import GenericForm from 'components/common/form/GenericForm'
import FormTextField from 'components/common/form/FormTextField'
import SubmitButton from 'components/common/form/SubmitButton'
import CurrencySelect from 'components/currency/CurrencySelect'
import RecurringDonationStatusSelect from './grid/RecurringDonationStatusSelect'

export enum RecurringDonationStatus {
trialing = 'trialing',
active = 'active',
canceled = 'canceled',
incomplete = 'incomplete',
incompleteExpired = 'incompleteExpired',
pastDue = 'pastDue',
unpaid = 'unpaid',
}

const validCurrencies = Object.keys(Currency)
const validStatuses = Object.keys(RecurringDonationStatus)

const validationSchema = yup
.object()
.defined()
.shape({
status: yup.string().oneOf(validStatuses).required(),
personId: yup.string().trim().max(50).required(),
extSubscriptionId: yup.string().trim().max(50).required(),
extCustomerId: yup.string().trim().max(50).required(),
amount: yup.number().positive().integer().required(),
currency: yup.string().oneOf(validCurrencies).required(),
sourceVault: yup.string().trim().uuid().required(),
})

export default function EditForm() {
const router = useRouter()
const queryClient = useQueryClient()
const { t } = useTranslation()

let id = router.query.id

let initialValues: RecurringDonationInput = {
status: '',
personId: '',
extSubscriptionId: '',
extCustomerId: '',
amount: 0,
currency: '',
sourceVault: '',
}

if (id) {
id = String(id)
const { data }: UseQueryResult<RecurringDonationResponse> = useRecurringDonation(id)

initialValues = {
status: data?.status,
personId: data?.personId,
extSubscriptionId: data?.extSubscriptionId,
extCustomerId: data?.extCustomerId,
amount: data?.amount,
currency: data?.currency,
sourceVault: data?.sourceVault,
}
}
Comment on lines +68 to +81
Copy link
Member

@kachar kachar Apr 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These values should be pass from the page

The idea is:

  1. In page file load the initial record fetched by ID
  2. Load the form with the initial records predefined coming down as prop
  3. Work on the data

At the moment CreatePage and EditPage look the same, but they should not.

See as example

https://github.com/podkrepi-bg/frontend/blob/621cb6bb8d1ab4f658effb08ee9e300b8dfc35e8/src/components/campaigns/grid/EditPage.tsx


const mutationFn = id ? useEditRecurringDonation(id) : useCreateRecurringDonation()

const mutation = useMutation<
AxiosResponse<RecurringDonationResponse>,
AxiosError<ApiErrors>,
RecurringDonationInput
>({
mutationFn,
onError: () => AlertStore.show(t('recurring-donation:alerts:error'), 'error'),
onSuccess: () => {
if (id)
queryClient.invalidateQueries(
endpoints.recurringDonation.getRecurringDonation(String(id)).url,
)
AlertStore.show(
id ? t('recurring-donation:alerts:edit') : t('recurring-donation:alerts:create'),
'success',
)
router.push(routes.admin.recurringDonation.index)
},
})
async function onSubmit(data: RecurringDonationInput) {
mutation.mutate(data)
}

return (
<GenericForm
onSubmit={onSubmit}
initialValues={initialValues}
validationSchema={validationSchema}>
<Box sx={{ marginTop: '5%', height: '62.6vh' }}>
<Typography variant="h5" component="h2" sx={{ marginBottom: 2, textAlign: 'center' }}>
{id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')}
</Typography>
<Grid container spacing={2} sx={{ width: 600, margin: '0 auto' }}>
<Grid item xs={6}>
<RecurringDonationStatusSelect />
</Grid>
<Grid item xs={6}>
<FormTextField type="text" label={t('recurring-donation:personId')} name="personId" />
</Grid>
<Grid item xs={6}>
<FormTextField
type="text"
label={t('recurring-donation:extSubscriptionId')}
name="extSubscriptionId"
/>
</Grid>
<Grid item xs={6}>
<FormTextField
type="text"
label={t('recurring-donation:extCustomerId')}
name="extCustomerId"
/>
</Grid>
<Grid item xs={6}>
<FormTextField type="number" label={t('recurring-donation:amount')} name="amount" />
</Grid>
<Grid item xs={6}>
<CurrencySelect />
</Grid>
<Grid item xs={6}>
<FormTextField type="text" label={t('recurring-donation:vaultId')} name="sourceVault" />
</Grid>

{id ? <></> : <></>}
<Grid item xs={6}>
<SubmitButton fullWidth label={t('recurring-donation:cta:submit')} />
</Grid>
<Grid item xs={6}>
<Link href={routes.admin.recurringDonation.index} passHref>
<Button>{t('recurring-donation:cta:cancel')}</Button>
</Link>
</Grid>
</Grid>
</Box>
</GenericForm>
)
}
19 changes: 19 additions & 0 deletions src/components/recurring-donation/RecurringDonationPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useTranslation } from 'next-i18next'

import AdminContainer from 'components/admin/navigation/AdminContainer'
import AdminLayout from 'components/admin/navigation/AdminLayout'
import Grid from './grid/Grid'
import GridAppbar from './grid/GridAppbar'

export default function VaultsPage() {
const { t } = useTranslation('recurring-donation')

return (
<AdminLayout>
<AdminContainer title={t('recurring-donation')}>
<GridAppbar />
<Grid />
</AdminContainer>
</AdminLayout>
)
}
41 changes: 41 additions & 0 deletions src/components/recurring-donation/grid/DeleteModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useMutation } from 'react-query'
import { observer } from 'mobx-react'
import { AxiosError, AxiosResponse } from 'axios'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'

import { RecurringDonationResponse } from 'gql/recurring-donation'
import { ApiErrors } from 'service/apiErrors'
import { useDeleteRecurringDonation } from 'service/recurringDonation'
import { ModalStore } from 'stores/dashboard/ModalStore'
import { AlertStore } from 'stores/AlertStore'
import { routes } from 'common/routes'
import DeleteDialog from 'components/admin/DeleteDialog'

export default observer(function DeleteModal() {
const router = useRouter()
const { hideDelete, selectedRecord } = ModalStore
const { t } = useTranslation('recurring-donation')

const mutationFn = useDeleteRecurringDonation(selectedRecord.id)

const deleteMutation = useMutation<
AxiosResponse<RecurringDonationResponse>,
AxiosError<ApiErrors>,
string
>({
mutationFn,
onError: () => AlertStore.show(t('alerts.error'), 'error'),
onSuccess: () => {
hideDelete()
AlertStore.show(t('alerts.delete'), 'success')
router.push(routes.admin.recurringDonation.index)
},
})

function deleteHandler() {
deleteMutation.mutate(selectedRecord.id)
}

return <DeleteDialog deleteHandler={deleteHandler} />
})
30 changes: 30 additions & 0 deletions src/components/recurring-donation/grid/DetailsModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react'
import { UseQueryResult } from 'react-query'
import { observer } from 'mobx-react'
import { useTranslation } from 'next-i18next'

import { RecurringDonationResponse } from 'gql/recurring-donation'
import { useRecurringDonation } from 'common/hooks/recurringDonation'
import { ModalStore } from 'stores/dashboard/ModalStore'
import DetailsDialog from 'components/admin/DetailsDialog'

export default observer(function DetailsModal() {
const { selectedRecord } = ModalStore
const { data }: UseQueryResult<RecurringDonationResponse> = useRecurringDonation(
selectedRecord.id,
)
console.log(data)
const { t } = useTranslation('recurring-donation')

const dataConverted = [
{ name: 'ID', value: `${data?.id}` },
{ name: t('status'), value: `${data?.status}` },
{ name: t('currency'), value: `${data?.currency}` },
{ name: t('amount'), value: `${data?.amount}` },
{ name: t('extSubscriptionId'), value: `${data?.extSubscriptionId}` },
{ name: t('extCustomerId'), value: `${data?.extCustomerId}` },
{ name: t('vaultId'), value: `${data?.vaultId}` },
]

return <DetailsDialog data={dataConverted} />
})
Loading