From 020ab201ad11f8071ec914013a394e84fe709c77 Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Thu, 7 Apr 2022 15:51:50 +0100 Subject: [PATCH 01/59] Created Recurring Donation Page --- src/common/hooks/recurringDonation.ts | 15 +++ src/common/routes.ts | 5 + .../recurring-donation/CreatePage.tsx | 0 .../recurring-donation/EditPage.tsx | 0 src/components/recurring-donation/Form.tsx | 0 .../RecurringDonationPage.tsx | 19 +++ .../recurring-donation/grid/DeleteModal.tsx | 0 .../recurring-donation/grid/DetailsModal.tsx | 0 .../recurring-donation/grid/Grid.tsx | 108 ++++++++++++++++++ .../recurring-donation/grid/GridAppbar.tsx | 45 ++++++++ src/gql/recurring-donation-status.ts | 9 ++ src/gql/recurring-donation.d.ts | 26 +++++ src/pages/admin/recurring-donation/index.tsx | 29 +++++ src/service/apiEndpoints.ts | 11 ++ 14 files changed, 267 insertions(+) create mode 100644 src/common/hooks/recurringDonation.ts create mode 100644 src/components/recurring-donation/CreatePage.tsx create mode 100644 src/components/recurring-donation/EditPage.tsx create mode 100644 src/components/recurring-donation/Form.tsx create mode 100644 src/components/recurring-donation/RecurringDonationPage.tsx create mode 100644 src/components/recurring-donation/grid/DeleteModal.tsx create mode 100644 src/components/recurring-donation/grid/DetailsModal.tsx create mode 100644 src/components/recurring-donation/grid/Grid.tsx create mode 100644 src/components/recurring-donation/grid/GridAppbar.tsx create mode 100644 src/gql/recurring-donation-status.ts create mode 100644 src/gql/recurring-donation.d.ts create mode 100644 src/pages/admin/recurring-donation/index.tsx diff --git a/src/common/hooks/recurringDonation.ts b/src/common/hooks/recurringDonation.ts new file mode 100644 index 000000000..16bc6b352 --- /dev/null +++ b/src/common/hooks/recurringDonation.ts @@ -0,0 +1,15 @@ +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() + return useQuery( + endpoints.recurringDonation.recurringDonation.url, + authQueryFnFactory(keycloak?.token), + ) +} diff --git a/src/common/routes.ts b/src/common/routes.ts index eeba1eaf3..740aef9da 100644 --- a/src/common/routes.ts +++ b/src/common/routes.ts @@ -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', diff --git a/src/components/recurring-donation/CreatePage.tsx b/src/components/recurring-donation/CreatePage.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/EditPage.tsx b/src/components/recurring-donation/EditPage.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/RecurringDonationPage.tsx b/src/components/recurring-donation/RecurringDonationPage.tsx new file mode 100644 index 000000000..5635877c2 --- /dev/null +++ b/src/components/recurring-donation/RecurringDonationPage.tsx @@ -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 ( + + + + + + + ) +} diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/grid/DeleteModal.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/grid/DetailsModal.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx new file mode 100644 index 000000000..f45301401 --- /dev/null +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -0,0 +1,108 @@ +import React, { useState } from 'react' +import { UseQueryResult } from 'react-query' +import { useTranslation } from 'next-i18next' +import { Box } from '@mui/material' +import { DataGrid, GridColDef, GridColumns, GridRenderCellParams } from '@mui/x-data-grid' + +import { routes } from 'common/routes' +import { RecurringDonationResponse } from 'gql/recurring-donation' +import { useRecurringDonationList } from 'common/hooks/recurringDonation' +import GridActions from 'components/admin/GridActions' + +// import DeleteModal from './DeleteModal' +// import DetailsModal from './DetailsModal' + +export default function Grid() { + const { t } = useTranslation('recurring-donation') + const { data }: UseQueryResult = useRecurringDonationList() + const [pageSize, setPageSize] = useState(5) + + const commonProps: Partial = { + align: 'left', + width: 150, + headerAlign: 'left', + } + + const columns: GridColumns = [ + { + field: 'status', + headerName: t('status'), + flex: 1.5, + ...commonProps, + }, + { + field: 'currency', + headerName: t('currency'), + flex: 1.5, + ...commonProps, + }, + { + field: 'amount', + headerName: t('amount'), + flex: 1.5, + ...commonProps, + }, + { + field: 'extSubscriptionId', + headerName: t('extSubscriptionId'), + ...commonProps, + width: 400, + }, + { + field: 'extCustomerId', + headerName: t('extCustomerId'), + ...commonProps, + width: 300, + }, + { + field: 'vaultId', + headerName: t('vaultId'), + ...commonProps, + width: 300, + }, + { + field: 'actions', + headerName: t('actions'), + width: 120, + type: 'actions', + headerAlign: 'center', + renderCell: (params: GridRenderCellParams): React.ReactNode => { + return ( + + ) + }, + }, + ] + + return ( + <> + + setPageSize(newPageSize)} + disableSelectionOnClick + /> + + {/* + */} + + ) +} diff --git a/src/components/recurring-donation/grid/GridAppbar.tsx b/src/components/recurring-donation/grid/GridAppbar.tsx new file mode 100644 index 000000000..bc5f856be --- /dev/null +++ b/src/components/recurring-donation/grid/GridAppbar.tsx @@ -0,0 +1,45 @@ +import { useRouter } from 'next/router' +import { useTranslation } from 'next-i18next' +import { Box, Toolbar, Tooltip, Typography } from '@mui/material' +import { Add as AddIcon } from '@mui/icons-material' + +import { routes } from 'common/routes' + +const addIconStyles = { + background: '#4ac3ff', + borderRadius: '50%', + cursor: 'pointer', + padding: 1.2, + boxShadow: 3, +} + +export default function GridAppbar() { + const router = useRouter() + const { t } = useTranslation() + + return ( + + + {t('recurring-donation:recurring-donation')} + + + + + router.push(routes.admin.recurringDonation.create)} + /> + + + + + ) +} diff --git a/src/gql/recurring-donation-status.ts b/src/gql/recurring-donation-status.ts new file mode 100644 index 000000000..8dac5c718 --- /dev/null +++ b/src/gql/recurring-donation-status.ts @@ -0,0 +1,9 @@ +export enum RecurringDonationStatus { + trialing = 'trialing', + active = 'active', + canceled = 'canceled', + incomplete = 'incomplete', + incompleteExpired = 'incompleteExpired', + pastDue = 'pastDue', + unpaid = 'unpaid', +} diff --git a/src/gql/recurring-donation.d.ts b/src/gql/recurring-donation.d.ts new file mode 100644 index 000000000..ca12d0d63 --- /dev/null +++ b/src/gql/recurring-donation.d.ts @@ -0,0 +1,26 @@ +import type { Currency } from './currency' +import { UUID } from './types' +import { RecurringDonationStatus } from './recurring-donation-status' + +export type RecurringDonationResponse = { + id: UUID + status: RecurringDonationStatus + personId: UUID + extSubscriptionId: UUID + extCustomerId: UUID + amount: number + currency: Currency + vaultId: string + createdAt: Date + updatedAt: Date | null +} + +export type RecurringDonationInput = { + status?: RecurringDonationStatus + personId?: UUID + extSubscriptionId?: UUID + extCustomerId?: UUID + amount?: number + currency?: Currency + vaultId?: string +} diff --git a/src/pages/admin/recurring-donation/index.tsx b/src/pages/admin/recurring-donation/index.tsx new file mode 100644 index 000000000..679b79c8e --- /dev/null +++ b/src/pages/admin/recurring-donation/index.tsx @@ -0,0 +1,29 @@ +import { GetServerSideProps } from 'next' +import { dehydrate, QueryClient } from 'react-query' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' + +import { keycloakInstance } from 'middleware/auth/keycloak' +// import { prefetchVaultsList } from 'common/hooks/vaults' +import RecurringDonationPage from 'components/recurring-donation/RecurringDonationPage' + +export const getServerSideProps: GetServerSideProps = async (params) => { + const client = new QueryClient() + const keycloak = keycloakInstance(params) + + // await prefetchVaultsList(client, keycloak?.token) + + return { + props: { + ...(await serverSideTranslations(params.locale ?? 'bg', [ + 'common', + 'auth', + 'recurring-donation', + 'admin', + 'validation', + ])), + dehydratedState: dehydrate(client), + }, + } +} + +export default RecurringDonationPage diff --git a/src/service/apiEndpoints.ts b/src/service/apiEndpoints.ts index 8dd2d142b..0decf3d62 100644 --- a/src/service/apiEndpoints.ts +++ b/src/service/apiEndpoints.ts @@ -139,4 +139,15 @@ export const endpoints = { editTransfer: (id: string) => { url: `/transfer/${id}`, method: 'PUT' }, removeTransfer: (id: string) => { url: `/transfer/${id}`, method: 'DELETE' }, }, + recurringDonation: { + recurringDonation: { url: '/recurring-donation', method: 'GET' }, + getRecurringDonation: (id: string) => + { url: `/recurring-donation/${id}`, method: 'GET' }, + createRecurringDonation: { url: '/recurring-donation', method: 'POST' }, + editRecurringDonation: (id: string) => + { url: `/recurring-donation/${id}`, method: 'PUT' }, + deleteRecurringDonation: (id: string) => + { url: `/recurring-donation/${id}`, method: 'DELETE' }, + deleteRecurringDonations: { url: '/recurring-donation/deletemany', method: 'POST' }, + }, } From 6dee97cf5f5eb986ac6e01421b9047992617e8b7 Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Thu, 7 Apr 2022 16:04:45 +0100 Subject: [PATCH 02/59] Added Recurring Donation Page --- src/pages/admin/recurring-donation/index.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/admin/recurring-donation/index.tsx b/src/pages/admin/recurring-donation/index.tsx index 679b79c8e..052e9d4f8 100644 --- a/src/pages/admin/recurring-donation/index.tsx +++ b/src/pages/admin/recurring-donation/index.tsx @@ -3,15 +3,12 @@ import { dehydrate, QueryClient } from 'react-query' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { keycloakInstance } from 'middleware/auth/keycloak' -// import { prefetchVaultsList } from 'common/hooks/vaults' import RecurringDonationPage from 'components/recurring-donation/RecurringDonationPage' export const getServerSideProps: GetServerSideProps = async (params) => { const client = new QueryClient() const keycloak = keycloakInstance(params) - // await prefetchVaultsList(client, keycloak?.token) - return { props: { ...(await serverSideTranslations(params.locale ?? 'bg', [ From 9dc347d9962632dcb08ef8f9000287c5336499cd Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Fri, 8 Apr 2022 09:35:41 +0100 Subject: [PATCH 03/59] added DetailsModal --- src/common/hooks/recurringDonation.ts | 8 +++++ .../recurring-donation/grid/DetailsModal.tsx | 29 +++++++++++++++++++ .../recurring-donation/grid/Grid.tsx | 6 ++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/common/hooks/recurringDonation.ts b/src/common/hooks/recurringDonation.ts index 16bc6b352..c11b2bab8 100644 --- a/src/common/hooks/recurringDonation.ts +++ b/src/common/hooks/recurringDonation.ts @@ -13,3 +13,11 @@ export function useRecurringDonationList() { authQueryFnFactory(keycloak?.token), ) } + +export function useRecurringDonation(id: string) { + const { keycloak } = useKeycloak() + return useQuery( + endpoints.recurringDonation.getRecurringDonation(id).url, + authQueryFnFactory(keycloak?.token), + ) +} diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/grid/DetailsModal.tsx index e69de29bb..8e45af79d 100644 --- a/src/components/recurring-donation/grid/DetailsModal.tsx +++ b/src/components/recurring-donation/grid/DetailsModal.tsx @@ -0,0 +1,29 @@ +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 = useRecurringDonation( + selectedRecord.id, + ) + 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 +}) diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index f45301401..8489505c0 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -10,7 +10,7 @@ import { useRecurringDonationList } from 'common/hooks/recurringDonation' import GridActions from 'components/admin/GridActions' // import DeleteModal from './DeleteModal' -// import DetailsModal from './DetailsModal' +import DetailsModal from './DetailsModal' export default function Grid() { const { t } = useTranslation('recurring-donation') @@ -101,8 +101,8 @@ export default function Grid() { disableSelectionOnClick /> - {/* - */} + + {/* */} ) } From 35f9debc74a5b909776688253e61ab907b46a4db Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Mon, 11 Apr 2022 13:39:54 +0100 Subject: [PATCH 04/59] create page added --- .../recurring-donation/CreatePage.tsx | 21 +++ src/components/recurring-donation/Form.tsx | 173 ++++++++++++++++++ .../grid/RecurringDonationStatusSelect.tsx | 47 +++++ ...atus.ts => recurring-donation-status.d.ts} | 0 src/gql/recurring-donation.d.ts | 9 +- src/pages/admin/recurring-donation/create.tsx | 23 +++ src/service/recurringDonation.ts | 28 +++ 7 files changed, 296 insertions(+), 5 deletions(-) create mode 100644 src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx rename src/gql/{recurring-donation-status.ts => recurring-donation-status.d.ts} (100%) create mode 100644 src/pages/admin/recurring-donation/create.tsx create mode 100644 src/service/recurringDonation.ts diff --git a/src/components/recurring-donation/CreatePage.tsx b/src/components/recurring-donation/CreatePage.tsx index e69de29bb..b3cadbb5c 100644 --- a/src/components/recurring-donation/CreatePage.tsx +++ b/src/components/recurring-donation/CreatePage.tsx @@ -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 ( + + + +
+ + + + ) +} diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index e69de29bb..e2ceb0c63 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -0,0 +1,173 @@ +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 = useRecurringDonation(id) + + initialValues = { + status: data?.status, + personId: data?.personId, + extSubscriptionId: data?.extSubscriptionId, + extCustomerId: data?.extCustomerId, + amount: data?.amount, + currency: data?.currency, + sourceVault: data?.sourceVault, + } + } + + const mutationFn = id ? useEditRecurringDonation(id) : useCreateRecurringDonation() + + const mutation = useMutation< + AxiosResponse, + AxiosError, + 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 ( + + + + {id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')} + + + + + + {id ? ( + <> + ) : ( + <> + + + + + + + + + + + + + + + + + + + + )} + + + + + + + + + + + + ) +} diff --git a/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx b/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx new file mode 100644 index 000000000..aafe3f99d --- /dev/null +++ b/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx @@ -0,0 +1,47 @@ +import { useTranslation } from 'react-i18next' +import { FormControl, MenuItem } from '@mui/material' +import { useField } from 'formik' + +import FormTextField from 'components/common/form/FormTextField' + +export default function RecurringDonationStatusSelect({ name = 'status' }) { + const { t } = useTranslation('recurring-donation') + + enum RecurringDonationStatus { + trialing = 'trialing', + active = 'active', + canceled = 'canceled', + incomplete = 'incomplete', + incompleteExpired = 'incompleteExpired', + pastDue = 'pastDue', + unpaid = 'unpaid', + } + + const values = Object.keys(RecurringDonationStatus) + const [field, meta] = useField(name) + + return ( + + + + {t('fields.' + name)} + + {values?.map((value, index) => ( + + {value} + + ))} + + + ) +} diff --git a/src/gql/recurring-donation-status.ts b/src/gql/recurring-donation-status.d.ts similarity index 100% rename from src/gql/recurring-donation-status.ts rename to src/gql/recurring-donation-status.d.ts diff --git a/src/gql/recurring-donation.d.ts b/src/gql/recurring-donation.d.ts index ca12d0d63..733e9785f 100644 --- a/src/gql/recurring-donation.d.ts +++ b/src/gql/recurring-donation.d.ts @@ -1,6 +1,5 @@ import type { Currency } from './currency' import { UUID } from './types' -import { RecurringDonationStatus } from './recurring-donation-status' export type RecurringDonationResponse = { id: UUID @@ -10,17 +9,17 @@ export type RecurringDonationResponse = { extCustomerId: UUID amount: number currency: Currency - vaultId: string + sourceVault: UUID createdAt: Date updatedAt: Date | null } export type RecurringDonationInput = { - status?: RecurringDonationStatus + status?: RecurringDonationStatus | string personId?: UUID extSubscriptionId?: UUID extCustomerId?: UUID amount?: number - currency?: Currency - vaultId?: string + currency?: Currency | string + sourceVault?: UUID } diff --git a/src/pages/admin/recurring-donation/create.tsx b/src/pages/admin/recurring-donation/create.tsx new file mode 100644 index 000000000..8deb3f49e --- /dev/null +++ b/src/pages/admin/recurring-donation/create.tsx @@ -0,0 +1,23 @@ +import { GetServerSideProps } from 'next' +import { dehydrate, QueryClient } from 'react-query' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' + +import CreatePage from 'components/recurring-donation/CreatePage' + +export const getServerSideProps: GetServerSideProps = async ({ locale }) => { + const client = new QueryClient() + + return { + props: { + ...(await serverSideTranslations(locale ?? 'bg', [ + 'common', + 'auth', + 'recurring-donation', + 'validation', + ])), + dehydratedState: dehydrate(client), + }, + } +} + +export default CreatePage diff --git a/src/service/recurringDonation.ts b/src/service/recurringDonation.ts new file mode 100644 index 000000000..042d304bc --- /dev/null +++ b/src/service/recurringDonation.ts @@ -0,0 +1,28 @@ +import { KeycloakInstance } from 'keycloak-js' +import { useKeycloak } from '@react-keycloak/ssr' +import { AxiosResponse } from 'axios' + +import { apiClient } from 'service/apiClient' +import { authConfig } from 'service/restRequests' +import { endpoints } from 'service/apiEndpoints' +import { RecurringDonationInput, RecurringDonationResponse } from 'gql/recurring-donation' + +export function useCreateRecurringDonation() { + const { keycloak } = useKeycloak() + return async (data: RecurringDonationInput) => { + return await apiClient.post< + RecurringDonationResponse, + AxiosResponse + >(endpoints.recurringDonation.createRecurringDonation.url, data, authConfig(keycloak?.token)) + } +} + +export function useEditRecurringDonation(id: string) { + const { keycloak } = useKeycloak() + return async (data: RecurringDonationInput) => { + return await apiClient.patch< + RecurringDonationResponse, + AxiosResponse + >(endpoints.recurringDonation.editRecurringDonation(id).url, data, authConfig(keycloak?.token)) + } +} From 5081edcbc8880d182227ead107ac2a8c465299ab Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Tue, 12 Apr 2022 10:40:55 +0100 Subject: [PATCH 05/59] added EditPage --- src/common/hooks/recurringDonation.ts | 11 +++ .../recurring-donation/EditPage.tsx | 21 ++++++ src/components/recurring-donation/Form.tsx | 72 +++++++++---------- .../recurring-donation/grid/Grid.tsx | 10 ++- .../admin/recurring-donation/[id]/index.tsx | 33 +++++++++ 5 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 src/pages/admin/recurring-donation/[id]/index.tsx diff --git a/src/common/hooks/recurringDonation.ts b/src/common/hooks/recurringDonation.ts index c11b2bab8..280178adf 100644 --- a/src/common/hooks/recurringDonation.ts +++ b/src/common/hooks/recurringDonation.ts @@ -21,3 +21,14 @@ export function useRecurringDonation(id: string) { authQueryFnFactory(keycloak?.token), ) } + +export async function prefetchRecurringDonationById( + client: QueryClient, + id: string, + token?: string, +) { + await client.prefetchQuery( + endpoints.recurringDonation.getRecurringDonation(id).url, + authQueryFnFactory(token), + ) +} diff --git a/src/components/recurring-donation/EditPage.tsx b/src/components/recurring-donation/EditPage.tsx index e69de29bb..8d0152bf0 100644 --- a/src/components/recurring-donation/EditPage.tsx +++ b/src/components/recurring-donation/EditPage.tsx @@ -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 ( + + + + + + + + ) +} diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index e2ceb0c63..3cfa9fbdf 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -118,46 +118,38 @@ export default function EditForm() { - {id ? ( - <> - ) : ( - <> - - - - - - - - - - - - - - - - - - - - )} + + + + + + + + + + + + + + + + + + + + {id ? <> : <>} diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index 8489505c0..eb72596a8 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -16,7 +16,7 @@ export default function Grid() { const { t } = useTranslation('recurring-donation') const { data }: UseQueryResult = useRecurringDonationList() const [pageSize, setPageSize] = useState(5) - + console.log(data) const commonProps: Partial = { align: 'left', width: 150, @@ -46,7 +46,7 @@ export default function Grid() { field: 'extSubscriptionId', headerName: t('extSubscriptionId'), ...commonProps, - width: 400, + width: 300, }, { field: 'extCustomerId', @@ -54,6 +54,12 @@ export default function Grid() { ...commonProps, width: 300, }, + { + field: 'personId', + headerName: t('personId'), + ...commonProps, + width: 300, + }, { field: 'vaultId', headerName: t('vaultId'), diff --git a/src/pages/admin/recurring-donation/[id]/index.tsx b/src/pages/admin/recurring-donation/[id]/index.tsx new file mode 100644 index 000000000..7f0df9a5f --- /dev/null +++ b/src/pages/admin/recurring-donation/[id]/index.tsx @@ -0,0 +1,33 @@ +import { GetServerSideProps } from 'next' +import { dehydrate, QueryClient } from 'react-query' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' + +import { keycloakInstance } from 'middleware/auth/keycloak' +import { endpoints } from 'service/apiEndpoints' +import { authQueryFnFactory } from 'service/restRequests' +import EditPage from 'components/recurring-donation/EditPage' + +export const getServerSideProps: GetServerSideProps = async (params) => { + const client = new QueryClient() + const keycloak = keycloakInstance(params) + const { id } = params.query + + await client.prefetchQuery( + endpoints.recurringDonation.editRecurringDonation(`${id}`).url, + authQueryFnFactory(keycloak.token), + ) + + return { + props: { + ...(await serverSideTranslations(params.locale ?? 'bg', [ + 'common', + 'auth', + 'validation', + 'recurring-donation', + ])), + dehydratedState: dehydrate(client), + }, + } +} + +export default EditPage From 100c0c0d1159a221ca85ae765e30f09661a0c733 Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Tue, 12 Apr 2022 10:55:11 +0100 Subject: [PATCH 06/59] apiEndpoinds --- src/service/apiEndpoints.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/service/apiEndpoints.ts b/src/service/apiEndpoints.ts index 0decf3d62..0a9b7aa88 100644 --- a/src/service/apiEndpoints.ts +++ b/src/service/apiEndpoints.ts @@ -150,4 +150,8 @@ export const endpoints = { { url: `/recurring-donation/${id}`, method: 'DELETE' }, deleteRecurringDonations: { url: '/recurring-donation/deletemany', method: 'POST' }, }, + account: { + me: { url: '/account/me', method: 'GET' }, + update: { url: '/account/me', method: 'PATCH' }, + }, } From 89b9b1fbc708dfe9701019630e4e10859adde312 Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Tue, 12 Apr 2022 11:36:01 +0100 Subject: [PATCH 07/59] --- .../recurring-donation/grid/DeleteModal.tsx | 41 +++++++++++++++++++ .../recurring-donation/grid/Grid.tsx | 6 +-- src/service/recurringDonation.ts | 10 +++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/grid/DeleteModal.tsx index e69de29bb..f00773369 100644 --- a/src/components/recurring-donation/grid/DeleteModal.tsx +++ b/src/components/recurring-donation/grid/DeleteModal.tsx @@ -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, + AxiosError, + 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 +}) diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index eb72596a8..ab8564df9 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -9,14 +9,14 @@ import { RecurringDonationResponse } from 'gql/recurring-donation' import { useRecurringDonationList } from 'common/hooks/recurringDonation' import GridActions from 'components/admin/GridActions' -// import DeleteModal from './DeleteModal' +import DeleteModal from './DeleteModal' import DetailsModal from './DetailsModal' export default function Grid() { const { t } = useTranslation('recurring-donation') const { data }: UseQueryResult = useRecurringDonationList() const [pageSize, setPageSize] = useState(5) - console.log(data) + const commonProps: Partial = { align: 'left', width: 150, @@ -108,7 +108,7 @@ export default function Grid() { /> - {/* */} + ) } diff --git a/src/service/recurringDonation.ts b/src/service/recurringDonation.ts index 042d304bc..2f55c9df2 100644 --- a/src/service/recurringDonation.ts +++ b/src/service/recurringDonation.ts @@ -26,3 +26,13 @@ export function useEditRecurringDonation(id: string) { >(endpoints.recurringDonation.editRecurringDonation(id).url, data, authConfig(keycloak?.token)) } } + +export function useDeleteRecurringDonation(id: string) { + const { keycloak } = useKeycloak() + return async () => { + return await apiClient.delete< + RecurringDonationResponse, + AxiosResponse + >(endpoints.recurringDonation.deleteRecurringDonation(id).url, authConfig(keycloak?.token)) + } +} From 52e94946c6beb79ea8bfc8cbb9e97f93b7122f37 Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Thu, 14 Apr 2022 10:52:00 +0100 Subject: [PATCH 08/59] added useTranslation --- public/locales/bg/recurring-donation.json | 31 +++++++++++++++++++ .../recurring-donation/grid/DetailsModal.tsx | 1 + .../recurring-donation/grid/Grid.tsx | 2 +- .../recurring-donation/grid/GridAppbar.tsx | 2 +- .../grid/RecurringDonationStatusSelect.tsx | 10 ++---- 5 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 public/locales/bg/recurring-donation.json diff --git a/public/locales/bg/recurring-donation.json b/public/locales/bg/recurring-donation.json new file mode 100644 index 000000000..53a2eab02 --- /dev/null +++ b/public/locales/bg/recurring-donation.json @@ -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": "Изпрати" + } +} \ No newline at end of file diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/grid/DetailsModal.tsx index 8e45af79d..35654d560 100644 --- a/src/components/recurring-donation/grid/DetailsModal.tsx +++ b/src/components/recurring-donation/grid/DetailsModal.tsx @@ -13,6 +13,7 @@ export default observer(function DetailsModal() { const { data }: UseQueryResult = useRecurringDonation( selectedRecord.id, ) + console.log(data) const { t } = useTranslation('recurring-donation') const dataConverted = [ diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index ab8564df9..0648dfa98 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -26,7 +26,7 @@ export default function Grid() { const columns: GridColumns = [ { field: 'status', - headerName: t('status'), + headerName: t('recurring-donation:status'), flex: 1.5, ...commonProps, }, diff --git a/src/components/recurring-donation/grid/GridAppbar.tsx b/src/components/recurring-donation/grid/GridAppbar.tsx index bc5f856be..a897fbf90 100644 --- a/src/components/recurring-donation/grid/GridAppbar.tsx +++ b/src/components/recurring-donation/grid/GridAppbar.tsx @@ -27,7 +27,7 @@ export default function GridAppbar() { height: '72px', }}> - {t('recurring-donation:recurring-donation')} + {t('recurring-donation:recurring-donations')} diff --git a/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx b/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx index aafe3f99d..ef288dcb1 100644 --- a/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx +++ b/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx @@ -26,15 +26,9 @@ export default function RecurringDonationStatusSelect({ name = 'status' }) { size="small" variant="outlined" error={Boolean(meta.error) && Boolean(meta.touched)}> - + - {t('fields.' + name)} + {t(name)} {values?.map((value, index) => ( From cffa49c17a941a3f889ecd6baab366531ef40f48 Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Thu, 14 Apr 2022 11:33:42 +0100 Subject: [PATCH 09/59] change createForm usetranslation --- src/components/recurring-donation/Form.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index 3cfa9fbdf..59a57af09 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -142,11 +142,7 @@ export default function EditForm() { - + {id ? <> : <>} From 1444b379306cedb8439f6fbc15d104cf0ac836a4 Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Fri, 15 Apr 2022 10:18:40 +0100 Subject: [PATCH 10/59] fixing EditPage --- src/components/recurring-donation/EditPage.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/recurring-donation/EditPage.tsx b/src/components/recurring-donation/EditPage.tsx index 8d0152bf0..fc7df1cc6 100644 --- a/src/components/recurring-donation/EditPage.tsx +++ b/src/components/recurring-donation/EditPage.tsx @@ -1,19 +1,27 @@ import { useTranslation } from 'next-i18next' +import { useRouter } from 'next/router' import { Container } from '@mui/material' +import { UseQueryResult } from 'react-query' import AdminContainer from 'components/admin/navigation/AdminContainer' import AdminLayout from 'components/admin/navigation/AdminLayout' - +import NotFoundIllustration from 'components/errors/assets/NotFoundIllustration' +import { useRecurringDonation } from 'common/hooks/recurringDonation' +import { RecurringDonationResponse } from 'gql/recurring-donation' import Form from './Form' export default function EditPage() { const { t } = useTranslation('recurring-donation') + const { query } = useRouter() + const { data: donation }: UseQueryResult = useRecurringDonation( + String(query.id), + ) return ( - + {donation ? : } From aa1f4498266c9bf579b9127156f0fbde587b0591 Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Thu, 21 Apr 2022 18:05:35 +0300 Subject: [PATCH 11/59] Sum the donations instead of displaying the first donation amount --- src/components/auth/profile/DonationTab.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/auth/profile/DonationTab.tsx b/src/components/auth/profile/DonationTab.tsx index 9d996f5e9..df3e4fa2d 100644 --- a/src/components/auth/profile/DonationTab.tsx +++ b/src/components/auth/profile/DonationTab.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { truncate } from 'lodash' +import { sumBy, truncate } from 'lodash' import { makeStyles } from '@mui/styles' import { Box, @@ -106,10 +106,10 @@ export default function DonationTab() { {t('profile:donations.recurringDonations')} {/* TODO: Use date-fns to format and localize the months, that the user has recurring donations when that is possible */} - Я, Ф, М, А 2022 + {/* Я, Ф, М, А 2022 */} - {money(userDonations.donations[0].amount)} + {money(sumBy(userDonations.donations, 'amount'))} From 14f0a0591e10e40f7e5e1b44a1c5cddd781162c1 Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Thu, 21 Apr 2022 18:10:13 +0300 Subject: [PATCH 12/59] Improve import paths --- src/components/auth/profile/DonationTab.tsx | 19 ++++++------- src/components/auth/profile/DonationTable.tsx | 27 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/components/auth/profile/DonationTab.tsx b/src/components/auth/profile/DonationTab.tsx index df3e4fa2d..8a1781573 100644 --- a/src/components/auth/profile/DonationTab.tsx +++ b/src/components/auth/profile/DonationTab.tsx @@ -1,6 +1,3 @@ -import React from 'react' -import { sumBy, truncate } from 'lodash' -import { makeStyles } from '@mui/styles' import { Box, Button, @@ -14,16 +11,20 @@ import { CircularProgress, useMediaQuery, } from '@mui/material' -import { useUserDonations } from 'common/hooks/donation' +import React from 'react' +import { sumBy, truncate } from 'lodash' +import { makeStyles } from '@mui/styles' +import { useTranslation } from 'next-i18next' -import ProfileTab from './ProfileTab' -import { ProfileTabs } from './tabs' import theme from 'common/theme' -import { useTranslation } from 'next-i18next' +import { money } from 'common/util/money' +import { useUserDonations } from 'common/hooks/donation' import { useCampaignList } from 'common/hooks/campaigns' -import { campaignListPictureUrl } from 'common/util/campaignImageUrls' import { useCurrentPerson } from 'common/util/useCurrentPerson' -import { money } from 'common/util/money' +import { campaignListPictureUrl } from 'common/util/campaignImageUrls' + +import { ProfileTabs } from './tabs' +import ProfileTab from './ProfileTab' import DonationTable from './DonationTable' const useStyles = makeStyles({ diff --git a/src/components/auth/profile/DonationTable.tsx b/src/components/auth/profile/DonationTable.tsx index 3fd67eba6..79ae53f4f 100644 --- a/src/components/auth/profile/DonationTable.tsx +++ b/src/components/auth/profile/DonationTable.tsx @@ -13,19 +13,20 @@ import { Avatar, Button, } from '@mui/material' +import { Box } from '@mui/system' +import styled from '@emotion/styled' +import React, { useMemo } from 'react' +import { bg, enUS } from 'date-fns/locale' +import { useTranslation } from 'next-i18next' import StarIcon from '@mui/icons-material/Star' -import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers' -import { money } from 'common/util/money' import { format, isAfter, isBefore, parseISO } from 'date-fns' -import React, { useEffect, useMemo, useState } from 'react' import ArrowForwardIcon from '@mui/icons-material/ArrowForward' -import { useTranslation } from 'next-i18next' -import { UserDonation } from 'gql/donations' -import theme from 'common/theme' -import { bg, enUS } from 'date-fns/locale' import { AdapterDateFns } from '@mui/x-date-pickers/AdapterDateFns' -import styled from '@emotion/styled' -import { Box } from '@mui/system' +import { DatePicker, LocalizationProvider } from '@mui/x-date-pickers' + +import theme from 'common/theme' +import { money } from 'common/util/money' +import { UserDonation } from 'gql/donations' export type DonationTableProps = { donations: UserDonation[] | undefined @@ -81,9 +82,7 @@ function DonationTable({ donations }: DonationTableProps) { {t('profile:donations.oneTime')} { - setOneTime(checked) - }} + onChange={(e, checked) => setOneTime(checked)} checked={oneTime} name="oneTime" /> @@ -91,9 +90,7 @@ function DonationTable({ donations }: DonationTableProps) { {t('profile:donations.monthly')} { - setMonthly(checked) - }} + onChange={(e, checked) => setMonthly(checked)} checked={monthly} name="monthly" /> From a11459e36b877cbdb7af57a5cc3bab382437017d Mon Sep 17 00:00:00 2001 From: Ani Kalpachka Date: Thu, 21 Apr 2022 19:25:28 +0300 Subject: [PATCH 13/59] Added icons before technology subtitles --- .../about-project/sections/TechStack.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/components/about-project/sections/TechStack.tsx b/src/components/about-project/sections/TechStack.tsx index d2dc1929e..9dc855282 100644 --- a/src/components/about-project/sections/TechStack.tsx +++ b/src/components/about-project/sections/TechStack.tsx @@ -1,13 +1,17 @@ import { useTranslation } from 'next-i18next' import { Box, Grid, Theme, Typography } from '@mui/material' +import JoinLeftIcon from '@mui/icons-material/JoinLeft' +import ImportantDevicesIcon from '@mui/icons-material/ImportantDevices' +import SettingsIcon from '@mui/icons-material/Settings' + +import Heading from 'components/common/Heading' import createStyles from '@mui/styles/createStyles' import makeStyles from '@mui/styles/makeStyles' -import Heading from 'components/common/Heading' - const rows = [ { + icon: , label: 'DevOps', items: [ 'about-project:tech-stack.docker', @@ -18,6 +22,7 @@ const rows = [ ], }, { + icon: , label: 'Frontend', items: [ 'TypeScript', @@ -33,6 +38,7 @@ const rows = [ ], }, { + icon: , label: 'Backend', items: ['TypeScript', 'Nest.js', 'PostgreSQL', 'Prisma', 'Jest', 'Sentry'], }, @@ -67,9 +73,12 @@ export default function TechStack() { - {rows.map(({ label, items }, section: number) => ( + {rows.map(({ label, icon, items }, section: number) => ( - {label} + + {icon} + {label} + {items.map((line: string, key: number) => ( From 5186b29acd4a997f3a4a228a457577ecf256f5c8 Mon Sep 17 00:00:00 2001 From: Ani Kalpachka Date: Thu, 21 Apr 2022 21:28:57 +0300 Subject: [PATCH 14/59] Adedd checkmark icons before the list items --- .../about-project/sections/TechStack.tsx | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/components/about-project/sections/TechStack.tsx b/src/components/about-project/sections/TechStack.tsx index 9dc855282..5e3f3379d 100644 --- a/src/components/about-project/sections/TechStack.tsx +++ b/src/components/about-project/sections/TechStack.tsx @@ -1,10 +1,12 @@ import { useTranslation } from 'next-i18next' + +import Heading from 'components/common/Heading' + import { Box, Grid, Theme, Typography } from '@mui/material' import JoinLeftIcon from '@mui/icons-material/JoinLeft' import ImportantDevicesIcon from '@mui/icons-material/ImportantDevices' import SettingsIcon from '@mui/icons-material/Settings' - -import Heading from 'components/common/Heading' +import CheckIcon from '@mui/icons-material/Check' import createStyles from '@mui/styles/createStyles' import makeStyles from '@mui/styles/makeStyles' @@ -51,8 +53,15 @@ const useStyles = makeStyles((theme: Theme) => paddingBottom: theme.spacing(7), }, list: { - listStyle: 'disc', - paddingLeft: '2rem', + margin: '0 auto 32px', + }, + listItem: { + display: 'flex', + alignItems: 'center', + gap: theme.spacing(1.5), + }, + categoryTitle: { + fontWeight: 600, }, }), ) @@ -77,11 +86,14 @@ export default function TechStack() { {icon} - {label} + + {label} + - + {items.map((line: string, key: number) => ( - + + {t(line)} ))} From 4c08320cd5f7ca7d1f1b9a5095d3b8bed66d0810 Mon Sep 17 00:00:00 2001 From: Ani Kalpachka Date: Thu, 21 Apr 2022 22:56:26 +0300 Subject: [PATCH 15/59] Fixed spacings between category wrappers --- src/components/about-project/sections/TechStack.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/about-project/sections/TechStack.tsx b/src/components/about-project/sections/TechStack.tsx index 5e3f3379d..b44d7ab9e 100644 --- a/src/components/about-project/sections/TechStack.tsx +++ b/src/components/about-project/sections/TechStack.tsx @@ -53,13 +53,19 @@ const useStyles = makeStyles((theme: Theme) => paddingBottom: theme.spacing(7), }, list: { - margin: '0 auto 32px', + margin: '0 auto', }, listItem: { display: 'flex', alignItems: 'center', gap: theme.spacing(1.5), }, + categoryWrapper: { + marginBottom: theme.spacing(6), + '&:nth-of-type(3)': { + marginBottom: 0, + }, + }, categoryTitle: { fontWeight: 600, }, @@ -81,9 +87,9 @@ export default function TechStack() { {t('about-project:tech-stack.title')} - + {rows.map(({ label, icon, items }, section: number) => ( - + {icon} From f7656dd766bdac37a18151db7cdbc29161c152a3 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Sat, 23 Apr 2022 23:32:08 +0300 Subject: [PATCH 16/59] Add form select field common component --- .../common/form/FormSelectField.tsx | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/components/common/form/FormSelectField.tsx diff --git a/src/components/common/form/FormSelectField.tsx b/src/components/common/form/FormSelectField.tsx new file mode 100644 index 000000000..b048dd58a --- /dev/null +++ b/src/components/common/form/FormSelectField.tsx @@ -0,0 +1,49 @@ +import React from 'react' +import { useField } from 'formik' +import { useTranslation } from 'next-i18next' +import { MenuItem, TextField, TextFieldProps } from '@mui/material' + +import { translateError } from 'common/form/useForm' +import { TranslatableField } from 'common/form/validation' + +export type FormSelectFieldOption = { + key: string + value: string | number + name: string +} +export type FormSelectFieldProps = { + label: string + name: string + options: FormSelectFieldOption[] +} & TextFieldProps + +export default function FormSelectField({ + label, + name, + options, + ...textFieldProps +}: FormSelectFieldProps) { + const { t } = useTranslation() + const [field, meta] = useField(name) + const helperText = meta.touched ? translateError(meta.error as TranslatableField, t) : '' + return ( + + {options.map((o) => { + return ( + + {o.name} + + ) + })} + + ) +} From 1a7d4292cffa4dbe8958e7a2eb4dd1e51c2a96ec Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Sun, 24 Apr 2022 20:39:23 +0200 Subject: [PATCH 17/59] Add person autocomplete component and start base dialog --- src/common/hooks/confirm.ts | 44 +++++++++++++ src/common/hooks/person.ts | 5 +- src/components/person/PersonAutocomplete.tsx | 47 ++++++++++++++ src/components/person/PersonSelectDialog.tsx | 67 ++++++++++++++++++++ src/components/recurring-donation/Form.tsx | 8 ++- 5 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 src/common/hooks/confirm.ts create mode 100644 src/components/person/PersonAutocomplete.tsx create mode 100644 src/components/person/PersonSelectDialog.tsx diff --git a/src/common/hooks/confirm.ts b/src/common/hooks/confirm.ts new file mode 100644 index 000000000..9ed3c8bc0 --- /dev/null +++ b/src/common/hooks/confirm.ts @@ -0,0 +1,44 @@ +import { useState } from 'react' + +export type ConfirmProps = { + onConfirm?: () => void | Promise + onClose?: () => void | Promise +} +export type ConfirmHookProps = { + open: boolean + loading: boolean + openHandler: () => void + confirmHandler: () => void + closeHandler: () => void +} + +const useConfirm = ({ onConfirm, onClose }: ConfirmProps): ConfirmHookProps => { + const [open, setOpen] = useState(false) + const [loading, setLoading] = useState(false) + + return { + open, + loading, + openHandler: () => { + setOpen(true) + }, + confirmHandler: async () => { + setOpen(false) + if (typeof onConfirm === 'function') { + setLoading(true) + await onConfirm() + setLoading(false) + } + }, + closeHandler: async () => { + setOpen(false) + if (typeof onClose === 'function') { + setLoading(true) + await onClose() + setLoading(false) + } + }, + } +} + +export default useConfirm diff --git a/src/common/hooks/person.ts b/src/common/hooks/person.ts index d65a5b198..15d91f7bc 100644 --- a/src/common/hooks/person.ts +++ b/src/common/hooks/person.ts @@ -1,14 +1,15 @@ import { useKeycloak } from '@react-keycloak/ssr' import { PersonResponse } from 'gql/person' import { KeycloakInstance } from 'keycloak-js' -import { useQuery } from 'react-query' +import { useQuery, UseQueryOptions } from 'react-query' import { endpoints } from 'service/apiEndpoints' import { authQueryFnFactory } from 'service/restRequests' -export const usePersonList = () => { +export const usePersonList = ({ options }: { options: UseQueryOptions }) => { const { keycloak } = useKeycloak() return useQuery( endpoints.person.list.url, authQueryFnFactory(keycloak?.token), + options, ) } diff --git a/src/components/person/PersonAutocomplete.tsx b/src/components/person/PersonAutocomplete.tsx new file mode 100644 index 000000000..bc0c75023 --- /dev/null +++ b/src/components/person/PersonAutocomplete.tsx @@ -0,0 +1,47 @@ +import { useTranslation } from 'react-i18next' +import { Autocomplete, AutocompleteProps, TextField } from '@mui/material' +import { PersonResponse } from 'gql/person' +import { usePersonList } from 'common/hooks/person' + +export type PersonAutocompleteProps = { + onSelect: (person: PersonResponse | null) => void + autocompleteProps?: Omit< + AutocompleteProps, + 'renderInput' | 'options' | 'getOptionLabel' | 'onChange' | 'loading' + > +} +export default function PersonAutocomplete({ + onSelect, + autocompleteProps, +}: PersonAutocompleteProps) { + const { t } = useTranslation('common') + const { + data: personList, + isLoading, + refetch, + } = usePersonList({ + options: { + enabled: false, + refetchOnWindowFocus: false, + }, + }) + return ( + option.firstName === value.firstName} + options={personList || []} + getOptionLabel={(person) => person.firstName + ' ' + person.lastName} + onChange={(e, person) => { + console.log(person) + onSelect(person) + }} + onOpen={() => { + refetch() + }} + loading={isLoading} + renderInput={(params) => ( + + )} + {...autocompleteProps} + /> + ) +} diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx new file mode 100644 index 000000000..2647d5087 --- /dev/null +++ b/src/components/person/PersonSelectDialog.tsx @@ -0,0 +1,67 @@ +import { Close } from '@mui/icons-material' +import { LoadingButton } from '@mui/lab' +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, +} from '@mui/material' +import useConfirm from 'common/hooks/confirm' +import CloseModalButton from 'components/common/CloseModalButton' +import { PersonResponse } from 'gql/person' +import React, { useState } from 'react' +import PersonAutocomplete from './PersonAutocomplete' + +type Props = { + onConfirm: (person: PersonResponse) => void +} + +function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { + const [person, setPerson] = useState(null) + const { open, confirmHandler, closeHandler, openHandler, loading } = useConfirm({ + onConfirm: async () => { + confirmCallback(person as PersonResponse) + }, + onClose: async () => { + null + }, + }) + return ( + <> + + + + Person Select + {/* theme.palette.grey[500], + }}> + + */} + + + { + setPerson(person) + }} + /> + + + + + Confirm + + + + + ) +} + +export default PersonSelectDialog diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index 59a57af09..2d71d8415 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -21,6 +21,7 @@ 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' +import PersonSelectDialog from 'components/person/PersonSelectDialog' export enum RecurringDonationStatus { trialing = 'trialing', @@ -119,7 +120,12 @@ export default function EditForm() { - + { + console.log(person) + }} + /> + {/* */} Date: Sun, 24 Apr 2022 21:14:13 +0200 Subject: [PATCH 18/59] Show person and cleanup --- src/components/person/PersonSelectDialog.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 2647d5087..626656cd0 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -1,12 +1,12 @@ -import { Close } from '@mui/icons-material' import { LoadingButton } from '@mui/lab' import { + Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, - IconButton, + Typography, } from '@mui/material' import useConfirm from 'common/hooks/confirm' import CloseModalButton from 'components/common/CloseModalButton' @@ -30,7 +30,12 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { }) return ( <> - + + {person ? person.firstName : 'No personSelected'} + + Person Select From b4de6a10000af25319a4dbd48bfa7b3cff491ecc Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Mon, 25 Apr 2022 22:15:34 +0200 Subject: [PATCH 19/59] Dialog full width and showId props on autocomplete --- src/components/person/PersonAutocomplete.tsx | 17 +++++++-- src/components/person/PersonSelectDialog.tsx | 37 ++++++++++---------- src/components/recurring-donation/Form.tsx | 9 +++-- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/components/person/PersonAutocomplete.tsx b/src/components/person/PersonAutocomplete.tsx index bc0c75023..653f2ffce 100644 --- a/src/components/person/PersonAutocomplete.tsx +++ b/src/components/person/PersonAutocomplete.tsx @@ -9,9 +9,11 @@ export type PersonAutocompleteProps = { AutocompleteProps, 'renderInput' | 'options' | 'getOptionLabel' | 'onChange' | 'loading' > + showId: boolean } export default function PersonAutocomplete({ onSelect, + showId, autocompleteProps, }: PersonAutocompleteProps) { const { t } = useTranslation('common') @@ -29,9 +31,12 @@ export default function PersonAutocomplete({ option.firstName === value.firstName} options={personList || []} - getOptionLabel={(person) => person.firstName + ' ' + person.lastName} + getOptionLabel={(person) => + showId + ? `${person.firstName} ${person.lastName} (${person.id})` + : person.firstName + ' ' + person.lastName + } onChange={(e, person) => { - console.log(person) onSelect(person) }} onOpen={() => { @@ -39,7 +44,13 @@ export default function PersonAutocomplete({ }} loading={isLoading} renderInput={(params) => ( - + )} {...autocompleteProps} /> diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 626656cd0..7ba8f4589 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -6,9 +6,11 @@ import { DialogActions, DialogContent, DialogTitle, + Grid, Typography, } from '@mui/material' import useConfirm from 'common/hooks/confirm' +import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' import { PersonResponse } from 'gql/person' import React, { useState } from 'react' @@ -31,35 +33,34 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { return ( <> - {person ? person.firstName : 'No personSelected'} - - - - Person Select - {/* theme.palette.grey[500], - }}> - - */} - + + Person Select { setPerson(person) }} + showId /> + {person ? ( + + ID: {person.id} + + ) : ( + "You haven't selected a person" + )} - + Confirm diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index 2d71d8415..f3f65ec97 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -116,16 +116,15 @@ export default function EditForm() { {id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')} - - - - + { console.log(person) }} /> - {/* */} + + + Date: Mon, 25 Apr 2022 22:34:19 +0200 Subject: [PATCH 20/59] Add person info component --- src/components/person/PersonInfo.tsx | 38 ++++++++++++++++++++ src/components/person/PersonSelectDialog.tsx | 11 +++--- src/gql/person.d.ts | 7 ++++ 3 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 src/components/person/PersonInfo.tsx diff --git a/src/components/person/PersonInfo.tsx b/src/components/person/PersonInfo.tsx new file mode 100644 index 000000000..7634aa493 --- /dev/null +++ b/src/components/person/PersonInfo.tsx @@ -0,0 +1,38 @@ +import { Box, Grid, Typography } from '@mui/material' +import theme from 'common/theme' +import { format, parseISO } from 'date-fns' +import { PersonResponse } from 'gql/person' +import React from 'react' + +type Props = { + person: PersonResponse +} + +function PersonInfo({ person }: Props) { + return ( + + + + Contact information: + + + Email: {person.email} + Tel: {person.phone} + Tel: {person.address} + + + + + General information: + + + Created at: {format(parseISO(person.createdAt), 'PPpp')} + Company: {person.company} + Confirmed email: {person.emailConfirmed ? 'Yes' : 'No'} + + + + ) +} + +export default PersonInfo diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 7ba8f4589..09a4bb0f5 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -15,6 +15,7 @@ import CloseModalButton from 'components/common/CloseModalButton' import { PersonResponse } from 'gql/person' import React, { useState } from 'react' import PersonAutocomplete from './PersonAutocomplete' +import PersonInfo from './PersonInfo' type Props = { onConfirm: (person: PersonResponse) => void @@ -51,13 +52,9 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { }} showId /> - {person ? ( - - ID: {person.id} - - ) : ( - "You haven't selected a person" - )} + + {person ? : "You haven't selected a person"} + diff --git a/src/gql/person.d.ts b/src/gql/person.d.ts index 6aa8c7a49..46972448e 100644 --- a/src/gql/person.d.ts +++ b/src/gql/person.d.ts @@ -5,6 +5,13 @@ export type PersonResponse = { personId: string firstName: string lastName: string + email: string + phone: string + address: string + company: string + createdAt: string + newsletter: boolean + emailConfirmed: boolean } export type PersonFormData = { From abc576f2e02836647afc69efa578123f6bd499ee Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Mon, 25 Apr 2022 22:40:33 +0200 Subject: [PATCH 21/59] Styled person info --- src/components/person/PersonInfo.tsx | 29 ++++++++++++++++---- src/components/person/PersonSelectDialog.tsx | 1 - 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/components/person/PersonInfo.tsx b/src/components/person/PersonInfo.tsx index 7634aa493..cc93b12c3 100644 --- a/src/components/person/PersonInfo.tsx +++ b/src/components/person/PersonInfo.tsx @@ -1,4 +1,6 @@ -import { Box, Grid, Typography } from '@mui/material' +import { Box, Grid, Theme, Typography } from '@mui/material' +import createStyles from '@mui/styles/createStyles' +import makeStyles from '@mui/styles/makeStyles' import theme from 'common/theme' import { format, parseISO } from 'date-fns' import { PersonResponse } from 'gql/person' @@ -8,24 +10,39 @@ type Props = { person: PersonResponse } +const useStyles = makeStyles((theme: Theme) => + createStyles({ + infoHeading: { + marginBottom: theme.spacing(2), + marginTop: theme.spacing(2), + }, + infoWrapper: { + '&>*': { + marginBottom: theme.spacing(1), + }, + }, + }), +) + function PersonInfo({ person }: Props) { + const classes = useStyles() return ( - + Contact information: - + Email: {person.email} Tel: {person.phone} - Tel: {person.address} + Adress: {person.address} - + General information: - + Created at: {format(parseISO(person.createdAt), 'PPpp')} Company: {person.company} Confirmed email: {person.emailConfirmed ? 'Yes' : 'No'} diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 09a4bb0f5..531f373dc 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -6,7 +6,6 @@ import { DialogActions, DialogContent, DialogTitle, - Grid, Typography, } from '@mui/material' import useConfirm from 'common/hooks/confirm' From 347cbf0af3b17a8e33b75b4824b01161a0ed19e5 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 20:28:14 +0200 Subject: [PATCH 22/59] Add select person styled box --- public/locales/bg/person.json | 11 ++++ public/locales/en/person.json | 11 ++++ src/components/person/PersonAutocomplete.tsx | 4 +- src/components/person/PersonSelectDialog.tsx | 56 +++++++++++++------ src/pages/admin/recurring-donation/create.tsx | 1 + 5 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 public/locales/bg/person.json create mode 100644 public/locales/en/person.json diff --git a/public/locales/bg/person.json b/public/locales/bg/person.json new file mode 100644 index 000000000..bd1e07340 --- /dev/null +++ b/public/locales/bg/person.json @@ -0,0 +1,11 @@ +{ + "selectDialog": { + "notSelected": "Не сте избрали човек", + "select": "Избиране", + "personSelect": "Изберете човек", + "confirm": "Потвърждавам" + }, + "autocomplete": { + "personSearch": "Намерете човек" + } +} diff --git a/public/locales/en/person.json b/public/locales/en/person.json new file mode 100644 index 000000000..276e47fe2 --- /dev/null +++ b/public/locales/en/person.json @@ -0,0 +1,11 @@ +{ + "selectDialog": { + "notSelected": "You haven't chosen a person", + "select": "Select", + "personSelect": "Person Select", + "confirm": "Confirm" + }, + "autocomplete": { + "personSearch": "Find a person" + } +} diff --git a/src/components/person/PersonAutocomplete.tsx b/src/components/person/PersonAutocomplete.tsx index 653f2ffce..cf85ee102 100644 --- a/src/components/person/PersonAutocomplete.tsx +++ b/src/components/person/PersonAutocomplete.tsx @@ -16,7 +16,7 @@ export default function PersonAutocomplete({ showId, autocompleteProps, }: PersonAutocompleteProps) { - const { t } = useTranslation('common') + const { t } = useTranslation('person') const { data: personList, isLoading, @@ -49,7 +49,7 @@ export default function PersonAutocomplete({ type="text" fullWidth defaultValue="" - label={t('donation:recurring.personSearch')} + label={t('person:autocomplete.personSearch')} /> )} {...autocompleteProps} diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 531f373dc..2b077e95b 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -6,12 +6,16 @@ import { DialogActions, DialogContent, DialogTitle, + InputAdornment, + TextField, Typography, } from '@mui/material' +import { makeStyles } from '@mui/styles' import useConfirm from 'common/hooks/confirm' import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' import { PersonResponse } from 'gql/person' +import { useTranslation } from 'next-i18next' import React, { useState } from 'react' import PersonAutocomplete from './PersonAutocomplete' import PersonInfo from './PersonInfo' @@ -20,8 +24,21 @@ type Props = { onConfirm: (person: PersonResponse) => void } +const useStyles = makeStyles({ + imitateInputBox: { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + border: `1px solid rgba(0, 0, 0, 0.23)`, + borderRadius: '3px', + padding: '8.5px 14px', + cursor: 'pointer', + }, +}) function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { const [person, setPerson] = useState(null) + const { t } = useTranslation('person') + const classes = useStyles() const { open, confirmHandler, closeHandler, openHandler, loading } = useConfirm({ onConfirm: async () => { confirmCallback(person as PersonResponse) @@ -32,33 +49,36 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { }) return ( <> - - {person ? person.firstName : 'You need to select a person'} - - Person Select + {t('person:selectDialog.personSelect')} - { - setPerson(person) - }} - showId - /> - - {person ? : "You haven't selected a person"} + + { + setPerson(person) + }} + showId + /> + + + + {person ? : t('person:selectDialog.notSelected')} - Confirm + {t('person:selectDialog.confirm')} diff --git a/src/pages/admin/recurring-donation/create.tsx b/src/pages/admin/recurring-donation/create.tsx index 8deb3f49e..8285ad24d 100644 --- a/src/pages/admin/recurring-donation/create.tsx +++ b/src/pages/admin/recurring-donation/create.tsx @@ -14,6 +14,7 @@ export const getServerSideProps: GetServerSideProps = async ({ locale }) => { 'auth', 'recurring-donation', 'validation', + 'person', ])), dehydratedState: dehydrate(client), }, From aacb24a543923091f565153aa6bed2878170232d Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 21:35:59 +0200 Subject: [PATCH 23/59] Add validation --- src/components/person/PersonSelectDialog.tsx | 37 ++++-- src/components/recurring-donation/Form.tsx | 113 ++++++++++--------- 2 files changed, 92 insertions(+), 58 deletions(-) diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 2b077e95b..cf48dd8c1 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -6,11 +6,10 @@ import { DialogActions, DialogContent, DialogTitle, - InputAdornment, - TextField, Typography, } from '@mui/material' import { makeStyles } from '@mui/styles' +import { translateError } from 'common/form/validation' import useConfirm from 'common/hooks/confirm' import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' @@ -21,7 +20,9 @@ import PersonAutocomplete from './PersonAutocomplete' import PersonInfo from './PersonInfo' type Props = { - onConfirm: (person: PersonResponse) => void + onConfirm?: (person: PersonResponse | null) => void + onClose?: (person: PersonResponse | null) => void + error?: string } const useStyles = makeStyles({ @@ -34,22 +35,43 @@ const useStyles = makeStyles({ padding: '8.5px 14px', cursor: 'pointer', }, + errorInputBox: { + borderColor: '#d32f2f', + color: '#d32f2f', + }, + errorText: { + color: '#d32f2f', + fontWeight: 400, + fontSize: '0.75rem', + lineHeight: 1.66, + letterSpacing: '0.03333em', + textAlign: 'left', + marginTop: '4px', + marginRight: '14px', + marginBottom: 0, + marginLeft: '14px', + }, }) -function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { +function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback, error }: Props) { const [person, setPerson] = useState(null) const { t } = useTranslation('person') const classes = useStyles() const { open, confirmHandler, closeHandler, openHandler, loading } = useConfirm({ onConfirm: async () => { - confirmCallback(person as PersonResponse) + confirmCallback ? confirmCallback(person) : null }, onClose: async () => { - null + closeCallback ? closeCallback(person) : null }, }) + return ( <> - + {person ? `${person.firstName} ${person.lastName} (${person.id})` @@ -59,6 +81,7 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { {t('person:selectDialog.select')} + {error ?

{translateError(error, t)}

: null} {t('person:selectDialog.personSelect')} diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index f3f65ec97..5ceffcfd5 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -22,6 +22,7 @@ import SubmitButton from 'components/common/form/SubmitButton' import CurrencySelect from 'components/currency/CurrencySelect' import RecurringDonationStatusSelect from './grid/RecurringDonationStatusSelect' import PersonSelectDialog from 'components/person/PersonSelectDialog' +import { Formik } from 'formik' export enum RecurringDonationStatus { trialing = 'trialing', @@ -53,7 +54,6 @@ export default function EditForm() { const router = useRouter() const queryClient = useQueryClient() const { t } = useTranslation() - let id = router.query.id let initialValues: RecurringDonationInput = { @@ -107,60 +107,71 @@ export default function EditForm() { } return ( - - - - {id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')} - - - - { - console.log(person) - }} - /> - - - - - - - - - - - - - - - - - - - + {({ errors, setFieldTouched, setFieldValue }) => ( + + + {id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')} + + + + { + person ? setFieldValue('personId', person.id) : setFieldTouched('personId') + }} + onClose={(person) => { + person ? setFieldValue('personId', person.id) : setFieldTouched('personId') + }} + /> + + + + + + + + + + + + + + + + + + + - {id ? <> : <>} - - - - - - - + {id ? <> : <>} + + + + + + + + - - - +
+ )} + ) } From fe3efae0df480390807b4e8739e537a7e274fd45 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 21:54:34 +0200 Subject: [PATCH 24/59] Extract FormFieldButton as a component --- src/components/common/FormFieldButton.tsx | 62 ++++++++++++++++++++ src/components/person/PersonSelectDialog.tsx | 62 +++----------------- 2 files changed, 71 insertions(+), 53 deletions(-) create mode 100644 src/components/common/FormFieldButton.tsx diff --git a/src/components/common/FormFieldButton.tsx b/src/components/common/FormFieldButton.tsx new file mode 100644 index 000000000..2d2ffd607 --- /dev/null +++ b/src/components/common/FormFieldButton.tsx @@ -0,0 +1,62 @@ +import React from 'react' +import { Button, Typography, Box } from '@mui/material' +import makeStyles from '@mui/styles/makeStyles' + +type Props = { + error?: string + onClick?: () => void + placeholder?: string + value?: string + button?: { label: string } +} + +const useStyles = makeStyles({ + imitateInputBox: { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + border: `1px solid rgba(0, 0, 0, 0.23)`, + borderRadius: '3px', + padding: '8.5px 14px', + cursor: 'pointer', + }, + errorInputBox: { + borderColor: '#d32f2f', + color: '#d32f2f', + }, + errorText: { + color: '#d32f2f', + fontWeight: 400, + fontSize: '0.75rem', + lineHeight: 1.66, + letterSpacing: '0.03333em', + textAlign: 'left', + marginTop: '4px', + marginRight: '14px', + marginBottom: 0, + marginLeft: '14px', + }, +}) + +function FormFieldButton({ error, onClick, value, placeholder, button }: Props) { + const classes = useStyles() + return ( + <> + + {value || placeholder} + {button ? ( + + ) : null} + + {error ?

{error}

: null} + + ) +} + +export default FormFieldButton diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index cf48dd8c1..c10a095fc 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -1,18 +1,10 @@ import { LoadingButton } from '@mui/lab' -import { - Box, - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - Typography, -} from '@mui/material' -import { makeStyles } from '@mui/styles' +import { Box, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material' import { translateError } from 'common/form/validation' import useConfirm from 'common/hooks/confirm' import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' +import FormFieldButton from 'components/common/FormFieldButton' import { PersonResponse } from 'gql/person' import { useTranslation } from 'next-i18next' import React, { useState } from 'react' @@ -25,37 +17,9 @@ type Props = { error?: string } -const useStyles = makeStyles({ - imitateInputBox: { - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - border: `1px solid rgba(0, 0, 0, 0.23)`, - borderRadius: '3px', - padding: '8.5px 14px', - cursor: 'pointer', - }, - errorInputBox: { - borderColor: '#d32f2f', - color: '#d32f2f', - }, - errorText: { - color: '#d32f2f', - fontWeight: 400, - fontSize: '0.75rem', - lineHeight: 1.66, - letterSpacing: '0.03333em', - textAlign: 'left', - marginTop: '4px', - marginRight: '14px', - marginBottom: 0, - marginLeft: '14px', - }, -}) function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback, error }: Props) { const [person, setPerson] = useState(null) - const { t } = useTranslation('person') - const classes = useStyles() + const { t } = useTranslation() const { open, confirmHandler, closeHandler, openHandler, loading } = useConfirm({ onConfirm: async () => { confirmCallback ? confirmCallback(person) : null @@ -67,21 +31,13 @@ function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback return ( <> - - - {person - ? `${person.firstName} ${person.lastName} (${person.id})` - : t('person:selectDialog.notSelected')} - - - - {error ?

{translateError(error, t)}

: null} + placeholder={t('person:selectDialog.notSelected')} + value={person ? `${person.firstName} ${person.lastName} (${person.id})` : undefined} + button={{ label: t('person:selectDialog.select') }} + error={error ? translateError(error, t) : undefined} + /> {t('person:selectDialog.personSelect')} From 943204c3ff80572ee0fc8bd0fca8901ad153790d Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 22:04:16 +0200 Subject: [PATCH 25/59] Add translations for person info component --- public/locales/bg/person.json | 10 ++++++++++ public/locales/en/person.json | 10 ++++++++++ src/components/person/PersonInfo.tsx | 30 ++++++++++++++++++++-------- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/public/locales/bg/person.json b/public/locales/bg/person.json index bd1e07340..007f129bf 100644 --- a/public/locales/bg/person.json +++ b/public/locales/bg/person.json @@ -7,5 +7,15 @@ }, "autocomplete": { "personSearch": "Намерете човек" + }, + "info": { + "tel": "Тел", + "contact": "Контакти", + "address": "Адрес", + "email": "Имейл", + "general": "Генерална информация", + "createdAt": "Създаден в", + "company": "Компания", + "confirmedEmail": "Потвърден имейл" } } diff --git a/public/locales/en/person.json b/public/locales/en/person.json index 276e47fe2..43cbdcbe2 100644 --- a/public/locales/en/person.json +++ b/public/locales/en/person.json @@ -7,5 +7,15 @@ }, "autocomplete": { "personSearch": "Find a person" + }, + "info": { + "tel": "Тел", + "contact": "Contact info", + "address": "Address", + "email": "Email", + "general": "General information", + "createdAt": "Created at", + "company": "Company", + "confirmedEmail": "Confirmed email" } } diff --git a/src/components/person/PersonInfo.tsx b/src/components/person/PersonInfo.tsx index cc93b12c3..ec921006f 100644 --- a/src/components/person/PersonInfo.tsx +++ b/src/components/person/PersonInfo.tsx @@ -4,6 +4,7 @@ import makeStyles from '@mui/styles/makeStyles' import theme from 'common/theme' import { format, parseISO } from 'date-fns' import { PersonResponse } from 'gql/person' +import { useTranslation } from 'next-i18next' import React from 'react' type Props = { @@ -26,26 +27,39 @@ const useStyles = makeStyles((theme: Theme) => function PersonInfo({ person }: Props) { const classes = useStyles() + const { t } = useTranslation() return ( - Contact information: + {t('person:info.contact')} - Email: {person.email} - Tel: {person.phone} - Adress: {person.address} + + {t('person:info.email')}: {person.email} + + + {t('person:info.tel')}: {person.phone} + + + {t('person:info.address')}: {person.address} + - General information: + {t('person:info.general')} - Created at: {format(parseISO(person.createdAt), 'PPpp')} - Company: {person.company} - Confirmed email: {person.emailConfirmed ? 'Yes' : 'No'} + + {t('person:info.createdAt')}: {format(parseISO(person.createdAt), 'PPpp')} + + + {t('person:info.company')}: {person.company} + + + {t('person:info.confirmedEmail')}: {person.emailConfirmed ? 'Yes' : 'No'} + From 95fedb9d7a1c818ab2b7b06e662d78ad23679d4e Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:18:34 +0200 Subject: [PATCH 26/59] Resolve comments and fix minor issues --- src/common/hooks/person.ts | 2 +- src/common/hooks/{confirm.ts => useConfirm.ts} | 0 src/components/person/PersonAutocomplete.tsx | 8 +++----- src/components/person/PersonInfo.tsx | 4 ++-- src/components/person/PersonSelectDialog.tsx | 5 +++-- src/components/recurring-donation/Form.tsx | 1 - 6 files changed, 9 insertions(+), 11 deletions(-) rename src/common/hooks/{confirm.ts => useConfirm.ts} (100%) diff --git a/src/common/hooks/person.ts b/src/common/hooks/person.ts index 15d91f7bc..496a70b24 100644 --- a/src/common/hooks/person.ts +++ b/src/common/hooks/person.ts @@ -5,7 +5,7 @@ import { useQuery, UseQueryOptions } from 'react-query' import { endpoints } from 'service/apiEndpoints' import { authQueryFnFactory } from 'service/restRequests' -export const usePersonList = ({ options }: { options: UseQueryOptions }) => { +export const usePersonList = (options?: UseQueryOptions) => { const { keycloak } = useKeycloak() return useQuery( endpoints.person.list.url, diff --git a/src/common/hooks/confirm.ts b/src/common/hooks/useConfirm.ts similarity index 100% rename from src/common/hooks/confirm.ts rename to src/common/hooks/useConfirm.ts diff --git a/src/components/person/PersonAutocomplete.tsx b/src/components/person/PersonAutocomplete.tsx index cf85ee102..82233590c 100644 --- a/src/components/person/PersonAutocomplete.tsx +++ b/src/components/person/PersonAutocomplete.tsx @@ -9,7 +9,7 @@ export type PersonAutocompleteProps = { AutocompleteProps, 'renderInput' | 'options' | 'getOptionLabel' | 'onChange' | 'loading' > - showId: boolean + showId?: boolean } export default function PersonAutocomplete({ onSelect, @@ -22,10 +22,8 @@ export default function PersonAutocomplete({ isLoading, refetch, } = usePersonList({ - options: { - enabled: false, - refetchOnWindowFocus: false, - }, + enabled: false, + refetchOnWindowFocus: false, }) return ( - {t('person:info.createdAt')}: {format(parseISO(person.createdAt), 'PPpp')} + {t('person:info.createdAt')}: {formatDateString(person.createdAt)} {t('person:info.company')}: {person.company} diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index c10a095fc..d5cfdf846 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -1,7 +1,7 @@ import { LoadingButton } from '@mui/lab' import { Box, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material' import { translateError } from 'common/form/validation' -import useConfirm from 'common/hooks/confirm' +import useConfirm from 'common/hooks/useConfirm' import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' import FormFieldButton from 'components/common/FormFieldButton' @@ -28,7 +28,7 @@ function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback closeCallback ? closeCallback(person) : null }, }) - + console.log(person) return ( <> diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index 5ceffcfd5..ec79850bf 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -16,7 +16,6 @@ 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' From 88c6f7714b45fe3381611b48b8d479ef4f9eee81 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:25:07 +0200 Subject: [PATCH 27/59] Fix modal store problem --- src/components/recurring-donation/grid/DeleteModal.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/grid/DeleteModal.tsx index f00773369..bbf40ff98 100644 --- a/src/components/recurring-donation/grid/DeleteModal.tsx +++ b/src/components/recurring-donation/grid/DeleteModal.tsx @@ -7,14 +7,15 @@ 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 { ModalStoreImpl as 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 ModalStoreInst = new ModalStore() + const { hideDelete, selectedRecord } = ModalStoreInst const { t } = useTranslation('recurring-donation') const mutationFn = useDeleteRecurringDonation(selectedRecord.id) @@ -37,5 +38,5 @@ export default observer(function DeleteModal() { deleteMutation.mutate(selectedRecord.id) } - return + return }) From 40a2fb53c92ce5a431e72bccccb62323e7edf9ea Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:30:34 +0200 Subject: [PATCH 28/59] Make only one modal store instance --- src/components/recurring-donation/grid/DeleteModal.tsx | 7 +++---- src/components/recurring-donation/grid/DetailsModal.tsx | 4 ++-- src/stores/dashboard/ModalStore.ts | 2 ++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/grid/DeleteModal.tsx index bbf40ff98..cbc2ef678 100644 --- a/src/components/recurring-donation/grid/DeleteModal.tsx +++ b/src/components/recurring-donation/grid/DeleteModal.tsx @@ -7,15 +7,14 @@ import { useTranslation } from 'next-i18next' import { RecurringDonationResponse } from 'gql/recurring-donation' import { ApiErrors } from 'service/apiErrors' import { useDeleteRecurringDonation } from 'service/recurringDonation' -import { ModalStoreImpl as ModalStore } from 'stores/dashboard/ModalStore' +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 ModalStoreInst = new ModalStore() - const { hideDelete, selectedRecord } = ModalStoreInst + const { hideDelete, selectedRecord } = ModalStore const { t } = useTranslation('recurring-donation') const mutationFn = useDeleteRecurringDonation(selectedRecord.id) @@ -38,5 +37,5 @@ export default observer(function DeleteModal() { deleteMutation.mutate(selectedRecord.id) } - return + return }) diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/grid/DetailsModal.tsx index 35654d560..804a61731 100644 --- a/src/components/recurring-donation/grid/DetailsModal.tsx +++ b/src/components/recurring-donation/grid/DetailsModal.tsx @@ -23,8 +23,8 @@ export default observer(function DetailsModal() { { name: t('amount'), value: `${data?.amount}` }, { name: t('extSubscriptionId'), value: `${data?.extSubscriptionId}` }, { name: t('extCustomerId'), value: `${data?.extCustomerId}` }, - { name: t('vaultId'), value: `${data?.vaultId}` }, + { name: t('vaultId'), value: `${data?.sourceVault}` }, ] - return + return }) diff --git a/src/stores/dashboard/ModalStore.ts b/src/stores/dashboard/ModalStore.ts index 35203468e..898d46ee8 100644 --- a/src/stores/dashboard/ModalStore.ts +++ b/src/stores/dashboard/ModalStore.ts @@ -48,3 +48,5 @@ export class ModalStoreImpl { this.selectedRecord = record } } + +export const ModalStore = new ModalStoreImpl() From 3d84901607f5a00dcb448e462c2c675b94267a13 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:32:49 +0200 Subject: [PATCH 29/59] Add modal store to GridActions --- src/components/recurring-donation/grid/Grid.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index 0648dfa98..45524fd17 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'next-i18next' import { Box } from '@mui/material' import { DataGrid, GridColDef, GridColumns, GridRenderCellParams } from '@mui/x-data-grid' +import { ModalStore } from 'stores/dashboard/ModalStore' import { routes } from 'common/routes' import { RecurringDonationResponse } from 'gql/recurring-donation' import { useRecurringDonationList } from 'common/hooks/recurringDonation' @@ -75,6 +76,7 @@ export default function Grid() { renderCell: (params: GridRenderCellParams): React.ReactNode => { return ( Date: Tue, 26 Apr 2022 23:33:59 +0200 Subject: [PATCH 30/59] Clean up folder structure --- src/components/recurring-donation/{grid => }/DeleteModal.tsx | 0 src/components/recurring-donation/{grid => }/DetailsModal.tsx | 0 .../{grid => }/RecurringDonationStatusSelect.tsx | 0 src/components/recurring-donation/grid/Grid.tsx | 4 ++-- 4 files changed, 2 insertions(+), 2 deletions(-) rename src/components/recurring-donation/{grid => }/DeleteModal.tsx (100%) rename src/components/recurring-donation/{grid => }/DetailsModal.tsx (100%) rename src/components/recurring-donation/{grid => }/RecurringDonationStatusSelect.tsx (100%) diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/DeleteModal.tsx similarity index 100% rename from src/components/recurring-donation/grid/DeleteModal.tsx rename to src/components/recurring-donation/DeleteModal.tsx diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/DetailsModal.tsx similarity index 100% rename from src/components/recurring-donation/grid/DetailsModal.tsx rename to src/components/recurring-donation/DetailsModal.tsx diff --git a/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx b/src/components/recurring-donation/RecurringDonationStatusSelect.tsx similarity index 100% rename from src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx rename to src/components/recurring-donation/RecurringDonationStatusSelect.tsx diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index 45524fd17..0e9a601be 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -10,8 +10,8 @@ import { RecurringDonationResponse } from 'gql/recurring-donation' import { useRecurringDonationList } from 'common/hooks/recurringDonation' import GridActions from 'components/admin/GridActions' -import DeleteModal from './DeleteModal' -import DetailsModal from './DetailsModal' +import DeleteModal from '../DeleteModal' +import DetailsModal from '../DetailsModal' export default function Grid() { const { t } = useTranslation('recurring-donation') From cd683110cc098d6d7f407ce2a6325713ab820358 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:36:07 +0200 Subject: [PATCH 31/59] Update imports for RecurringDonationStatusSelect --- src/components/recurring-donation/Form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index ec79850bf..b1b1f7d2c 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -19,7 +19,7 @@ import { AlertStore } from 'stores/AlertStore' 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' +import RecurringDonationStatusSelect from './RecurringDonationStatusSelect' import PersonSelectDialog from 'components/person/PersonSelectDialog' import { Formik } from 'formik' From 5cece466bc03f13041e165cf33bd6dce6ec0133f Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Thu, 7 Apr 2022 15:51:50 +0100 Subject: [PATCH 32/59] Created Recurring Donation Page --- src/common/hooks/recurringDonation.ts | 15 +++ src/common/routes.ts | 5 + .../recurring-donation/CreatePage.tsx | 0 .../recurring-donation/EditPage.tsx | 0 src/components/recurring-donation/Form.tsx | 0 .../RecurringDonationPage.tsx | 19 +++ .../recurring-donation/grid/DeleteModal.tsx | 0 .../recurring-donation/grid/DetailsModal.tsx | 0 .../recurring-donation/grid/Grid.tsx | 108 ++++++++++++++++++ .../recurring-donation/grid/GridAppbar.tsx | 45 ++++++++ src/gql/recurring-donation-status.ts | 9 ++ src/gql/recurring-donation.d.ts | 26 +++++ src/pages/admin/recurring-donation/index.tsx | 29 +++++ src/service/apiEndpoints.ts | 11 ++ 14 files changed, 267 insertions(+) create mode 100644 src/common/hooks/recurringDonation.ts create mode 100644 src/components/recurring-donation/CreatePage.tsx create mode 100644 src/components/recurring-donation/EditPage.tsx create mode 100644 src/components/recurring-donation/Form.tsx create mode 100644 src/components/recurring-donation/RecurringDonationPage.tsx create mode 100644 src/components/recurring-donation/grid/DeleteModal.tsx create mode 100644 src/components/recurring-donation/grid/DetailsModal.tsx create mode 100644 src/components/recurring-donation/grid/Grid.tsx create mode 100644 src/components/recurring-donation/grid/GridAppbar.tsx create mode 100644 src/gql/recurring-donation-status.ts create mode 100644 src/gql/recurring-donation.d.ts create mode 100644 src/pages/admin/recurring-donation/index.tsx diff --git a/src/common/hooks/recurringDonation.ts b/src/common/hooks/recurringDonation.ts new file mode 100644 index 000000000..16bc6b352 --- /dev/null +++ b/src/common/hooks/recurringDonation.ts @@ -0,0 +1,15 @@ +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() + return useQuery( + endpoints.recurringDonation.recurringDonation.url, + authQueryFnFactory(keycloak?.token), + ) +} diff --git a/src/common/routes.ts b/src/common/routes.ts index dbad76f27..7da96770d 100644 --- a/src/common/routes.ts +++ b/src/common/routes.ts @@ -130,6 +130,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', diff --git a/src/components/recurring-donation/CreatePage.tsx b/src/components/recurring-donation/CreatePage.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/EditPage.tsx b/src/components/recurring-donation/EditPage.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/RecurringDonationPage.tsx b/src/components/recurring-donation/RecurringDonationPage.tsx new file mode 100644 index 000000000..5635877c2 --- /dev/null +++ b/src/components/recurring-donation/RecurringDonationPage.tsx @@ -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 ( + + + + + + + ) +} diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/grid/DeleteModal.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/grid/DetailsModal.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx new file mode 100644 index 000000000..f45301401 --- /dev/null +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -0,0 +1,108 @@ +import React, { useState } from 'react' +import { UseQueryResult } from 'react-query' +import { useTranslation } from 'next-i18next' +import { Box } from '@mui/material' +import { DataGrid, GridColDef, GridColumns, GridRenderCellParams } from '@mui/x-data-grid' + +import { routes } from 'common/routes' +import { RecurringDonationResponse } from 'gql/recurring-donation' +import { useRecurringDonationList } from 'common/hooks/recurringDonation' +import GridActions from 'components/admin/GridActions' + +// import DeleteModal from './DeleteModal' +// import DetailsModal from './DetailsModal' + +export default function Grid() { + const { t } = useTranslation('recurring-donation') + const { data }: UseQueryResult = useRecurringDonationList() + const [pageSize, setPageSize] = useState(5) + + const commonProps: Partial = { + align: 'left', + width: 150, + headerAlign: 'left', + } + + const columns: GridColumns = [ + { + field: 'status', + headerName: t('status'), + flex: 1.5, + ...commonProps, + }, + { + field: 'currency', + headerName: t('currency'), + flex: 1.5, + ...commonProps, + }, + { + field: 'amount', + headerName: t('amount'), + flex: 1.5, + ...commonProps, + }, + { + field: 'extSubscriptionId', + headerName: t('extSubscriptionId'), + ...commonProps, + width: 400, + }, + { + field: 'extCustomerId', + headerName: t('extCustomerId'), + ...commonProps, + width: 300, + }, + { + field: 'vaultId', + headerName: t('vaultId'), + ...commonProps, + width: 300, + }, + { + field: 'actions', + headerName: t('actions'), + width: 120, + type: 'actions', + headerAlign: 'center', + renderCell: (params: GridRenderCellParams): React.ReactNode => { + return ( + + ) + }, + }, + ] + + return ( + <> + + setPageSize(newPageSize)} + disableSelectionOnClick + /> + + {/* + */} + + ) +} diff --git a/src/components/recurring-donation/grid/GridAppbar.tsx b/src/components/recurring-donation/grid/GridAppbar.tsx new file mode 100644 index 000000000..bc5f856be --- /dev/null +++ b/src/components/recurring-donation/grid/GridAppbar.tsx @@ -0,0 +1,45 @@ +import { useRouter } from 'next/router' +import { useTranslation } from 'next-i18next' +import { Box, Toolbar, Tooltip, Typography } from '@mui/material' +import { Add as AddIcon } from '@mui/icons-material' + +import { routes } from 'common/routes' + +const addIconStyles = { + background: '#4ac3ff', + borderRadius: '50%', + cursor: 'pointer', + padding: 1.2, + boxShadow: 3, +} + +export default function GridAppbar() { + const router = useRouter() + const { t } = useTranslation() + + return ( + + + {t('recurring-donation:recurring-donation')} + + + + + router.push(routes.admin.recurringDonation.create)} + /> + + + + + ) +} diff --git a/src/gql/recurring-donation-status.ts b/src/gql/recurring-donation-status.ts new file mode 100644 index 000000000..8dac5c718 --- /dev/null +++ b/src/gql/recurring-donation-status.ts @@ -0,0 +1,9 @@ +export enum RecurringDonationStatus { + trialing = 'trialing', + active = 'active', + canceled = 'canceled', + incomplete = 'incomplete', + incompleteExpired = 'incompleteExpired', + pastDue = 'pastDue', + unpaid = 'unpaid', +} diff --git a/src/gql/recurring-donation.d.ts b/src/gql/recurring-donation.d.ts new file mode 100644 index 000000000..ca12d0d63 --- /dev/null +++ b/src/gql/recurring-donation.d.ts @@ -0,0 +1,26 @@ +import type { Currency } from './currency' +import { UUID } from './types' +import { RecurringDonationStatus } from './recurring-donation-status' + +export type RecurringDonationResponse = { + id: UUID + status: RecurringDonationStatus + personId: UUID + extSubscriptionId: UUID + extCustomerId: UUID + amount: number + currency: Currency + vaultId: string + createdAt: Date + updatedAt: Date | null +} + +export type RecurringDonationInput = { + status?: RecurringDonationStatus + personId?: UUID + extSubscriptionId?: UUID + extCustomerId?: UUID + amount?: number + currency?: Currency + vaultId?: string +} diff --git a/src/pages/admin/recurring-donation/index.tsx b/src/pages/admin/recurring-donation/index.tsx new file mode 100644 index 000000000..679b79c8e --- /dev/null +++ b/src/pages/admin/recurring-donation/index.tsx @@ -0,0 +1,29 @@ +import { GetServerSideProps } from 'next' +import { dehydrate, QueryClient } from 'react-query' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' + +import { keycloakInstance } from 'middleware/auth/keycloak' +// import { prefetchVaultsList } from 'common/hooks/vaults' +import RecurringDonationPage from 'components/recurring-donation/RecurringDonationPage' + +export const getServerSideProps: GetServerSideProps = async (params) => { + const client = new QueryClient() + const keycloak = keycloakInstance(params) + + // await prefetchVaultsList(client, keycloak?.token) + + return { + props: { + ...(await serverSideTranslations(params.locale ?? 'bg', [ + 'common', + 'auth', + 'recurring-donation', + 'admin', + 'validation', + ])), + dehydratedState: dehydrate(client), + }, + } +} + +export default RecurringDonationPage diff --git a/src/service/apiEndpoints.ts b/src/service/apiEndpoints.ts index 8f90fb31c..dbf8ff196 100644 --- a/src/service/apiEndpoints.ts +++ b/src/service/apiEndpoints.ts @@ -138,4 +138,15 @@ export const endpoints = { me: { url: '/account/me', method: 'GET' }, update: { url: '/account/me', method: 'PATCH' }, }, + recurringDonation: { + recurringDonation: { url: '/recurring-donation', method: 'GET' }, + getRecurringDonation: (id: string) => + { url: `/recurring-donation/${id}`, method: 'GET' }, + createRecurringDonation: { url: '/recurring-donation', method: 'POST' }, + editRecurringDonation: (id: string) => + { url: `/recurring-donation/${id}`, method: 'PUT' }, + deleteRecurringDonation: (id: string) => + { url: `/recurring-donation/${id}`, method: 'DELETE' }, + deleteRecurringDonations: { url: '/recurring-donation/deletemany', method: 'POST' }, + }, } From 226739e8619ea1d773ba08d77bfdf4ea35137f8f Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Thu, 7 Apr 2022 16:04:45 +0100 Subject: [PATCH 33/59] Added Recurring Donation Page --- src/pages/admin/recurring-donation/index.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/pages/admin/recurring-donation/index.tsx b/src/pages/admin/recurring-donation/index.tsx index 679b79c8e..052e9d4f8 100644 --- a/src/pages/admin/recurring-donation/index.tsx +++ b/src/pages/admin/recurring-donation/index.tsx @@ -3,15 +3,12 @@ import { dehydrate, QueryClient } from 'react-query' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { keycloakInstance } from 'middleware/auth/keycloak' -// import { prefetchVaultsList } from 'common/hooks/vaults' import RecurringDonationPage from 'components/recurring-donation/RecurringDonationPage' export const getServerSideProps: GetServerSideProps = async (params) => { const client = new QueryClient() const keycloak = keycloakInstance(params) - // await prefetchVaultsList(client, keycloak?.token) - return { props: { ...(await serverSideTranslations(params.locale ?? 'bg', [ From 2b9a8099826042c7b41c905229b19749a2039dfb Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Fri, 8 Apr 2022 09:35:41 +0100 Subject: [PATCH 34/59] added DetailsModal --- src/common/hooks/recurringDonation.ts | 8 +++++ .../recurring-donation/grid/DetailsModal.tsx | 29 +++++++++++++++++++ .../recurring-donation/grid/Grid.tsx | 6 ++-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/common/hooks/recurringDonation.ts b/src/common/hooks/recurringDonation.ts index 16bc6b352..c11b2bab8 100644 --- a/src/common/hooks/recurringDonation.ts +++ b/src/common/hooks/recurringDonation.ts @@ -13,3 +13,11 @@ export function useRecurringDonationList() { authQueryFnFactory(keycloak?.token), ) } + +export function useRecurringDonation(id: string) { + const { keycloak } = useKeycloak() + return useQuery( + endpoints.recurringDonation.getRecurringDonation(id).url, + authQueryFnFactory(keycloak?.token), + ) +} diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/grid/DetailsModal.tsx index e69de29bb..8e45af79d 100644 --- a/src/components/recurring-donation/grid/DetailsModal.tsx +++ b/src/components/recurring-donation/grid/DetailsModal.tsx @@ -0,0 +1,29 @@ +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 = useRecurringDonation( + selectedRecord.id, + ) + 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 +}) diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index f45301401..8489505c0 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -10,7 +10,7 @@ import { useRecurringDonationList } from 'common/hooks/recurringDonation' import GridActions from 'components/admin/GridActions' // import DeleteModal from './DeleteModal' -// import DetailsModal from './DetailsModal' +import DetailsModal from './DetailsModal' export default function Grid() { const { t } = useTranslation('recurring-donation') @@ -101,8 +101,8 @@ export default function Grid() { disableSelectionOnClick />
- {/* - */} + + {/* */} ) } From 0080ddea17f60f7bfe62a22bbb0a689ed4435c3c Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Mon, 11 Apr 2022 13:39:54 +0100 Subject: [PATCH 35/59] create page added --- .../recurring-donation/CreatePage.tsx | 21 +++ src/components/recurring-donation/Form.tsx | 173 ++++++++++++++++++ .../grid/RecurringDonationStatusSelect.tsx | 47 +++++ ...atus.ts => recurring-donation-status.d.ts} | 0 src/gql/recurring-donation.d.ts | 9 +- src/pages/admin/recurring-donation/create.tsx | 23 +++ src/service/recurringDonation.ts | 28 +++ 7 files changed, 296 insertions(+), 5 deletions(-) create mode 100644 src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx rename src/gql/{recurring-donation-status.ts => recurring-donation-status.d.ts} (100%) create mode 100644 src/pages/admin/recurring-donation/create.tsx create mode 100644 src/service/recurringDonation.ts diff --git a/src/components/recurring-donation/CreatePage.tsx b/src/components/recurring-donation/CreatePage.tsx index e69de29bb..b3cadbb5c 100644 --- a/src/components/recurring-donation/CreatePage.tsx +++ b/src/components/recurring-donation/CreatePage.tsx @@ -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 ( + + + + + + + + ) +} diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index e69de29bb..e2ceb0c63 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -0,0 +1,173 @@ +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 = useRecurringDonation(id) + + initialValues = { + status: data?.status, + personId: data?.personId, + extSubscriptionId: data?.extSubscriptionId, + extCustomerId: data?.extCustomerId, + amount: data?.amount, + currency: data?.currency, + sourceVault: data?.sourceVault, + } + } + + const mutationFn = id ? useEditRecurringDonation(id) : useCreateRecurringDonation() + + const mutation = useMutation< + AxiosResponse, + AxiosError, + 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 ( + + + + {id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')} + + + + + + {id ? ( + <> + ) : ( + <> + + + + + + + + + + + + + + + + + + + + )} + + + + + + + + + + + + ) +} diff --git a/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx b/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx new file mode 100644 index 000000000..aafe3f99d --- /dev/null +++ b/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx @@ -0,0 +1,47 @@ +import { useTranslation } from 'react-i18next' +import { FormControl, MenuItem } from '@mui/material' +import { useField } from 'formik' + +import FormTextField from 'components/common/form/FormTextField' + +export default function RecurringDonationStatusSelect({ name = 'status' }) { + const { t } = useTranslation('recurring-donation') + + enum RecurringDonationStatus { + trialing = 'trialing', + active = 'active', + canceled = 'canceled', + incomplete = 'incomplete', + incompleteExpired = 'incompleteExpired', + pastDue = 'pastDue', + unpaid = 'unpaid', + } + + const values = Object.keys(RecurringDonationStatus) + const [field, meta] = useField(name) + + return ( + + + + {t('fields.' + name)} + + {values?.map((value, index) => ( + + {value} + + ))} + + + ) +} diff --git a/src/gql/recurring-donation-status.ts b/src/gql/recurring-donation-status.d.ts similarity index 100% rename from src/gql/recurring-donation-status.ts rename to src/gql/recurring-donation-status.d.ts diff --git a/src/gql/recurring-donation.d.ts b/src/gql/recurring-donation.d.ts index ca12d0d63..733e9785f 100644 --- a/src/gql/recurring-donation.d.ts +++ b/src/gql/recurring-donation.d.ts @@ -1,6 +1,5 @@ import type { Currency } from './currency' import { UUID } from './types' -import { RecurringDonationStatus } from './recurring-donation-status' export type RecurringDonationResponse = { id: UUID @@ -10,17 +9,17 @@ export type RecurringDonationResponse = { extCustomerId: UUID amount: number currency: Currency - vaultId: string + sourceVault: UUID createdAt: Date updatedAt: Date | null } export type RecurringDonationInput = { - status?: RecurringDonationStatus + status?: RecurringDonationStatus | string personId?: UUID extSubscriptionId?: UUID extCustomerId?: UUID amount?: number - currency?: Currency - vaultId?: string + currency?: Currency | string + sourceVault?: UUID } diff --git a/src/pages/admin/recurring-donation/create.tsx b/src/pages/admin/recurring-donation/create.tsx new file mode 100644 index 000000000..8deb3f49e --- /dev/null +++ b/src/pages/admin/recurring-donation/create.tsx @@ -0,0 +1,23 @@ +import { GetServerSideProps } from 'next' +import { dehydrate, QueryClient } from 'react-query' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' + +import CreatePage from 'components/recurring-donation/CreatePage' + +export const getServerSideProps: GetServerSideProps = async ({ locale }) => { + const client = new QueryClient() + + return { + props: { + ...(await serverSideTranslations(locale ?? 'bg', [ + 'common', + 'auth', + 'recurring-donation', + 'validation', + ])), + dehydratedState: dehydrate(client), + }, + } +} + +export default CreatePage diff --git a/src/service/recurringDonation.ts b/src/service/recurringDonation.ts new file mode 100644 index 000000000..042d304bc --- /dev/null +++ b/src/service/recurringDonation.ts @@ -0,0 +1,28 @@ +import { KeycloakInstance } from 'keycloak-js' +import { useKeycloak } from '@react-keycloak/ssr' +import { AxiosResponse } from 'axios' + +import { apiClient } from 'service/apiClient' +import { authConfig } from 'service/restRequests' +import { endpoints } from 'service/apiEndpoints' +import { RecurringDonationInput, RecurringDonationResponse } from 'gql/recurring-donation' + +export function useCreateRecurringDonation() { + const { keycloak } = useKeycloak() + return async (data: RecurringDonationInput) => { + return await apiClient.post< + RecurringDonationResponse, + AxiosResponse + >(endpoints.recurringDonation.createRecurringDonation.url, data, authConfig(keycloak?.token)) + } +} + +export function useEditRecurringDonation(id: string) { + const { keycloak } = useKeycloak() + return async (data: RecurringDonationInput) => { + return await apiClient.patch< + RecurringDonationResponse, + AxiosResponse + >(endpoints.recurringDonation.editRecurringDonation(id).url, data, authConfig(keycloak?.token)) + } +} From bf883bf0fa59a9465df24bc670ccbe77cbd2c87b Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Tue, 12 Apr 2022 10:40:55 +0100 Subject: [PATCH 36/59] added EditPage --- src/common/hooks/recurringDonation.ts | 11 +++ .../recurring-donation/EditPage.tsx | 21 ++++++ src/components/recurring-donation/Form.tsx | 72 +++++++++---------- .../recurring-donation/grid/Grid.tsx | 10 ++- .../admin/recurring-donation/[id]/index.tsx | 33 +++++++++ 5 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 src/pages/admin/recurring-donation/[id]/index.tsx diff --git a/src/common/hooks/recurringDonation.ts b/src/common/hooks/recurringDonation.ts index c11b2bab8..280178adf 100644 --- a/src/common/hooks/recurringDonation.ts +++ b/src/common/hooks/recurringDonation.ts @@ -21,3 +21,14 @@ export function useRecurringDonation(id: string) { authQueryFnFactory(keycloak?.token), ) } + +export async function prefetchRecurringDonationById( + client: QueryClient, + id: string, + token?: string, +) { + await client.prefetchQuery( + endpoints.recurringDonation.getRecurringDonation(id).url, + authQueryFnFactory(token), + ) +} diff --git a/src/components/recurring-donation/EditPage.tsx b/src/components/recurring-donation/EditPage.tsx index e69de29bb..8d0152bf0 100644 --- a/src/components/recurring-donation/EditPage.tsx +++ b/src/components/recurring-donation/EditPage.tsx @@ -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 ( + + + + + + + + ) +} diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index e2ceb0c63..3cfa9fbdf 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -118,46 +118,38 @@ export default function EditForm() { - {id ? ( - <> - ) : ( - <> - - - - - - - - - - - - - - - - - - - - )} + + + + + + + + + + + + + + + + + + + + {id ? <> : <>} diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index 8489505c0..eb72596a8 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -16,7 +16,7 @@ export default function Grid() { const { t } = useTranslation('recurring-donation') const { data }: UseQueryResult = useRecurringDonationList() const [pageSize, setPageSize] = useState(5) - + console.log(data) const commonProps: Partial = { align: 'left', width: 150, @@ -46,7 +46,7 @@ export default function Grid() { field: 'extSubscriptionId', headerName: t('extSubscriptionId'), ...commonProps, - width: 400, + width: 300, }, { field: 'extCustomerId', @@ -54,6 +54,12 @@ export default function Grid() { ...commonProps, width: 300, }, + { + field: 'personId', + headerName: t('personId'), + ...commonProps, + width: 300, + }, { field: 'vaultId', headerName: t('vaultId'), diff --git a/src/pages/admin/recurring-donation/[id]/index.tsx b/src/pages/admin/recurring-donation/[id]/index.tsx new file mode 100644 index 000000000..7f0df9a5f --- /dev/null +++ b/src/pages/admin/recurring-donation/[id]/index.tsx @@ -0,0 +1,33 @@ +import { GetServerSideProps } from 'next' +import { dehydrate, QueryClient } from 'react-query' +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' + +import { keycloakInstance } from 'middleware/auth/keycloak' +import { endpoints } from 'service/apiEndpoints' +import { authQueryFnFactory } from 'service/restRequests' +import EditPage from 'components/recurring-donation/EditPage' + +export const getServerSideProps: GetServerSideProps = async (params) => { + const client = new QueryClient() + const keycloak = keycloakInstance(params) + const { id } = params.query + + await client.prefetchQuery( + endpoints.recurringDonation.editRecurringDonation(`${id}`).url, + authQueryFnFactory(keycloak.token), + ) + + return { + props: { + ...(await serverSideTranslations(params.locale ?? 'bg', [ + 'common', + 'auth', + 'validation', + 'recurring-donation', + ])), + dehydratedState: dehydrate(client), + }, + } +} + +export default EditPage From 7d6a44b21b5af5e6fb52f803ce3aa515273bd64b Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Tue, 12 Apr 2022 10:55:11 +0100 Subject: [PATCH 37/59] apiEndpoinds --- src/service/apiEndpoints.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/service/apiEndpoints.ts b/src/service/apiEndpoints.ts index dbf8ff196..d485d725a 100644 --- a/src/service/apiEndpoints.ts +++ b/src/service/apiEndpoints.ts @@ -149,4 +149,8 @@ export const endpoints = { { url: `/recurring-donation/${id}`, method: 'DELETE' }, deleteRecurringDonations: { url: '/recurring-donation/deletemany', method: 'POST' }, }, + account: { + me: { url: '/account/me', method: 'GET' }, + update: { url: '/account/me', method: 'PATCH' }, + }, } From bdc1495bdab542fa559fe146cd8f31bde67ff4d3 Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Tue, 12 Apr 2022 11:36:01 +0100 Subject: [PATCH 38/59] --- .../recurring-donation/grid/DeleteModal.tsx | 41 +++++++++++++++++++ .../recurring-donation/grid/Grid.tsx | 6 +-- src/service/recurringDonation.ts | 10 +++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/grid/DeleteModal.tsx index e69de29bb..f00773369 100644 --- a/src/components/recurring-donation/grid/DeleteModal.tsx +++ b/src/components/recurring-donation/grid/DeleteModal.tsx @@ -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, + AxiosError, + 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 +}) diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index eb72596a8..ab8564df9 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -9,14 +9,14 @@ import { RecurringDonationResponse } from 'gql/recurring-donation' import { useRecurringDonationList } from 'common/hooks/recurringDonation' import GridActions from 'components/admin/GridActions' -// import DeleteModal from './DeleteModal' +import DeleteModal from './DeleteModal' import DetailsModal from './DetailsModal' export default function Grid() { const { t } = useTranslation('recurring-donation') const { data }: UseQueryResult = useRecurringDonationList() const [pageSize, setPageSize] = useState(5) - console.log(data) + const commonProps: Partial = { align: 'left', width: 150, @@ -108,7 +108,7 @@ export default function Grid() { />
- {/* */} + ) } diff --git a/src/service/recurringDonation.ts b/src/service/recurringDonation.ts index 042d304bc..2f55c9df2 100644 --- a/src/service/recurringDonation.ts +++ b/src/service/recurringDonation.ts @@ -26,3 +26,13 @@ export function useEditRecurringDonation(id: string) { >(endpoints.recurringDonation.editRecurringDonation(id).url, data, authConfig(keycloak?.token)) } } + +export function useDeleteRecurringDonation(id: string) { + const { keycloak } = useKeycloak() + return async () => { + return await apiClient.delete< + RecurringDonationResponse, + AxiosResponse + >(endpoints.recurringDonation.deleteRecurringDonation(id).url, authConfig(keycloak?.token)) + } +} From 4eca962cb8ee0d0cf88a45e45f0cb8f2a32e76f6 Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Thu, 14 Apr 2022 10:52:00 +0100 Subject: [PATCH 39/59] added useTranslation --- public/locales/bg/recurring-donation.json | 31 +++++++++++++++++++ .../recurring-donation/grid/DetailsModal.tsx | 1 + .../recurring-donation/grid/Grid.tsx | 2 +- .../recurring-donation/grid/GridAppbar.tsx | 2 +- .../grid/RecurringDonationStatusSelect.tsx | 10 ++---- 5 files changed, 36 insertions(+), 10 deletions(-) create mode 100644 public/locales/bg/recurring-donation.json diff --git a/public/locales/bg/recurring-donation.json b/public/locales/bg/recurring-donation.json new file mode 100644 index 000000000..53a2eab02 --- /dev/null +++ b/public/locales/bg/recurring-donation.json @@ -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": "Изпрати" + } +} \ No newline at end of file diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/grid/DetailsModal.tsx index 8e45af79d..35654d560 100644 --- a/src/components/recurring-donation/grid/DetailsModal.tsx +++ b/src/components/recurring-donation/grid/DetailsModal.tsx @@ -13,6 +13,7 @@ export default observer(function DetailsModal() { const { data }: UseQueryResult = useRecurringDonation( selectedRecord.id, ) + console.log(data) const { t } = useTranslation('recurring-donation') const dataConverted = [ diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index ab8564df9..0648dfa98 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -26,7 +26,7 @@ export default function Grid() { const columns: GridColumns = [ { field: 'status', - headerName: t('status'), + headerName: t('recurring-donation:status'), flex: 1.5, ...commonProps, }, diff --git a/src/components/recurring-donation/grid/GridAppbar.tsx b/src/components/recurring-donation/grid/GridAppbar.tsx index bc5f856be..a897fbf90 100644 --- a/src/components/recurring-donation/grid/GridAppbar.tsx +++ b/src/components/recurring-donation/grid/GridAppbar.tsx @@ -27,7 +27,7 @@ export default function GridAppbar() { height: '72px', }}> - {t('recurring-donation:recurring-donation')} + {t('recurring-donation:recurring-donations')} diff --git a/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx b/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx index aafe3f99d..ef288dcb1 100644 --- a/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx +++ b/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx @@ -26,15 +26,9 @@ export default function RecurringDonationStatusSelect({ name = 'status' }) { size="small" variant="outlined" error={Boolean(meta.error) && Boolean(meta.touched)}> - + - {t('fields.' + name)} + {t(name)} {values?.map((value, index) => ( From 6b010f290563eace03cbdc4487ee5014f7f226ef Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Thu, 14 Apr 2022 11:33:42 +0100 Subject: [PATCH 40/59] change createForm usetranslation --- src/components/recurring-donation/Form.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index 3cfa9fbdf..59a57af09 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -142,11 +142,7 @@ export default function EditForm() {
- + {id ? <> : <>} From e88e54f5a0940b92b2915d16dc8a6273285ec34b Mon Sep 17 00:00:00 2001 From: Marina-yoya <64007447+Marina-yoya@users.noreply.github.com> Date: Fri, 15 Apr 2022 10:18:40 +0100 Subject: [PATCH 41/59] fixing EditPage --- src/components/recurring-donation/EditPage.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/recurring-donation/EditPage.tsx b/src/components/recurring-donation/EditPage.tsx index 8d0152bf0..fc7df1cc6 100644 --- a/src/components/recurring-donation/EditPage.tsx +++ b/src/components/recurring-donation/EditPage.tsx @@ -1,19 +1,27 @@ import { useTranslation } from 'next-i18next' +import { useRouter } from 'next/router' import { Container } from '@mui/material' +import { UseQueryResult } from 'react-query' import AdminContainer from 'components/admin/navigation/AdminContainer' import AdminLayout from 'components/admin/navigation/AdminLayout' - +import NotFoundIllustration from 'components/errors/assets/NotFoundIllustration' +import { useRecurringDonation } from 'common/hooks/recurringDonation' +import { RecurringDonationResponse } from 'gql/recurring-donation' import Form from './Form' export default function EditPage() { const { t } = useTranslation('recurring-donation') + const { query } = useRouter() + const { data: donation }: UseQueryResult = useRecurringDonation( + String(query.id), + ) return ( - + {donation ? : } From 17e242189f2830073f202208841a416594ca29fc Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Sun, 24 Apr 2022 20:39:23 +0200 Subject: [PATCH 42/59] Add person autocomplete component and start base dialog --- src/common/hooks/confirm.ts | 44 +++++++++++++ src/common/hooks/person.ts | 5 +- src/components/person/PersonAutocomplete.tsx | 47 ++++++++++++++ src/components/person/PersonSelectDialog.tsx | 67 ++++++++++++++++++++ src/components/recurring-donation/Form.tsx | 8 ++- 5 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 src/common/hooks/confirm.ts create mode 100644 src/components/person/PersonAutocomplete.tsx create mode 100644 src/components/person/PersonSelectDialog.tsx diff --git a/src/common/hooks/confirm.ts b/src/common/hooks/confirm.ts new file mode 100644 index 000000000..9ed3c8bc0 --- /dev/null +++ b/src/common/hooks/confirm.ts @@ -0,0 +1,44 @@ +import { useState } from 'react' + +export type ConfirmProps = { + onConfirm?: () => void | Promise + onClose?: () => void | Promise +} +export type ConfirmHookProps = { + open: boolean + loading: boolean + openHandler: () => void + confirmHandler: () => void + closeHandler: () => void +} + +const useConfirm = ({ onConfirm, onClose }: ConfirmProps): ConfirmHookProps => { + const [open, setOpen] = useState(false) + const [loading, setLoading] = useState(false) + + return { + open, + loading, + openHandler: () => { + setOpen(true) + }, + confirmHandler: async () => { + setOpen(false) + if (typeof onConfirm === 'function') { + setLoading(true) + await onConfirm() + setLoading(false) + } + }, + closeHandler: async () => { + setOpen(false) + if (typeof onClose === 'function') { + setLoading(true) + await onClose() + setLoading(false) + } + }, + } +} + +export default useConfirm diff --git a/src/common/hooks/person.ts b/src/common/hooks/person.ts index d65a5b198..15d91f7bc 100644 --- a/src/common/hooks/person.ts +++ b/src/common/hooks/person.ts @@ -1,14 +1,15 @@ import { useKeycloak } from '@react-keycloak/ssr' import { PersonResponse } from 'gql/person' import { KeycloakInstance } from 'keycloak-js' -import { useQuery } from 'react-query' +import { useQuery, UseQueryOptions } from 'react-query' import { endpoints } from 'service/apiEndpoints' import { authQueryFnFactory } from 'service/restRequests' -export const usePersonList = () => { +export const usePersonList = ({ options }: { options: UseQueryOptions }) => { const { keycloak } = useKeycloak() return useQuery( endpoints.person.list.url, authQueryFnFactory(keycloak?.token), + options, ) } diff --git a/src/components/person/PersonAutocomplete.tsx b/src/components/person/PersonAutocomplete.tsx new file mode 100644 index 000000000..bc0c75023 --- /dev/null +++ b/src/components/person/PersonAutocomplete.tsx @@ -0,0 +1,47 @@ +import { useTranslation } from 'react-i18next' +import { Autocomplete, AutocompleteProps, TextField } from '@mui/material' +import { PersonResponse } from 'gql/person' +import { usePersonList } from 'common/hooks/person' + +export type PersonAutocompleteProps = { + onSelect: (person: PersonResponse | null) => void + autocompleteProps?: Omit< + AutocompleteProps, + 'renderInput' | 'options' | 'getOptionLabel' | 'onChange' | 'loading' + > +} +export default function PersonAutocomplete({ + onSelect, + autocompleteProps, +}: PersonAutocompleteProps) { + const { t } = useTranslation('common') + const { + data: personList, + isLoading, + refetch, + } = usePersonList({ + options: { + enabled: false, + refetchOnWindowFocus: false, + }, + }) + return ( + option.firstName === value.firstName} + options={personList || []} + getOptionLabel={(person) => person.firstName + ' ' + person.lastName} + onChange={(e, person) => { + console.log(person) + onSelect(person) + }} + onOpen={() => { + refetch() + }} + loading={isLoading} + renderInput={(params) => ( + + )} + {...autocompleteProps} + /> + ) +} diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx new file mode 100644 index 000000000..2647d5087 --- /dev/null +++ b/src/components/person/PersonSelectDialog.tsx @@ -0,0 +1,67 @@ +import { Close } from '@mui/icons-material' +import { LoadingButton } from '@mui/lab' +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, + IconButton, +} from '@mui/material' +import useConfirm from 'common/hooks/confirm' +import CloseModalButton from 'components/common/CloseModalButton' +import { PersonResponse } from 'gql/person' +import React, { useState } from 'react' +import PersonAutocomplete from './PersonAutocomplete' + +type Props = { + onConfirm: (person: PersonResponse) => void +} + +function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { + const [person, setPerson] = useState(null) + const { open, confirmHandler, closeHandler, openHandler, loading } = useConfirm({ + onConfirm: async () => { + confirmCallback(person as PersonResponse) + }, + onClose: async () => { + null + }, + }) + return ( + <> + + + + Person Select + {/* theme.palette.grey[500], + }}> + + */} + + + { + setPerson(person) + }} + /> + + + + + Confirm + + + + + ) +} + +export default PersonSelectDialog diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index 59a57af09..2d71d8415 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -21,6 +21,7 @@ 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' +import PersonSelectDialog from 'components/person/PersonSelectDialog' export enum RecurringDonationStatus { trialing = 'trialing', @@ -119,7 +120,12 @@ export default function EditForm() {
- + { + console.log(person) + }} + /> + {/* */} Date: Sun, 24 Apr 2022 21:14:13 +0200 Subject: [PATCH 43/59] Show person and cleanup --- src/components/person/PersonSelectDialog.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 2647d5087..626656cd0 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -1,12 +1,12 @@ -import { Close } from '@mui/icons-material' import { LoadingButton } from '@mui/lab' import { + Box, Button, Dialog, DialogActions, DialogContent, DialogTitle, - IconButton, + Typography, } from '@mui/material' import useConfirm from 'common/hooks/confirm' import CloseModalButton from 'components/common/CloseModalButton' @@ -30,7 +30,12 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { }) return ( <> - + + {person ? person.firstName : 'No personSelected'} + + Person Select From 9304343a06e78eb61e689eefdc5485cd30c8a970 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Mon, 25 Apr 2022 22:15:34 +0200 Subject: [PATCH 44/59] Dialog full width and showId props on autocomplete --- src/components/person/PersonAutocomplete.tsx | 17 +++++++-- src/components/person/PersonSelectDialog.tsx | 37 ++++++++++---------- src/components/recurring-donation/Form.tsx | 9 +++-- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/components/person/PersonAutocomplete.tsx b/src/components/person/PersonAutocomplete.tsx index bc0c75023..653f2ffce 100644 --- a/src/components/person/PersonAutocomplete.tsx +++ b/src/components/person/PersonAutocomplete.tsx @@ -9,9 +9,11 @@ export type PersonAutocompleteProps = { AutocompleteProps, 'renderInput' | 'options' | 'getOptionLabel' | 'onChange' | 'loading' > + showId: boolean } export default function PersonAutocomplete({ onSelect, + showId, autocompleteProps, }: PersonAutocompleteProps) { const { t } = useTranslation('common') @@ -29,9 +31,12 @@ export default function PersonAutocomplete({ option.firstName === value.firstName} options={personList || []} - getOptionLabel={(person) => person.firstName + ' ' + person.lastName} + getOptionLabel={(person) => + showId + ? `${person.firstName} ${person.lastName} (${person.id})` + : person.firstName + ' ' + person.lastName + } onChange={(e, person) => { - console.log(person) onSelect(person) }} onOpen={() => { @@ -39,7 +44,13 @@ export default function PersonAutocomplete({ }} loading={isLoading} renderInput={(params) => ( - + )} {...autocompleteProps} /> diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 626656cd0..7ba8f4589 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -6,9 +6,11 @@ import { DialogActions, DialogContent, DialogTitle, + Grid, Typography, } from '@mui/material' import useConfirm from 'common/hooks/confirm' +import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' import { PersonResponse } from 'gql/person' import React, { useState } from 'react' @@ -31,35 +33,34 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { return ( <> - {person ? person.firstName : 'No personSelected'} - - - - Person Select - {/* theme.palette.grey[500], - }}> - - */} - + + Person Select { setPerson(person) }} + showId /> + {person ? ( + + ID: {person.id} + + ) : ( + "You haven't selected a person" + )} - + Confirm diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index 2d71d8415..f3f65ec97 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -116,16 +116,15 @@ export default function EditForm() { {id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')}
- - - - + { console.log(person) }} /> - {/* */} + + + Date: Mon, 25 Apr 2022 22:34:19 +0200 Subject: [PATCH 45/59] Add person info component --- src/components/person/PersonInfo.tsx | 38 ++++++++++++++++++++ src/components/person/PersonSelectDialog.tsx | 11 +++--- src/gql/person.d.ts | 7 ++++ 3 files changed, 49 insertions(+), 7 deletions(-) create mode 100644 src/components/person/PersonInfo.tsx diff --git a/src/components/person/PersonInfo.tsx b/src/components/person/PersonInfo.tsx new file mode 100644 index 000000000..7634aa493 --- /dev/null +++ b/src/components/person/PersonInfo.tsx @@ -0,0 +1,38 @@ +import { Box, Grid, Typography } from '@mui/material' +import theme from 'common/theme' +import { format, parseISO } from 'date-fns' +import { PersonResponse } from 'gql/person' +import React from 'react' + +type Props = { + person: PersonResponse +} + +function PersonInfo({ person }: Props) { + return ( + + + + Contact information: + + + Email: {person.email} + Tel: {person.phone} + Tel: {person.address} + + + + + General information: + + + Created at: {format(parseISO(person.createdAt), 'PPpp')} + Company: {person.company} + Confirmed email: {person.emailConfirmed ? 'Yes' : 'No'} + + + + ) +} + +export default PersonInfo diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 7ba8f4589..09a4bb0f5 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -15,6 +15,7 @@ import CloseModalButton from 'components/common/CloseModalButton' import { PersonResponse } from 'gql/person' import React, { useState } from 'react' import PersonAutocomplete from './PersonAutocomplete' +import PersonInfo from './PersonInfo' type Props = { onConfirm: (person: PersonResponse) => void @@ -51,13 +52,9 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { }} showId /> - {person ? ( - - ID: {person.id} - - ) : ( - "You haven't selected a person" - )} + + {person ? : "You haven't selected a person"} + diff --git a/src/gql/person.d.ts b/src/gql/person.d.ts index 6aa8c7a49..46972448e 100644 --- a/src/gql/person.d.ts +++ b/src/gql/person.d.ts @@ -5,6 +5,13 @@ export type PersonResponse = { personId: string firstName: string lastName: string + email: string + phone: string + address: string + company: string + createdAt: string + newsletter: boolean + emailConfirmed: boolean } export type PersonFormData = { From 3118a9334b41c459a5d31fe70a57ff2e296684df Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Mon, 25 Apr 2022 22:40:33 +0200 Subject: [PATCH 46/59] Styled person info --- src/components/person/PersonInfo.tsx | 29 ++++++++++++++++---- src/components/person/PersonSelectDialog.tsx | 1 - 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/components/person/PersonInfo.tsx b/src/components/person/PersonInfo.tsx index 7634aa493..cc93b12c3 100644 --- a/src/components/person/PersonInfo.tsx +++ b/src/components/person/PersonInfo.tsx @@ -1,4 +1,6 @@ -import { Box, Grid, Typography } from '@mui/material' +import { Box, Grid, Theme, Typography } from '@mui/material' +import createStyles from '@mui/styles/createStyles' +import makeStyles from '@mui/styles/makeStyles' import theme from 'common/theme' import { format, parseISO } from 'date-fns' import { PersonResponse } from 'gql/person' @@ -8,24 +10,39 @@ type Props = { person: PersonResponse } +const useStyles = makeStyles((theme: Theme) => + createStyles({ + infoHeading: { + marginBottom: theme.spacing(2), + marginTop: theme.spacing(2), + }, + infoWrapper: { + '&>*': { + marginBottom: theme.spacing(1), + }, + }, + }), +) + function PersonInfo({ person }: Props) { + const classes = useStyles() return ( - + Contact information: - + Email: {person.email} Tel: {person.phone} - Tel: {person.address} + Adress: {person.address} - + General information: - + Created at: {format(parseISO(person.createdAt), 'PPpp')} Company: {person.company} Confirmed email: {person.emailConfirmed ? 'Yes' : 'No'} diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 09a4bb0f5..531f373dc 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -6,7 +6,6 @@ import { DialogActions, DialogContent, DialogTitle, - Grid, Typography, } from '@mui/material' import useConfirm from 'common/hooks/confirm' From 4d327a6c4e8ca25e45f1e60cf551e80e7631840a Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 20:28:14 +0200 Subject: [PATCH 47/59] Add select person styled box --- public/locales/bg/person.json | 11 ++++ public/locales/en/person.json | 11 ++++ src/components/person/PersonAutocomplete.tsx | 4 +- src/components/person/PersonSelectDialog.tsx | 56 +++++++++++++------ src/pages/admin/recurring-donation/create.tsx | 1 + 5 files changed, 63 insertions(+), 20 deletions(-) create mode 100644 public/locales/bg/person.json create mode 100644 public/locales/en/person.json diff --git a/public/locales/bg/person.json b/public/locales/bg/person.json new file mode 100644 index 000000000..bd1e07340 --- /dev/null +++ b/public/locales/bg/person.json @@ -0,0 +1,11 @@ +{ + "selectDialog": { + "notSelected": "Не сте избрали човек", + "select": "Избиране", + "personSelect": "Изберете човек", + "confirm": "Потвърждавам" + }, + "autocomplete": { + "personSearch": "Намерете човек" + } +} diff --git a/public/locales/en/person.json b/public/locales/en/person.json new file mode 100644 index 000000000..276e47fe2 --- /dev/null +++ b/public/locales/en/person.json @@ -0,0 +1,11 @@ +{ + "selectDialog": { + "notSelected": "You haven't chosen a person", + "select": "Select", + "personSelect": "Person Select", + "confirm": "Confirm" + }, + "autocomplete": { + "personSearch": "Find a person" + } +} diff --git a/src/components/person/PersonAutocomplete.tsx b/src/components/person/PersonAutocomplete.tsx index 653f2ffce..cf85ee102 100644 --- a/src/components/person/PersonAutocomplete.tsx +++ b/src/components/person/PersonAutocomplete.tsx @@ -16,7 +16,7 @@ export default function PersonAutocomplete({ showId, autocompleteProps, }: PersonAutocompleteProps) { - const { t } = useTranslation('common') + const { t } = useTranslation('person') const { data: personList, isLoading, @@ -49,7 +49,7 @@ export default function PersonAutocomplete({ type="text" fullWidth defaultValue="" - label={t('donation:recurring.personSearch')} + label={t('person:autocomplete.personSearch')} /> )} {...autocompleteProps} diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 531f373dc..2b077e95b 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -6,12 +6,16 @@ import { DialogActions, DialogContent, DialogTitle, + InputAdornment, + TextField, Typography, } from '@mui/material' +import { makeStyles } from '@mui/styles' import useConfirm from 'common/hooks/confirm' import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' import { PersonResponse } from 'gql/person' +import { useTranslation } from 'next-i18next' import React, { useState } from 'react' import PersonAutocomplete from './PersonAutocomplete' import PersonInfo from './PersonInfo' @@ -20,8 +24,21 @@ type Props = { onConfirm: (person: PersonResponse) => void } +const useStyles = makeStyles({ + imitateInputBox: { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + border: `1px solid rgba(0, 0, 0, 0.23)`, + borderRadius: '3px', + padding: '8.5px 14px', + cursor: 'pointer', + }, +}) function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { const [person, setPerson] = useState(null) + const { t } = useTranslation('person') + const classes = useStyles() const { open, confirmHandler, closeHandler, openHandler, loading } = useConfirm({ onConfirm: async () => { confirmCallback(person as PersonResponse) @@ -32,33 +49,36 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { }) return ( <> - - {person ? person.firstName : 'You need to select a person'} - - Person Select + {t('person:selectDialog.personSelect')} - { - setPerson(person) - }} - showId - /> - - {person ? : "You haven't selected a person"} + + { + setPerson(person) + }} + showId + /> + + + + {person ? : t('person:selectDialog.notSelected')} - Confirm + {t('person:selectDialog.confirm')} diff --git a/src/pages/admin/recurring-donation/create.tsx b/src/pages/admin/recurring-donation/create.tsx index 8deb3f49e..8285ad24d 100644 --- a/src/pages/admin/recurring-donation/create.tsx +++ b/src/pages/admin/recurring-donation/create.tsx @@ -14,6 +14,7 @@ export const getServerSideProps: GetServerSideProps = async ({ locale }) => { 'auth', 'recurring-donation', 'validation', + 'person', ])), dehydratedState: dehydrate(client), }, From f709f935d99575b3a7fc3b63e98cfe0c3eec4c96 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 21:35:59 +0200 Subject: [PATCH 48/59] Add validation --- src/components/person/PersonSelectDialog.tsx | 37 ++++-- src/components/recurring-donation/Form.tsx | 113 ++++++++++--------- 2 files changed, 92 insertions(+), 58 deletions(-) diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index 2b077e95b..cf48dd8c1 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -6,11 +6,10 @@ import { DialogActions, DialogContent, DialogTitle, - InputAdornment, - TextField, Typography, } from '@mui/material' import { makeStyles } from '@mui/styles' +import { translateError } from 'common/form/validation' import useConfirm from 'common/hooks/confirm' import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' @@ -21,7 +20,9 @@ import PersonAutocomplete from './PersonAutocomplete' import PersonInfo from './PersonInfo' type Props = { - onConfirm: (person: PersonResponse) => void + onConfirm?: (person: PersonResponse | null) => void + onClose?: (person: PersonResponse | null) => void + error?: string } const useStyles = makeStyles({ @@ -34,22 +35,43 @@ const useStyles = makeStyles({ padding: '8.5px 14px', cursor: 'pointer', }, + errorInputBox: { + borderColor: '#d32f2f', + color: '#d32f2f', + }, + errorText: { + color: '#d32f2f', + fontWeight: 400, + fontSize: '0.75rem', + lineHeight: 1.66, + letterSpacing: '0.03333em', + textAlign: 'left', + marginTop: '4px', + marginRight: '14px', + marginBottom: 0, + marginLeft: '14px', + }, }) -function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { +function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback, error }: Props) { const [person, setPerson] = useState(null) const { t } = useTranslation('person') const classes = useStyles() const { open, confirmHandler, closeHandler, openHandler, loading } = useConfirm({ onConfirm: async () => { - confirmCallback(person as PersonResponse) + confirmCallback ? confirmCallback(person) : null }, onClose: async () => { - null + closeCallback ? closeCallback(person) : null }, }) + return ( <> - + {person ? `${person.firstName} ${person.lastName} (${person.id})` @@ -59,6 +81,7 @@ function PersonSelectDialog({ onConfirm: confirmCallback }: Props) { {t('person:selectDialog.select')} + {error ?

{translateError(error, t)}

: null} {t('person:selectDialog.personSelect')} diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index f3f65ec97..5ceffcfd5 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -22,6 +22,7 @@ import SubmitButton from 'components/common/form/SubmitButton' import CurrencySelect from 'components/currency/CurrencySelect' import RecurringDonationStatusSelect from './grid/RecurringDonationStatusSelect' import PersonSelectDialog from 'components/person/PersonSelectDialog' +import { Formik } from 'formik' export enum RecurringDonationStatus { trialing = 'trialing', @@ -53,7 +54,6 @@ export default function EditForm() { const router = useRouter() const queryClient = useQueryClient() const { t } = useTranslation() - let id = router.query.id let initialValues: RecurringDonationInput = { @@ -107,60 +107,71 @@ export default function EditForm() { } return ( - - - - {id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')} - - - - { - console.log(person) - }} - /> - - - - - - - - - - - - - - - - - - - + {({ errors, setFieldTouched, setFieldValue }) => ( + + + {id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')} + + + + { + person ? setFieldValue('personId', person.id) : setFieldTouched('personId') + }} + onClose={(person) => { + person ? setFieldValue('personId', person.id) : setFieldTouched('personId') + }} + /> + + + + + + + + + + + + + + + + + + + - {id ? <> : <>} - - - - - - - + {id ? <> : <>} + + + + + + + + - - - +
+ )} + ) } From ee491ff55ecee8b25deeadcb7aa2c6a8bf0b6edb Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 21:54:34 +0200 Subject: [PATCH 49/59] Extract FormFieldButton as a component --- src/components/common/FormFieldButton.tsx | 62 ++++++++++++++++++++ src/components/person/PersonSelectDialog.tsx | 62 +++----------------- 2 files changed, 71 insertions(+), 53 deletions(-) create mode 100644 src/components/common/FormFieldButton.tsx diff --git a/src/components/common/FormFieldButton.tsx b/src/components/common/FormFieldButton.tsx new file mode 100644 index 000000000..2d2ffd607 --- /dev/null +++ b/src/components/common/FormFieldButton.tsx @@ -0,0 +1,62 @@ +import React from 'react' +import { Button, Typography, Box } from '@mui/material' +import makeStyles from '@mui/styles/makeStyles' + +type Props = { + error?: string + onClick?: () => void + placeholder?: string + value?: string + button?: { label: string } +} + +const useStyles = makeStyles({ + imitateInputBox: { + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + border: `1px solid rgba(0, 0, 0, 0.23)`, + borderRadius: '3px', + padding: '8.5px 14px', + cursor: 'pointer', + }, + errorInputBox: { + borderColor: '#d32f2f', + color: '#d32f2f', + }, + errorText: { + color: '#d32f2f', + fontWeight: 400, + fontSize: '0.75rem', + lineHeight: 1.66, + letterSpacing: '0.03333em', + textAlign: 'left', + marginTop: '4px', + marginRight: '14px', + marginBottom: 0, + marginLeft: '14px', + }, +}) + +function FormFieldButton({ error, onClick, value, placeholder, button }: Props) { + const classes = useStyles() + return ( + <> + + {value || placeholder} + {button ? ( + + ) : null} + + {error ?

{error}

: null} + + ) +} + +export default FormFieldButton diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index cf48dd8c1..c10a095fc 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -1,18 +1,10 @@ import { LoadingButton } from '@mui/lab' -import { - Box, - Button, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - Typography, -} from '@mui/material' -import { makeStyles } from '@mui/styles' +import { Box, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material' import { translateError } from 'common/form/validation' import useConfirm from 'common/hooks/confirm' import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' +import FormFieldButton from 'components/common/FormFieldButton' import { PersonResponse } from 'gql/person' import { useTranslation } from 'next-i18next' import React, { useState } from 'react' @@ -25,37 +17,9 @@ type Props = { error?: string } -const useStyles = makeStyles({ - imitateInputBox: { - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - border: `1px solid rgba(0, 0, 0, 0.23)`, - borderRadius: '3px', - padding: '8.5px 14px', - cursor: 'pointer', - }, - errorInputBox: { - borderColor: '#d32f2f', - color: '#d32f2f', - }, - errorText: { - color: '#d32f2f', - fontWeight: 400, - fontSize: '0.75rem', - lineHeight: 1.66, - letterSpacing: '0.03333em', - textAlign: 'left', - marginTop: '4px', - marginRight: '14px', - marginBottom: 0, - marginLeft: '14px', - }, -}) function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback, error }: Props) { const [person, setPerson] = useState(null) - const { t } = useTranslation('person') - const classes = useStyles() + const { t } = useTranslation() const { open, confirmHandler, closeHandler, openHandler, loading } = useConfirm({ onConfirm: async () => { confirmCallback ? confirmCallback(person) : null @@ -67,21 +31,13 @@ function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback return ( <> - - - {person - ? `${person.firstName} ${person.lastName} (${person.id})` - : t('person:selectDialog.notSelected')} - - - - {error ?

{translateError(error, t)}

: null} + placeholder={t('person:selectDialog.notSelected')} + value={person ? `${person.firstName} ${person.lastName} (${person.id})` : undefined} + button={{ label: t('person:selectDialog.select') }} + error={error ? translateError(error, t) : undefined} + /> {t('person:selectDialog.personSelect')} From 0b31bbae1a8d89150eb3aa2c56b9611678d476e8 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 22:04:16 +0200 Subject: [PATCH 50/59] Add translations for person info component --- public/locales/bg/person.json | 10 ++++++++++ public/locales/en/person.json | 10 ++++++++++ src/components/person/PersonInfo.tsx | 30 ++++++++++++++++++++-------- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/public/locales/bg/person.json b/public/locales/bg/person.json index bd1e07340..007f129bf 100644 --- a/public/locales/bg/person.json +++ b/public/locales/bg/person.json @@ -7,5 +7,15 @@ }, "autocomplete": { "personSearch": "Намерете човек" + }, + "info": { + "tel": "Тел", + "contact": "Контакти", + "address": "Адрес", + "email": "Имейл", + "general": "Генерална информация", + "createdAt": "Създаден в", + "company": "Компания", + "confirmedEmail": "Потвърден имейл" } } diff --git a/public/locales/en/person.json b/public/locales/en/person.json index 276e47fe2..43cbdcbe2 100644 --- a/public/locales/en/person.json +++ b/public/locales/en/person.json @@ -7,5 +7,15 @@ }, "autocomplete": { "personSearch": "Find a person" + }, + "info": { + "tel": "Тел", + "contact": "Contact info", + "address": "Address", + "email": "Email", + "general": "General information", + "createdAt": "Created at", + "company": "Company", + "confirmedEmail": "Confirmed email" } } diff --git a/src/components/person/PersonInfo.tsx b/src/components/person/PersonInfo.tsx index cc93b12c3..ec921006f 100644 --- a/src/components/person/PersonInfo.tsx +++ b/src/components/person/PersonInfo.tsx @@ -4,6 +4,7 @@ import makeStyles from '@mui/styles/makeStyles' import theme from 'common/theme' import { format, parseISO } from 'date-fns' import { PersonResponse } from 'gql/person' +import { useTranslation } from 'next-i18next' import React from 'react' type Props = { @@ -26,26 +27,39 @@ const useStyles = makeStyles((theme: Theme) => function PersonInfo({ person }: Props) { const classes = useStyles() + const { t } = useTranslation() return ( - Contact information: + {t('person:info.contact')} - Email: {person.email} - Tel: {person.phone} - Adress: {person.address} + + {t('person:info.email')}: {person.email} + + + {t('person:info.tel')}: {person.phone} + + + {t('person:info.address')}: {person.address} + - General information: + {t('person:info.general')} - Created at: {format(parseISO(person.createdAt), 'PPpp')} - Company: {person.company} - Confirmed email: {person.emailConfirmed ? 'Yes' : 'No'} + + {t('person:info.createdAt')}: {format(parseISO(person.createdAt), 'PPpp')} + + + {t('person:info.company')}: {person.company} + + + {t('person:info.confirmedEmail')}: {person.emailConfirmed ? 'Yes' : 'No'} + From 2a91242dd806f78f3e5e5a557fba6a1da19e496f Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:18:34 +0200 Subject: [PATCH 51/59] Resolve comments and fix minor issues --- src/common/hooks/person.ts | 2 +- src/common/hooks/{confirm.ts => useConfirm.ts} | 0 src/components/person/PersonAutocomplete.tsx | 8 +++----- src/components/person/PersonInfo.tsx | 4 ++-- src/components/person/PersonSelectDialog.tsx | 5 +++-- src/components/recurring-donation/Form.tsx | 1 - 6 files changed, 9 insertions(+), 11 deletions(-) rename src/common/hooks/{confirm.ts => useConfirm.ts} (100%) diff --git a/src/common/hooks/person.ts b/src/common/hooks/person.ts index 15d91f7bc..496a70b24 100644 --- a/src/common/hooks/person.ts +++ b/src/common/hooks/person.ts @@ -5,7 +5,7 @@ import { useQuery, UseQueryOptions } from 'react-query' import { endpoints } from 'service/apiEndpoints' import { authQueryFnFactory } from 'service/restRequests' -export const usePersonList = ({ options }: { options: UseQueryOptions }) => { +export const usePersonList = (options?: UseQueryOptions) => { const { keycloak } = useKeycloak() return useQuery( endpoints.person.list.url, diff --git a/src/common/hooks/confirm.ts b/src/common/hooks/useConfirm.ts similarity index 100% rename from src/common/hooks/confirm.ts rename to src/common/hooks/useConfirm.ts diff --git a/src/components/person/PersonAutocomplete.tsx b/src/components/person/PersonAutocomplete.tsx index cf85ee102..82233590c 100644 --- a/src/components/person/PersonAutocomplete.tsx +++ b/src/components/person/PersonAutocomplete.tsx @@ -9,7 +9,7 @@ export type PersonAutocompleteProps = { AutocompleteProps, 'renderInput' | 'options' | 'getOptionLabel' | 'onChange' | 'loading' > - showId: boolean + showId?: boolean } export default function PersonAutocomplete({ onSelect, @@ -22,10 +22,8 @@ export default function PersonAutocomplete({ isLoading, refetch, } = usePersonList({ - options: { - enabled: false, - refetchOnWindowFocus: false, - }, + enabled: false, + refetchOnWindowFocus: false, }) return ( - {t('person:info.createdAt')}: {format(parseISO(person.createdAt), 'PPpp')} + {t('person:info.createdAt')}: {formatDateString(person.createdAt)} {t('person:info.company')}: {person.company} diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index c10a095fc..d5cfdf846 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -1,7 +1,7 @@ import { LoadingButton } from '@mui/lab' import { Box, Dialog, DialogActions, DialogContent, DialogTitle } from '@mui/material' import { translateError } from 'common/form/validation' -import useConfirm from 'common/hooks/confirm' +import useConfirm from 'common/hooks/useConfirm' import theme from 'common/theme' import CloseModalButton from 'components/common/CloseModalButton' import FormFieldButton from 'components/common/FormFieldButton' @@ -28,7 +28,7 @@ function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback closeCallback ? closeCallback(person) : null }, }) - + console.log(person) return ( <> diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index 5ceffcfd5..ec79850bf 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -16,7 +16,6 @@ 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' From e2abb0782c75a5998bb18942955f74ab0df02584 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:25:07 +0200 Subject: [PATCH 52/59] Fix modal store problem --- src/components/recurring-donation/grid/DeleteModal.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/grid/DeleteModal.tsx index f00773369..bbf40ff98 100644 --- a/src/components/recurring-donation/grid/DeleteModal.tsx +++ b/src/components/recurring-donation/grid/DeleteModal.tsx @@ -7,14 +7,15 @@ 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 { ModalStoreImpl as 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 ModalStoreInst = new ModalStore() + const { hideDelete, selectedRecord } = ModalStoreInst const { t } = useTranslation('recurring-donation') const mutationFn = useDeleteRecurringDonation(selectedRecord.id) @@ -37,5 +38,5 @@ export default observer(function DeleteModal() { deleteMutation.mutate(selectedRecord.id) } - return + return }) From fea3907299b68269295b3883de99f8394e2df9b0 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:30:34 +0200 Subject: [PATCH 53/59] Make only one modal store instance --- src/components/recurring-donation/grid/DeleteModal.tsx | 7 +++---- src/components/recurring-donation/grid/DetailsModal.tsx | 4 ++-- src/stores/dashboard/ModalStore.ts | 2 ++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/grid/DeleteModal.tsx index bbf40ff98..cbc2ef678 100644 --- a/src/components/recurring-donation/grid/DeleteModal.tsx +++ b/src/components/recurring-donation/grid/DeleteModal.tsx @@ -7,15 +7,14 @@ import { useTranslation } from 'next-i18next' import { RecurringDonationResponse } from 'gql/recurring-donation' import { ApiErrors } from 'service/apiErrors' import { useDeleteRecurringDonation } from 'service/recurringDonation' -import { ModalStoreImpl as ModalStore } from 'stores/dashboard/ModalStore' +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 ModalStoreInst = new ModalStore() - const { hideDelete, selectedRecord } = ModalStoreInst + const { hideDelete, selectedRecord } = ModalStore const { t } = useTranslation('recurring-donation') const mutationFn = useDeleteRecurringDonation(selectedRecord.id) @@ -38,5 +37,5 @@ export default observer(function DeleteModal() { deleteMutation.mutate(selectedRecord.id) } - return + return }) diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/grid/DetailsModal.tsx index 35654d560..804a61731 100644 --- a/src/components/recurring-donation/grid/DetailsModal.tsx +++ b/src/components/recurring-donation/grid/DetailsModal.tsx @@ -23,8 +23,8 @@ export default observer(function DetailsModal() { { name: t('amount'), value: `${data?.amount}` }, { name: t('extSubscriptionId'), value: `${data?.extSubscriptionId}` }, { name: t('extCustomerId'), value: `${data?.extCustomerId}` }, - { name: t('vaultId'), value: `${data?.vaultId}` }, + { name: t('vaultId'), value: `${data?.sourceVault}` }, ] - return + return }) diff --git a/src/stores/dashboard/ModalStore.ts b/src/stores/dashboard/ModalStore.ts index 35203468e..898d46ee8 100644 --- a/src/stores/dashboard/ModalStore.ts +++ b/src/stores/dashboard/ModalStore.ts @@ -48,3 +48,5 @@ export class ModalStoreImpl { this.selectedRecord = record } } + +export const ModalStore = new ModalStoreImpl() From 0da0a2b2bfac9ea62a45909e5fb02aa2b7f43160 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:32:49 +0200 Subject: [PATCH 54/59] Add modal store to GridActions --- src/components/recurring-donation/grid/Grid.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index 0648dfa98..45524fd17 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'next-i18next' import { Box } from '@mui/material' import { DataGrid, GridColDef, GridColumns, GridRenderCellParams } from '@mui/x-data-grid' +import { ModalStore } from 'stores/dashboard/ModalStore' import { routes } from 'common/routes' import { RecurringDonationResponse } from 'gql/recurring-donation' import { useRecurringDonationList } from 'common/hooks/recurringDonation' @@ -75,6 +76,7 @@ export default function Grid() { renderCell: (params: GridRenderCellParams): React.ReactNode => { return ( Date: Tue, 26 Apr 2022 23:33:59 +0200 Subject: [PATCH 55/59] Clean up folder structure --- src/components/recurring-donation/{grid => }/DeleteModal.tsx | 0 src/components/recurring-donation/{grid => }/DetailsModal.tsx | 0 .../{grid => }/RecurringDonationStatusSelect.tsx | 0 src/components/recurring-donation/grid/Grid.tsx | 4 ++-- 4 files changed, 2 insertions(+), 2 deletions(-) rename src/components/recurring-donation/{grid => }/DeleteModal.tsx (100%) rename src/components/recurring-donation/{grid => }/DetailsModal.tsx (100%) rename src/components/recurring-donation/{grid => }/RecurringDonationStatusSelect.tsx (100%) diff --git a/src/components/recurring-donation/grid/DeleteModal.tsx b/src/components/recurring-donation/DeleteModal.tsx similarity index 100% rename from src/components/recurring-donation/grid/DeleteModal.tsx rename to src/components/recurring-donation/DeleteModal.tsx diff --git a/src/components/recurring-donation/grid/DetailsModal.tsx b/src/components/recurring-donation/DetailsModal.tsx similarity index 100% rename from src/components/recurring-donation/grid/DetailsModal.tsx rename to src/components/recurring-donation/DetailsModal.tsx diff --git a/src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx b/src/components/recurring-donation/RecurringDonationStatusSelect.tsx similarity index 100% rename from src/components/recurring-donation/grid/RecurringDonationStatusSelect.tsx rename to src/components/recurring-donation/RecurringDonationStatusSelect.tsx diff --git a/src/components/recurring-donation/grid/Grid.tsx b/src/components/recurring-donation/grid/Grid.tsx index 45524fd17..0e9a601be 100644 --- a/src/components/recurring-donation/grid/Grid.tsx +++ b/src/components/recurring-donation/grid/Grid.tsx @@ -10,8 +10,8 @@ import { RecurringDonationResponse } from 'gql/recurring-donation' import { useRecurringDonationList } from 'common/hooks/recurringDonation' import GridActions from 'components/admin/GridActions' -import DeleteModal from './DeleteModal' -import DetailsModal from './DetailsModal' +import DeleteModal from '../DeleteModal' +import DetailsModal from '../DetailsModal' export default function Grid() { const { t } = useTranslation('recurring-donation') From 8ae5516410e6451e6716f8a87cdffcdce5733ab7 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:36:07 +0200 Subject: [PATCH 56/59] Update imports for RecurringDonationStatusSelect --- src/components/recurring-donation/Form.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index ec79850bf..b1b1f7d2c 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -19,7 +19,7 @@ import { AlertStore } from 'stores/AlertStore' 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' +import RecurringDonationStatusSelect from './RecurringDonationStatusSelect' import PersonSelectDialog from 'components/person/PersonSelectDialog' import { Formik } from 'formik' From 43ad5214f1d1903ddb0ae16ee01800e0cca5a99a Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:42:52 +0200 Subject: [PATCH 57/59] Fix apiEndpoints after rebase --- src/service/apiEndpoints.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/service/apiEndpoints.ts b/src/service/apiEndpoints.ts index d485d725a..dbf8ff196 100644 --- a/src/service/apiEndpoints.ts +++ b/src/service/apiEndpoints.ts @@ -149,8 +149,4 @@ export const endpoints = { { url: `/recurring-donation/${id}`, method: 'DELETE' }, deleteRecurringDonations: { url: '/recurring-donation/deletemany', method: 'POST' }, }, - account: { - me: { url: '/account/me', method: 'GET' }, - update: { url: '/account/me', method: 'PATCH' }, - }, } From 82c673c217ec2f25111aa8429ed10d96ad8f7615 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:45:19 +0200 Subject: [PATCH 58/59] Fix recurringDonation api endpoints --- src/service/apiEndpoints.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/service/apiEndpoints.ts b/src/service/apiEndpoints.ts index e0ced0a71..dbf8ff196 100644 --- a/src/service/apiEndpoints.ts +++ b/src/service/apiEndpoints.ts @@ -134,17 +134,6 @@ export const endpoints = { editTransfer: (id: string) => { url: `/transfer/${id}`, method: 'PUT' }, removeTransfer: (id: string) => { url: `/transfer/${id}`, method: 'DELETE' }, }, - recurringDonation: { - recurringDonation: { url: '/recurring-donation', method: 'GET' }, - getRecurringDonation: (id: string) => - { url: `/recurring-donation/${id}`, method: 'GET' }, - createRecurringDonation: { url: '/recurring-donation', method: 'POST' }, - editRecurringDonation: (id: string) => - { url: `/recurring-donation/${id}`, method: 'PUT' }, - deleteRecurringDonation: (id: string) => - { url: `/recurring-donation/${id}`, method: 'DELETE' }, - deleteRecurringDonations: { url: '/recurring-donation/deletemany', method: 'POST' }, - }, account: { me: { url: '/account/me', method: 'GET' }, update: { url: '/account/me', method: 'PATCH' }, From cf42395f913afb7970fb920f0c2da8e21f843bd1 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Tue, 26 Apr 2022 23:59:10 +0200 Subject: [PATCH 59/59] Fix formik submitting --- src/components/person/PersonSelectDialog.tsx | 1 - .../recurring-donation/DetailsModal.tsx | 1 - src/components/recurring-donation/Form.tsx | 120 +++++++++--------- 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/src/components/person/PersonSelectDialog.tsx b/src/components/person/PersonSelectDialog.tsx index d5cfdf846..98d418862 100644 --- a/src/components/person/PersonSelectDialog.tsx +++ b/src/components/person/PersonSelectDialog.tsx @@ -28,7 +28,6 @@ function PersonSelectDialog({ onConfirm: confirmCallback, onClose: closeCallback closeCallback ? closeCallback(person) : null }, }) - console.log(person) return ( <> = useRecurringDonation( selectedRecord.id, ) - console.log(data) const { t } = useTranslation('recurring-donation') const dataConverted = [ diff --git a/src/components/recurring-donation/Form.tsx b/src/components/recurring-donation/Form.tsx index b1b1f7d2c..d6c483878 100644 --- a/src/components/recurring-donation/Form.tsx +++ b/src/components/recurring-donation/Form.tsx @@ -21,7 +21,7 @@ import SubmitButton from 'components/common/form/SubmitButton' import CurrencySelect from 'components/currency/CurrencySelect' import RecurringDonationStatusSelect from './RecurringDonationStatusSelect' import PersonSelectDialog from 'components/person/PersonSelectDialog' -import { Formik } from 'formik' +import { Form, Formik } from 'formik' export enum RecurringDonationStatus { trialing = 'trialing', @@ -111,65 +111,67 @@ export default function EditForm() { onSubmit={onSubmit} initialValues={initialValues} validationSchema={validationSchema}> - {({ errors, setFieldTouched, setFieldValue }) => ( - - - {id ? t('recurring-donation:edit-form-heading') : t('recurring-donation:form-heading')} - - - - { - person ? setFieldValue('personId', person.id) : setFieldTouched('personId') - }} - onClose={(person) => { - person ? setFieldValue('personId', person.id) : setFieldTouched('personId') - }} - /> + {({ errors, handleSubmit, setFieldTouched, setFieldValue }) => ( + + + + {id + ? t('recurring-donation:edit-form-heading') + : t('recurring-donation:form-heading')} + + + + { + person ? setFieldValue('personId', person.id) : setFieldTouched('personId') + }} + onClose={(person) => { + person ? setFieldValue('personId', person.id) : setFieldTouched('personId') + }} + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - {id ? <> : <>} - - - - - - - - - - +
+ )} )