From 8a1b15d7950031dd90999acc114e3f34c0a5ec33 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Mon, 19 Dec 2022 15:44:41 +0100 Subject: [PATCH 01/17] 1254 - add campaign title to certificate --- src/components/pdf/Certificate.tsx | 8 +++++--- .../api/pdf/certificate/[donationId].tsx | 20 ++++++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/components/pdf/Certificate.tsx b/src/components/pdf/Certificate.tsx index 4233ab82c..e0fe93c6a 100644 --- a/src/components/pdf/Certificate.tsx +++ b/src/components/pdf/Certificate.tsx @@ -5,6 +5,7 @@ import { DonationResponse } from 'gql/donations' import { PersonResponse } from 'gql/person' import { formatDateString } from 'common/util/date' import { money } from 'common/util/money' +import { CampaignResponse } from 'gql/campaigns' Font.register({ family: 'Arial', @@ -106,9 +107,10 @@ const styles = StyleSheet.create({ type Props = { donation: DonationResponse + campaign: CampaignResponse person?: PersonResponse } -export default function Certificate({ donation, person }: Props) { +export default function Certificate({ donation, person, campaign }: Props) { const name = `${person?.firstName} ${person?.lastName}` const formattedDate = formatDateString(donation.createdAt) return ( @@ -128,8 +130,8 @@ export default function Certificate({ donation, person }: Props) { дари сума в размер на{' '} - {money(donation?.amount ?? 0)} за дейността на - сдружението. + {money(donation?.amount ?? 0)} за кампания{' '} + {campaign.title} diff --git a/src/pages/api/pdf/certificate/[donationId].tsx b/src/pages/api/pdf/certificate/[donationId].tsx index 27970cc1d..9783d762a 100644 --- a/src/pages/api/pdf/certificate/[donationId].tsx +++ b/src/pages/api/pdf/certificate/[donationId].tsx @@ -7,6 +7,9 @@ import { endpoints } from 'service/apiEndpoints' import Certificate from 'components/pdf/Certificate' import { authConfig } from 'service/restRequests' import { getToken } from 'next-auth/jwt' +import { VaultResponse } from 'gql/vault' +import { CampaignResponse } from 'gql/campaigns' +import { AxiosResponse } from 'axios' const Handler: NextApiHandler = async (req: NextApiRequest, res: NextApiResponse) => { const id = Array.isArray(req.query.donationId) ? req.query.donationId[0] : req.query.donationId @@ -21,12 +24,27 @@ const Handler: NextApiHandler = async (req: NextApiRequest, res: NextApiResponse endpoints.donation.getUserDonation(id).url, authConfig(jwt?.accessToken), ) + //get the target vault from the donation + const { data: campaign } = await apiClient + .get( + // Casting to string here might lead to an error + endpoints.vaults.getVault(donation.targetVaultId).url, + authConfig(jwt?.accessToken), + ) + .then((res) => { + const campaignPromise: Promise> = + apiClient.get( + endpoints.campaign.viewCampaignById(res.data.campaignId).url, + authConfig(jwt?.accessToken), + ) + return campaignPromise + }) if (!donation) { res.status(404).json({ notFound: true }) } else { const pdfStream = await renderToStream( - , + , ) res.setHeader('Content-Type', 'application/pdf') pdfStream.pipe(res) From e2dbbb35a5d140d09e85daa77e8313229373ab3a Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Mon, 19 Dec 2022 16:15:04 +0100 Subject: [PATCH 02/17] 1254 - fix off imports --- src/components/pdf/Certificate-en.tsx | 0 src/pages/api/pdf/certificate/[donationId].tsx | 15 ++++++++------- 2 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 src/components/pdf/Certificate-en.tsx diff --git a/src/components/pdf/Certificate-en.tsx b/src/components/pdf/Certificate-en.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/src/pages/api/pdf/certificate/[donationId].tsx b/src/pages/api/pdf/certificate/[donationId].tsx index 9783d762a..e0ec76ba1 100644 --- a/src/pages/api/pdf/certificate/[donationId].tsx +++ b/src/pages/api/pdf/certificate/[donationId].tsx @@ -1,15 +1,16 @@ -import { renderToStream } from '@react-pdf/renderer' import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' +import { getToken } from 'next-auth/jwt' +import { AxiosResponse } from 'axios' +import { renderToStream } from '@react-pdf/renderer' -import { apiClient } from 'service/apiClient' import { UserDonationResponse } from 'gql/donations' -import { endpoints } from 'service/apiEndpoints' -import Certificate from 'components/pdf/Certificate' -import { authConfig } from 'service/restRequests' -import { getToken } from 'next-auth/jwt' import { VaultResponse } from 'gql/vault' import { CampaignResponse } from 'gql/campaigns' -import { AxiosResponse } from 'axios' +import { apiClient } from 'service/apiClient' +import { endpoints } from 'service/apiEndpoints' +import { authConfig } from 'service/restRequests' + +import Certificate from 'components/pdf/Certificate' const Handler: NextApiHandler = async (req: NextApiRequest, res: NextApiResponse) => { const id = Array.isArray(req.query.donationId) ? req.query.donationId[0] : req.query.donationId From be93db9b727939665aa5e3ec9b0e70cef05cdb90 Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Mon, 19 Dec 2022 17:23:01 +0100 Subject: [PATCH 03/17] 1254 - remove Certifacte-en --- README.md | 2 ++ src/components/pdf/Certificate-en.tsx | 0 2 files changed, 2 insertions(+) delete mode 100644 src/components/pdf/Certificate-en.tsx diff --git a/README.md b/README.md index d935eb8c9..1b33bcd65 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,9 @@ Read more at [End-2-End Testing](https://github.com/podkrepi-bg/frontend/blob/ma ## Contributors ✨ + [![All Contributors](https://img.shields.io/badge/all_contributors-63-orange.svg?style=flat-square)](#contributors-) + Please check [contributors guide](https://github.com/podkrepi-bg/frontend/blob/master/CONTRIBUTING.md) for: diff --git a/src/components/pdf/Certificate-en.tsx b/src/components/pdf/Certificate-en.tsx deleted file mode 100644 index e69de29bb..000000000 From 70291fded4c617baeb392d54aba1347e82a4bcfd Mon Sep 17 00:00:00 2001 From: "dimitar.nizamov" Date: Mon, 19 Dec 2022 17:24:12 +0100 Subject: [PATCH 04/17] 1254 - remove unecessary comments --- src/pages/api/pdf/certificate/[donationId].tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/api/pdf/certificate/[donationId].tsx b/src/pages/api/pdf/certificate/[donationId].tsx index e0ec76ba1..25614fd57 100644 --- a/src/pages/api/pdf/certificate/[donationId].tsx +++ b/src/pages/api/pdf/certificate/[donationId].tsx @@ -21,14 +21,12 @@ const Handler: NextApiHandler = async (req: NextApiRequest, res: NextApiResponse return } const { data: donation } = await apiClient.get( - // Casting to string here might lead to an error endpoints.donation.getUserDonation(id).url, authConfig(jwt?.accessToken), ) - //get the target vault from the donation + const { data: campaign } = await apiClient .get( - // Casting to string here might lead to an error endpoints.vaults.getVault(donation.targetVaultId).url, authConfig(jwt?.accessToken), ) From 08af73bc0fa4634c643e2bc13bb4fae617a771a2 Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Thu, 15 Dec 2022 20:27:13 +0200 Subject: [PATCH 05/17] Remove Discord widget bot from the code (#1256) --- package.json | 1 - src/pages/chat.tsx | 34 ---------------------------------- yarn.lock | 31 ------------------------------- 3 files changed, 66 deletions(-) delete mode 100644 src/pages/chat.tsx diff --git a/package.json b/package.json index 51908e36b..a2e8921db 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "@sentry/nextjs": "7.21.1", "@tanstack/react-query": "^4.16.1", "@tryghost/content-api": "^1.11.4", - "@widgetbot/react-embed": "^1.4.0", "axios": "0.21.4", "axios-hooks": "2.7.0", "date-fns": "2.24.0", diff --git a/src/pages/chat.tsx b/src/pages/chat.tsx deleted file mode 100644 index 79d0e80e4..000000000 --- a/src/pages/chat.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import { GetServerSideProps } from 'next' -import { serverSideTranslations } from 'next-i18next/serverSideTranslations' -import WidgetBot from '@widgetbot/react-embed' - -import Layout from 'components/layout/Layout' - -export type LoginPageProps = { - providers: string[] - csrfToken: string -} - -export const getServerSideProps: GetServerSideProps = async (ctx) => { - return { - props: { - ...(await serverSideTranslations(ctx.locale ?? 'bg', ['common', 'auth', 'validation'])), - csrfToken: '', - providers: [], - }, - } -} - -export default function Chat() { - return ( - - {/* @ts-expect-error pass css string instead of number */} - - - ) -} diff --git a/yarn.lock b/yarn.lock index c3814321c..903f6b563 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3015,25 +3015,6 @@ __metadata: languageName: node linkType: hard -"@widgetbot/embed-api@npm:^1.1.3": - version: 1.1.3 - resolution: "@widgetbot/embed-api@npm:1.1.3" - checksum: b6b8385d726a76fd1aad7397376824721d86ce19c28e2994b0768c7b23dbfbdbfad60dd7a2a11a0ab00cf17c9dec54b84114fe9c7684682d72b0a357fb179cf0 - languageName: node - linkType: hard - -"@widgetbot/react-embed@npm:^1.4.0": - version: 1.4.0 - resolution: "@widgetbot/react-embed@npm:1.4.0" - dependencies: - "@widgetbot/embed-api": ^1.1.3 - react: ^16.13.1 - peerDependencies: - react: ^15.x || ^16.x - checksum: 945e16c476beedba27aba4cdbe5e90f6e5936f7908263fedda4a2ff2c0222c7ecbd683bf048466086f59b015cea7733e3b08f7c12d2b9965c85104be39d3f946 - languageName: node - linkType: hard - "abab@npm:^2.0.3, abab@npm:^2.0.5": version: 2.0.5 resolution: "abab@npm:2.0.5" @@ -8836,7 +8817,6 @@ __metadata: "@types/yup": 0.29.11 "@typescript-eslint/eslint-plugin": 4.26.0 "@typescript-eslint/parser": 4.26.0 - "@widgetbot/react-embed": ^1.4.0 all-contributors-cli: ^6.20.0 axios: 0.21.4 axios-hooks: 2.7.0 @@ -9418,17 +9398,6 @@ __metadata: languageName: node linkType: hard -"react@npm:^16.13.1": - version: 16.14.0 - resolution: "react@npm:16.14.0" - dependencies: - loose-envify: ^1.1.0 - object-assign: ^4.1.1 - prop-types: ^15.6.2 - checksum: 8484f3ecb13414526f2a7412190575fc134da785c02695eb92bb6028c930bfe1c238d7be2a125088fec663cc7cda0a3623373c46807cf2c281f49c34b79881ac - languageName: node - linkType: hard - "read-pkg-up@npm:^3.0.0": version: 3.0.0 resolution: "read-pkg-up@npm:3.0.0" From 320146208bee863e522e6f0c8b20b7591c0f9a97 Mon Sep 17 00:00:00 2001 From: Anton Mihaylov Date: Fri, 16 Dec 2022 09:24:09 +0200 Subject: [PATCH 06/17] feat: extract email field as a separate component and reuse it in the forms (#1255) --- .../auth/forgottenPassword/ForgottenPasswordForm.tsx | 4 ++-- src/components/auth/login/LoginForm.tsx | 4 ++-- src/components/auth/profile/UpdateEmailModal.tsx | 4 ++-- src/components/auth/register/RegisterForm.tsx | 3 ++- src/components/common/form/EmailField.tsx | 10 ++++++++++ src/components/contact/ContactForm.tsx | 9 ++------- src/components/irregularity/admin/grid/CreateForm.tsx | 4 +++- src/components/irregularity/admin/grid/EditForm.tsx | 4 +++- src/components/one-time-donation/AnonymousForm.tsx | 4 ++-- src/components/one-time-donation/LoginForm.tsx | 4 ++-- src/components/one-time-donation/RegisterDialog.tsx | 8 ++------ src/components/person/grid/PersonForm.tsx | 9 ++------- 12 files changed, 34 insertions(+), 33 deletions(-) create mode 100644 src/components/common/form/EmailField.tsx diff --git a/src/components/auth/forgottenPassword/ForgottenPasswordForm.tsx b/src/components/auth/forgottenPassword/ForgottenPasswordForm.tsx index 659eafd58..f9125bc99 100644 --- a/src/components/auth/forgottenPassword/ForgottenPasswordForm.tsx +++ b/src/components/auth/forgottenPassword/ForgottenPasswordForm.tsx @@ -5,7 +5,7 @@ import { Typography, Grid } from '@mui/material' import SubmitButton from 'components/common/form/SubmitButton' import GenericForm from 'components/common/form/GenericForm' -import FormTextField from 'components/common/form/FormTextField' +import EmailField from 'components/common/form/EmailField' import { useMutation } from '@tanstack/react-query' import { AxiosError, AxiosResponse } from 'axios' import { ApiErrors } from 'service/apiErrors' @@ -62,7 +62,7 @@ export default function ForgottenPasswordForm({ - + diff --git a/src/components/auth/login/LoginForm.tsx b/src/components/auth/login/LoginForm.tsx index 7869737fe..d32c669bb 100644 --- a/src/components/auth/login/LoginForm.tsx +++ b/src/components/auth/login/LoginForm.tsx @@ -10,7 +10,7 @@ import { AlertStore } from 'stores/AlertStore' import FormInput from 'components/common/form/FormInput' import GenericForm from 'components/common/form/GenericForm' import SubmitButton from 'components/common/form/SubmitButton' -import FormTextField from 'components/common/form/FormTextField' +import EmailField from 'components/common/form/EmailField' import Google from 'common/icons/Google' import PasswordField from 'components/common/form/PasswordField' import { email, password } from 'common/form/validation' @@ -73,7 +73,7 @@ export default function LoginForm({ initialValues = defaults }: LoginFormProps) - + diff --git a/src/components/auth/profile/UpdateEmailModal.tsx b/src/components/auth/profile/UpdateEmailModal.tsx index 18c0de623..34ad1fc54 100644 --- a/src/components/auth/profile/UpdateEmailModal.tsx +++ b/src/components/auth/profile/UpdateEmailModal.tsx @@ -2,7 +2,7 @@ import { Modal, Box, Grid, IconButton } from '@mui/material' import { styled } from '@mui/material/styles' import GenericForm from 'components/common/form/GenericForm' import SubmitButton from 'components/common/form/SubmitButton' -import FormTextField from 'components/common/form/FormTextField' +import EmailField from 'components/common/form/EmailField' import { Person, UpdateUserAccount, UpdatePerson } from 'gql/person' import { useMutation } from '@tanstack/react-query' import { AxiosError, AxiosResponse } from 'axios' @@ -129,7 +129,7 @@ function UpdateEmailModal({ validationSchema={validationSchema}> - + diff --git a/src/components/auth/register/RegisterForm.tsx b/src/components/auth/register/RegisterForm.tsx index d7230ec3c..dac885e5f 100644 --- a/src/components/auth/register/RegisterForm.tsx +++ b/src/components/auth/register/RegisterForm.tsx @@ -15,6 +15,7 @@ import FormTextField from 'components/common/form/FormTextField' import PasswordField from 'components/common/form/PasswordField' import AcceptPrivacyPolicyField from 'components/common/form/AcceptPrivacyPolicyField' import AcceptTermsField from 'components/common/form/AcceptTermsField' +import EmailField from 'components/common/form/EmailField' export type RegisterFormData = { firstName: string @@ -109,7 +110,7 @@ export default function RegisterForm({ initialValues = defaults }: RegisterFormP /> - + diff --git a/src/components/common/form/EmailField.tsx b/src/components/common/form/EmailField.tsx new file mode 100644 index 000000000..115fce5be --- /dev/null +++ b/src/components/common/form/EmailField.tsx @@ -0,0 +1,10 @@ +import React from 'react' +import FormTextField, { RegisterFormProps } from './FormTextField' + +export type EmailFieldProps = Omit & { + type?: string +} + +export default function EmailField(props: EmailFieldProps) { + return +} diff --git a/src/components/contact/ContactForm.tsx b/src/components/contact/ContactForm.tsx index fd7df2f70..942ee4372 100644 --- a/src/components/contact/ContactForm.tsx +++ b/src/components/contact/ContactForm.tsx @@ -16,6 +16,7 @@ import FormTextField from 'components/common/form/FormTextField' import AcceptTermsField from 'components/common/form/AcceptTermsField' import { name, companyName, phone, email } from 'common/form/validation' import AcceptPrivacyPolicyField from 'components/common/form/AcceptPrivacyPolicyField' +import EmailField from 'components/common/form/EmailField' const validationSchema: yup.SchemaOf = yup .object() @@ -110,13 +111,7 @@ export default function ContactForm({ initialValues = defaults }: ContactFormPro /> - + - + diff --git a/src/components/irregularity/admin/grid/EditForm.tsx b/src/components/irregularity/admin/grid/EditForm.tsx index c6d17d804..0449e74fc 100644 --- a/src/components/irregularity/admin/grid/EditForm.tsx +++ b/src/components/irregularity/admin/grid/EditForm.tsx @@ -24,6 +24,8 @@ import FileUpload from 'components/file-upload/FileUpload' import GenericForm from 'components/common/form/GenericForm' import SubmitButton from 'components/common/form/SubmitButton' import FormTextField from 'components/common/form/FormTextField' +import EmailField from 'components/common/form/EmailField' + import { IrregularityEditInput, IrregularityFileResponse, @@ -162,7 +164,7 @@ export default function EditForm({ campaigns, irregularity, irregularityFiles }: - + diff --git a/src/components/one-time-donation/AnonymousForm.tsx b/src/components/one-time-donation/AnonymousForm.tsx index df6a28e48..fdd735ea1 100644 --- a/src/components/one-time-donation/AnonymousForm.tsx +++ b/src/components/one-time-donation/AnonymousForm.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { useTranslation } from 'next-i18next' import { Grid, Typography } from '@mui/material' -import FormTextField from 'components/common/form/FormTextField' +import EmailField from 'components/common/form/EmailField' export default function AnonymousForm() { const { t } = useTranslation('one-time-donation') @@ -16,7 +16,7 @@ export default function AnonymousForm() { {t('anonymous-menu.info-start')} - + diff --git a/src/components/one-time-donation/LoginForm.tsx b/src/components/one-time-donation/LoginForm.tsx index a5cbb975e..615af598a 100644 --- a/src/components/one-time-donation/LoginForm.tsx +++ b/src/components/one-time-donation/LoginForm.tsx @@ -5,7 +5,7 @@ import { Box, Button, CircularProgress, Grid, Typography } from '@mui/material' import theme from 'common/theme' import Google from 'common/icons/Google' import { OneTimeDonation } from 'gql/donations' -import FormTextField from 'components/common/form/FormTextField' +import EmailField from '../common/form/EmailField' import { signIn } from 'next-auth/react' import { StepsContext } from './helpers/stepperContext' import { AlertStore } from 'stores/AlertStore' @@ -53,7 +53,7 @@ function LoginForm() { - + - + diff --git a/src/components/person/grid/PersonForm.tsx b/src/components/person/grid/PersonForm.tsx index a90575425..2251a9a5e 100644 --- a/src/components/person/grid/PersonForm.tsx +++ b/src/components/person/grid/PersonForm.tsx @@ -6,6 +6,7 @@ import GenericForm from 'components/common/form/GenericForm' import { name, phone, email } from 'common/form/validation' import SubmitButton from 'components/common/form/SubmitButton' import FormTextField from 'components/common/form/FormTextField' +import EmailField from 'components/common/form/EmailField' import { AdminPersonFormData, AdminPersonResponse, PersonResponse } from 'gql/person' import { useMutation, UseQueryResult } from '@tanstack/react-query' import { AxiosError, AxiosResponse } from 'axios' @@ -100,13 +101,7 @@ export default function PersonForm({ initialValues = defaults }: FormProps) { /> */} - + Date: Fri, 16 Dec 2022 16:05:21 +0200 Subject: [PATCH 07/17] Improve typing of user session (#1258) --- public/locales/bg/profile.json | 2 +- src/components/layout/nav/AdminMenu.tsx | 2 +- src/components/layout/nav/PrivateMenu.tsx | 2 +- src/gql/next-auth.d.ts | 15 ++++++++++++++- src/pages/api/auth/[...nextauth].ts | 13 ------------- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/public/locales/bg/profile.json b/public/locales/bg/profile.json index 7c9ccca82..7ba18823f 100644 --- a/public/locales/bg/profile.json +++ b/public/locales/bg/profile.json @@ -3,7 +3,7 @@ "campaigns": "Подкрепени кампании", "personalInfo": { "index": "Лична информация", - "login": "Login информация:", + "login": "Данни за вход:", "email": "Email адрес:", "edit": "Редактирай", "password": "Парола:", diff --git a/src/components/layout/nav/AdminMenu.tsx b/src/components/layout/nav/AdminMenu.tsx index dc3adfe42..794e26f4b 100644 --- a/src/components/layout/nav/AdminMenu.tsx +++ b/src/components/layout/nav/AdminMenu.tsx @@ -67,7 +67,7 @@ export default function AdminMenu() { return null } - const title = `${session.user.name}\n(${session.user.email})` + const title = `${session?.user?.name}\n(${session?.user?.email})` const lettersAvatar = `${session.user?.given_name.charAt(0)}${session.user?.family_name.charAt( 0, )}`.toUpperCase() diff --git a/src/components/layout/nav/PrivateMenu.tsx b/src/components/layout/nav/PrivateMenu.tsx index 52b258e84..86fa6dbbd 100644 --- a/src/components/layout/nav/PrivateMenu.tsx +++ b/src/components/layout/nav/PrivateMenu.tsx @@ -60,7 +60,7 @@ export default function PrivateMenu() { return null } - const title = `${session.user.name}\n(${session.user.email})` + const title = `${session?.user?.name}\n(${session?.user?.email})` const lettersAvatar = `${session.user?.given_name.charAt(0)}${session.user?.family_name.charAt( 0, )}`.toUpperCase() diff --git a/src/gql/next-auth.d.ts b/src/gql/next-auth.d.ts index 1d82533a2..fae7daf01 100644 --- a/src/gql/next-auth.d.ts +++ b/src/gql/next-auth.d.ts @@ -1,4 +1,4 @@ -import NextAuth from 'next-auth' +import { ServerUser } from 'service/auth' /** * Declaring the Session and User type as per docs here: @@ -28,3 +28,16 @@ declare module 'next-auth' { picture: string } } + +declare module 'next-auth/jwt' { + /** + * JWT contents which builds the session object + */ + export interface JWT { + accessToken: string + accessTokenExpires: number + refreshToken: string + user: ServerUser | null + expires?: number + } +} diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index d99662aa6..27f8177c5 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -13,19 +13,6 @@ import { import { apiClient } from 'service/apiClient' import { endpoints } from 'service/apiEndpoints' -declare module 'next-auth/jwt' { - /** - * JWT contents which builds the session object - */ - export interface JWT { - accessToken: string - accessTokenExpires: number - refreshToken: string - user: ServerUser | null - expires?: number - } -} - const onCreate: EventCallbacks['createUser'] = async ({ user }) => { const { email } = user From b16ca391f727d0f17b032aea7b42aadc3cfc30b1 Mon Sep 17 00:00:00 2001 From: Lyoubomir Katzarov Date: Fri, 16 Dec 2022 19:17:47 +0100 Subject: [PATCH 08/17] 1129 - Add pagination to donation wishes. (#1252) * 1129 - Add pagination to donation wishes. * 1129 - Check if numOfPages is defined at runtime. * Move pagination to the bottom of the wishes list Co-authored-by: Ilko Kacharov --- src/common/hooks/donationWish.ts | 11 ++- src/components/campaigns/CampaignDetails.tsx | 4 +- src/components/campaigns/CampaignMessages.tsx | 72 --------------- src/components/campaigns/DonationWishes.tsx | 91 +++++++++++++++++++ src/gql/donationWish.d.ts | 5 + src/service/apiEndpoints.ts | 7 +- 6 files changed, 109 insertions(+), 81 deletions(-) delete mode 100644 src/components/campaigns/CampaignMessages.tsx create mode 100644 src/components/campaigns/DonationWishes.tsx diff --git a/src/common/hooks/donationWish.ts b/src/common/hooks/donationWish.ts index 30e06d278..3a325f051 100644 --- a/src/common/hooks/donationWish.ts +++ b/src/common/hooks/donationWish.ts @@ -2,10 +2,11 @@ import { useQuery } from '@tanstack/react-query' import { endpoints } from 'service/apiEndpoints' -import { DonationWishResponse } from 'gql/donationWish' +import { DonationWishPaginatedResponse } from 'gql/donationWish' -export function useDonationWishesList(camapignId: string) { - return useQuery([ - endpoints.donationWish.listDonationWishes(camapignId).url, - ]) +export function useDonationWishesList(camapignId: string, pageIndex?: number, pageSize?: number) { + return useQuery({ + queryKey: [endpoints.donationWish.listDonationWishes(camapignId, pageIndex, pageSize).url], + keepPreviousData: true, + }) } diff --git a/src/components/campaigns/CampaignDetails.tsx b/src/components/campaigns/CampaignDetails.tsx index 04856b556..a022c7ce1 100644 --- a/src/components/campaigns/CampaignDetails.tsx +++ b/src/components/campaigns/CampaignDetails.tsx @@ -2,7 +2,7 @@ import React from 'react' import Image from 'next/image' import { CampaignResponse } from 'gql/campaigns' import { BeneficiaryType } from 'components/beneficiary/BeneficiaryTypes' -import CampaignMessages from './CampaignMessages' +import DonationWishes from './DonationWishes' import CampaignSlider from './CampaignSlider' import { backgroundCampaignPictureUrl, @@ -164,7 +164,7 @@ export default function CampaignDetails({ campaign }: Props) { - + diff --git a/src/components/campaigns/CampaignMessages.tsx b/src/components/campaigns/CampaignMessages.tsx deleted file mode 100644 index f8313dc4f..000000000 --- a/src/components/campaigns/CampaignMessages.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react' -import { styled } from '@mui/material/styles' -import { useTranslation } from 'next-i18next' -import { Grid, Typography } from '@mui/material' -import RateReviewIcon from '@mui/icons-material/RateReview' -import { useDonationWishesList } from 'common/hooks/donationWish' -import { getExactDate } from 'common/util/date' -import { bg, enUS } from 'date-fns/locale' -import AccountCircleIcon from '@mui/icons-material/AccountCircle' - -const PREFIX = 'CampaignMessages' - -const classes = { - messagesWrapper: `${PREFIX}-messagesWrapper`, - messagesTitleWrapper: `${PREFIX}-messagesTitleWrapper`, - wishWrapper: `${PREFIX}-wishWrapper`, -} - -const StyledGrid = styled(Grid)(({ theme }) => ({ - [`&.${classes.messagesWrapper}`]: { - margin: theme.spacing(5, 0), - }, - - [`& .${classes.messagesTitleWrapper}`]: { - display: 'flex', - gap: theme.spacing(3), - alignItems: 'end', - marginBottom: theme.spacing(3), - }, - [`& .${classes.wishWrapper}`]: { - display: 'flex', - gap: theme.spacing(1), - alignItems: 'flex-start', - marginBottom: theme.spacing(2), - }, -})) - -export default function CampaignMessages({ campaignId }: { campaignId: string }) { - const { t, i18n } = useTranslation() - const locale = i18n.language == 'bg' ? bg : enUS - const { data: list } = useDonationWishesList(campaignId) - - return ( - - - - {t('campaigns:campaign.messages')} - - {list?.map((wish) => ( - - - - - {wish.person - ? wish.person.firstName + ' ' + wish.person.lastName - : t('campaigns:donations.anonymous')} - - - - - {wish.message} - - {getExactDate(wish.createdAt, locale)} - - - ))} - - ) -} diff --git a/src/components/campaigns/DonationWishes.tsx b/src/components/campaigns/DonationWishes.tsx new file mode 100644 index 000000000..90caac7f2 --- /dev/null +++ b/src/components/campaigns/DonationWishes.tsx @@ -0,0 +1,91 @@ +import React, { useRef, useState } from 'react' +import { useTranslation } from 'next-i18next' +import { Unstable_Grid2 as Grid2, Stack, Typography } from '@mui/material' +import RateReviewIcon from '@mui/icons-material/RateReview' +import { useDonationWishesList } from 'common/hooks/donationWish' +import { getExactDate } from 'common/util/date' +import { bg, enUS } from 'date-fns/locale' +import AccountCircleIcon from '@mui/icons-material/AccountCircle' +import Pagination from '@mui/material/Pagination' + +type Props = { + campaignId: string + pageSize?: number +} + +export default function DonationWishes({ campaignId, pageSize = 12 }: Props) { + const { t, i18n } = useTranslation() + const titleRef = useRef(null) + const locale = i18n.language == 'bg' ? bg : enUS + + const [pageIndex, setPageIndex] = useState(0) + const { data, isSuccess } = useDonationWishesList(campaignId, pageIndex, pageSize) + + const numOfPages = isSuccess ? Math.ceil(data.totalCount / pageSize) : 0 + + const handlePageChange = (_e: React.ChangeEvent, page: number) => { + // 's impl is 1 index based + // Our pagination apis are 0 index based + setPageIndex(page - 1) + if (titleRef.current) { + titleRef.current.scrollIntoView({ + block: 'center', + behavior: 'smooth', + }) + } + } + + return ( + + + + + + {t('campaigns:campaign.messages')} + + + + + {isSuccess && + data.items.map((wish) => ( + + + + + + + + + + {wish.person + ? wish.person.firstName + ' ' + wish.person.lastName + : t('campaigns:donations.anonymous')} + + + {getExactDate(wish.createdAt, locale)} + + + {wish.message} + + + + + ))} + + {numOfPages > 1 && ( + + )} + + + + ) +} diff --git a/src/gql/donationWish.d.ts b/src/gql/donationWish.d.ts index dcdc9d3e3..4731692ba 100644 --- a/src/gql/donationWish.d.ts +++ b/src/gql/donationWish.d.ts @@ -14,3 +14,8 @@ export type DonationWishResponse = { person?: { firstName: string; lastName: string } createdAt: DateTime } + +export type DonationWishPaginatedResponse = { + items: DonationWishResponse[] + totalCount: number +} diff --git a/src/service/apiEndpoints.ts b/src/service/apiEndpoints.ts index 5e7b83293..844882550 100644 --- a/src/service/apiEndpoints.ts +++ b/src/service/apiEndpoints.ts @@ -214,7 +214,10 @@ export const endpoints = { }, donationWish: { createDonationWish: { url: '/donation-wish', method: 'POST' }, - listDonationWishes: (campaignId: string) => - { url: `/donation-wish/list/${campaignId}`, method: 'GET' }, + listDonationWishes: (campaignId?: string, pageIndex?: number, pageSize?: number) => + { + url: `/donation-wish/list/${campaignId}?pageindex=${pageIndex}&pagesize=${pageSize}`, + method: 'GET', + }, }, } From 54ec381c576d36078a6d4878b8a3bd9dedc6196d Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Sat, 17 Dec 2022 07:39:54 +0200 Subject: [PATCH 09/17] Create eslint.yml --- .github/workflows/eslint.yml | 50 ++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/eslint.yml diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml new file mode 100644 index 000000000..a25c87779 --- /dev/null +++ b/.github/workflows/eslint.yml @@ -0,0 +1,50 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +# ESLint is a tool for identifying and reporting on patterns +# found in ECMAScript/JavaScript code. +# More details at https://github.com/eslint/eslint +# and https://eslint.org + +name: ESLint + +on: + push: + branches: [ "master" ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ "master" ] + schedule: + - cron: '36 22 * * 5' + +jobs: + eslint: + name: Run eslint scanning + runs-on: ubuntu-latest + permissions: + contents: read + security-events: write + actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Install ESLint + run: | + npm install eslint@8.10.0 + npm install @microsoft/eslint-formatter-sarif@2.1.7 + + - name: Run ESLint + run: npx eslint . + --config .eslintrc.js + --ext .js,.jsx,.ts,.tsx + --format @microsoft/eslint-formatter-sarif + --output-file eslint-results.sarif + continue-on-error: true + + - name: Upload analysis results to GitHub + uses: github/codeql-action/upload-sarif@v2 + with: + sarif_file: eslint-results.sarif + wait-for-processing: true From 8b4f707f9d18a144250c08b5073b9aee42190edf Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Sat, 17 Dec 2022 07:41:19 +0200 Subject: [PATCH 10/17] Update .all-contributorsrc --- .all-contributorsrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index 84874bbf9..ae80e071b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -9,7 +9,7 @@ "login": "kachar", "name": "Ilko", "avatar_url": "https://avatars.githubusercontent.com/u/893608?v=4", - "profile": "https://stackoverflow.com/users/668245/kachar", + "profile": "https://github.com/kachar", "contributions": [ "code", "doc", From 2893958e5007d70af9ba00b15491af1516894d22 Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Sat, 17 Dec 2022 07:42:03 +0200 Subject: [PATCH 11/17] Update .all-contributorsrc --- .all-contributorsrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.all-contributorsrc b/.all-contributorsrc index ae80e071b..d98eada93 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -710,7 +710,7 @@ ] } ], - "contributorsPerLine": 7, + "contributorsPerLine": 10, "projectName": "frontend", "projectOwner": "podkrepi-bg", "repoType": "github", From c015b85e41f9e3c11db0eb827e34c9298e698771 Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Sat, 17 Dec 2022 07:46:00 +0200 Subject: [PATCH 12/17] Update eslint.yml --- .github/workflows/eslint.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml index a25c87779..9e1201572 100644 --- a/.github/workflows/eslint.yml +++ b/.github/workflows/eslint.yml @@ -32,11 +32,11 @@ jobs: - name: Install ESLint run: | - npm install eslint@8.10.0 - npm install @microsoft/eslint-formatter-sarif@2.1.7 + yarn install eslint@8.10.0 + yarn install @microsoft/eslint-formatter-sarif@2.1.7 - name: Run ESLint - run: npx eslint . + run: yarn run eslint . --config .eslintrc.js --ext .js,.jsx,.ts,.tsx --format @microsoft/eslint-formatter-sarif From 279ded1f60b42ae57a1c5d7d72ce9402a31f6c12 Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Sat, 17 Dec 2022 07:54:27 +0200 Subject: [PATCH 13/17] Delete eslint.yml --- .github/workflows/eslint.yml | 50 ------------------------------------ 1 file changed, 50 deletions(-) delete mode 100644 .github/workflows/eslint.yml diff --git a/.github/workflows/eslint.yml b/.github/workflows/eslint.yml deleted file mode 100644 index 9e1201572..000000000 --- a/.github/workflows/eslint.yml +++ /dev/null @@ -1,50 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. -# ESLint is a tool for identifying and reporting on patterns -# found in ECMAScript/JavaScript code. -# More details at https://github.com/eslint/eslint -# and https://eslint.org - -name: ESLint - -on: - push: - branches: [ "master" ] - pull_request: - # The branches below must be a subset of the branches above - branches: [ "master" ] - schedule: - - cron: '36 22 * * 5' - -jobs: - eslint: - name: Run eslint scanning - runs-on: ubuntu-latest - permissions: - contents: read - security-events: write - actions: read # only required for a private repository by github/codeql-action/upload-sarif to get the Action run status - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install ESLint - run: | - yarn install eslint@8.10.0 - yarn install @microsoft/eslint-formatter-sarif@2.1.7 - - - name: Run ESLint - run: yarn run eslint . - --config .eslintrc.js - --ext .js,.jsx,.ts,.tsx - --format @microsoft/eslint-formatter-sarif - --output-file eslint-results.sarif - continue-on-error: true - - - name: Upload analysis results to GitHub - uses: github/codeql-action/upload-sarif@v2 - with: - sarif_file: eslint-results.sarif - wait-for-processing: true From 1ce208480f43d2bd8b68f3ed3c6a01d8bc333120 Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Mon, 19 Dec 2022 11:51:35 +0200 Subject: [PATCH 14/17] Update 2.feature_request.yml [skip ci] --- .github/ISSUE_TEMPLATE/2.feature_request.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/2.feature_request.yml b/.github/ISSUE_TEMPLATE/2.feature_request.yml index 6e941aca0..17de4e8d3 100644 --- a/.github/ISSUE_TEMPLATE/2.feature_request.yml +++ b/.github/ISSUE_TEMPLATE/2.feature_request.yml @@ -13,6 +13,7 @@ body: - 'Campaigns' - 'Donations' - 'Contacts' + - 'Blog' - 'SEO & Metadata' - 'Administration' - 'Authentication' From eda59f7ab13e7fb1ac5a942d9efa7edfb96618af Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Mon, 19 Dec 2022 11:51:51 +0200 Subject: [PATCH 15/17] Update 1.bug_report.yml [skip ci] --- .github/ISSUE_TEMPLATE/1.bug_report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/1.bug_report.yml b/.github/ISSUE_TEMPLATE/1.bug_report.yml index e361ac946..bd28439be 100644 --- a/.github/ISSUE_TEMPLATE/1.bug_report.yml +++ b/.github/ISSUE_TEMPLATE/1.bug_report.yml @@ -13,6 +13,7 @@ body: - 'Campaigns' - 'Donations' - 'Contacts' + - 'Blog' - 'SEO & Metadata' - 'Administration' - 'Authentication' From b85e4f21229583ea8f4caa4f527461b375d90534 Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Mon, 19 Dec 2022 19:12:17 +0200 Subject: [PATCH 16/17] Fetch more donation data from api and display it in the pdf --- src/components/pdf/Certificate.tsx | 45 ++++++++++--------- src/gql/donations.d.ts | 16 ++++++- .../api/pdf/certificate/[donationId].tsx | 21 +-------- 3 files changed, 39 insertions(+), 43 deletions(-) diff --git a/src/components/pdf/Certificate.tsx b/src/components/pdf/Certificate.tsx index e0fe93c6a..46d05b21e 100644 --- a/src/components/pdf/Certificate.tsx +++ b/src/components/pdf/Certificate.tsx @@ -2,10 +2,8 @@ import { Document, Page, StyleSheet, Text, View, Font, Image } from '@react-pdf/ import Logo from './Logo' import { DonationResponse } from 'gql/donations' -import { PersonResponse } from 'gql/person' import { formatDateString } from 'common/util/date' import { money } from 'common/util/money' -import { CampaignResponse } from 'gql/campaigns' Font.register({ family: 'Arial', @@ -23,7 +21,6 @@ const styles = StyleSheet.create({ position: 'absolute', height: '100%', width: '100%', - display: 'block', }, heading: { fontSize: '36', @@ -53,37 +50,44 @@ const styles = StyleSheet.create({ }, donationText: { textAlign: 'center', - marginTop: '50', + paddingTop: '5', width: '300', alignSelf: 'center', fontSize: '16', }, + donationRow: { + textAlign: 'center', + color: '#2A4E84', + fontSize: '14', + width: '450', + height: '55', + alignSelf: 'center', + }, dateAndSignView: { display: 'flex', alignItems: 'center', flexDirection: 'row', justifyContent: 'space-around', - marginTop: '15', }, date: { - marginTop: '25', + marginTop: '10', marginLeft: '50', }, dateText: { - marginTop: '2', + marginTop: '5', marginLeft: '50', fontSize: '11', }, members: { fontSize: '11', - marginTop: '71', + marginTop: '60', marginRight: '60', marginLeft: '50', }, signs: { position: 'absolute', left: '350', - top: '20', + top: '5', display: 'flex', justifyContent: 'space-around', flexDirection: 'row', @@ -107,12 +111,8 @@ const styles = StyleSheet.create({ type Props = { donation: DonationResponse - campaign: CampaignResponse - person?: PersonResponse } -export default function Certificate({ donation, person, campaign }: Props) { - const name = `${person?.firstName} ${person?.lastName}` - const formattedDate = formatDateString(donation.createdAt) +export default function Certificate({ donation }: Props) { return ( @@ -123,20 +123,23 @@ export default function Certificate({ donation, person, campaign }: Props) { за дарение № {donation.id.slice(0, 2)} - С този сертификат Управителният съвет на Сдружение - „Подкрепи БГ“ удостоверява, че: - {name} + С този сертификат Управителният съвет на + Сдружение „Подкрепи БГ“ удостоверява, че: + + {donation.person?.firstName} {donation.person?.lastName} + - + дари сума в размер на{' '} - {money(donation?.amount ?? 0)} за кампания{' '} - {campaign.title} + {money(donation?.amount ?? 0)} + за кампания: + {donation?.targetVault?.campaign?.title ?? '-'} - {formattedDate} + {formatDateString(donation.createdAt)} Дата diff --git a/src/gql/donations.d.ts b/src/gql/donations.d.ts index 0e40b7d5d..dc68c3a9b 100644 --- a/src/gql/donations.d.ts +++ b/src/gql/donations.d.ts @@ -40,8 +40,20 @@ export type DonationResponse = { currency: Currency amount: number personId?: UUID - person?: { firsName: string; lastName: string } - targetVault: { name: string } + person?: { + id: string + firstName: string + lastName: string + } + targetVault?: { + id: string + name: string + campaign?: { + id: string + slug: string + title: string + } + } } export type UserDonationResponse = DonationResponse & { diff --git a/src/pages/api/pdf/certificate/[donationId].tsx b/src/pages/api/pdf/certificate/[donationId].tsx index 25614fd57..c2d0b37d9 100644 --- a/src/pages/api/pdf/certificate/[donationId].tsx +++ b/src/pages/api/pdf/certificate/[donationId].tsx @@ -1,11 +1,8 @@ import { NextApiHandler, NextApiRequest, NextApiResponse } from 'next' import { getToken } from 'next-auth/jwt' -import { AxiosResponse } from 'axios' import { renderToStream } from '@react-pdf/renderer' import { UserDonationResponse } from 'gql/donations' -import { VaultResponse } from 'gql/vault' -import { CampaignResponse } from 'gql/campaigns' import { apiClient } from 'service/apiClient' import { endpoints } from 'service/apiEndpoints' import { authConfig } from 'service/restRequests' @@ -25,26 +22,10 @@ const Handler: NextApiHandler = async (req: NextApiRequest, res: NextApiResponse authConfig(jwt?.accessToken), ) - const { data: campaign } = await apiClient - .get( - endpoints.vaults.getVault(donation.targetVaultId).url, - authConfig(jwt?.accessToken), - ) - .then((res) => { - const campaignPromise: Promise> = - apiClient.get( - endpoints.campaign.viewCampaignById(res.data.campaignId).url, - authConfig(jwt?.accessToken), - ) - return campaignPromise - }) - if (!donation) { res.status(404).json({ notFound: true }) } else { - const pdfStream = await renderToStream( - , - ) + const pdfStream = await renderToStream() res.setHeader('Content-Type', 'application/pdf') pdfStream.pipe(res) } From ee4a59570cbc071fbf8c9cab16c4b79b3d038312 Mon Sep 17 00:00:00 2001 From: Ilko Kacharov Date: Mon, 19 Dec 2022 20:03:13 +0200 Subject: [PATCH 17/17] Remove vault name from response type --- src/gql/donations.d.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/gql/donations.d.ts b/src/gql/donations.d.ts index dc68c3a9b..05cc9f858 100644 --- a/src/gql/donations.d.ts +++ b/src/gql/donations.d.ts @@ -47,7 +47,6 @@ export type DonationResponse = { } targetVault?: { id: string - name: string campaign?: { id: string slug: string