Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 46 additions & 12 deletions src/components/donation-flow/DonationFlowForm.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react'
import React, { useEffect, useRef } from 'react'
import { useRouter } from 'next/router'
import { useSession } from 'next-auth/react'
import { useElements, useStripe } from '@stripe/react-stripe-js'
import * as yup from 'yup'
import { Form, Formik } from 'formik'
import { Form, Formik, FormikProps } from 'formik'
import { PersistFormikValues } from 'formik-persist-values'
import {
Alert,
Box,
Button,
Hidden,
Expand All @@ -17,12 +18,17 @@ import {
} from '@mui/material'
import { ArrowBack, Info } from '@mui/icons-material'

import { useCreateStripePayment, useUpdatePaymentIntent } from 'service/donation'
import theme from 'common/theme'
import { routes } from 'common/routes'
import CheckboxField from 'components/common/form/CheckboxField'
import AcceptPrivacyPolicyField from 'components/common/form/AcceptPrivacyPolicyField'
import ConfirmationDialog from 'components/common/ConfirmationDialog'
import SubmitButton from 'components/common/form/SubmitButton'
import {
useCancelPaymentIntent,
useCreateStripePayment,
useUpdatePaymentIntent,
} from 'service/donation'

import StepSplitter from './common/StepSplitter'
import PaymentMethod from './steps/payment-method/PaymentMethod'
Expand Down Expand Up @@ -86,21 +92,31 @@ export const validationSchema: yup.SchemaOf<DonationFormData> = yup
})

export function DonationFlowForm() {
const formikRef = useRef<FormikProps<DonationFormData> | null>(null)
const { i18n } = useTranslation()
const { data: session } = useSession()
useEffect(() => {
if (session?.user) {
formikRef.current?.setFieldValue('email', session.user.email)
return
}
formikRef.current?.setFieldValue('email', '')
}, [session])
const { campaign, stripePaymentIntent, paymentError, setPaymentError } = useDonationFlow()
const stripe = useStripe()
const elements = useElements()
const router = useRouter()
const createStripePaymentMutation = useCreateStripePayment()
const updatePaymentIntentMutation = useUpdatePaymentIntent()
const cancelPaymentIntentMutation = useCancelPaymentIntent()
const paymentMethodSectionRef = React.useRef<HTMLDivElement>(null)
const authenticationSectionRef = React.useRef<HTMLDivElement>(null)
const [showCancelDialog, setShowCancelDialog] = React.useState(false)
const [submitPaymentLoading, setSubmitPaymentLoading] = React.useState(false)

return (
<Formik
innerRef={formikRef}
initialValues={{
...initialValues,
email: session?.user?.email ?? '',
Expand Down Expand Up @@ -199,12 +215,22 @@ export function DonationFlowForm() {
autoComplete="off">
<ConfirmationDialog
isOpen={showCancelDialog}
handleCancel={() => router.push(routes.campaigns.viewCampaignBySlug(campaign.slug))}
handleCancel={() => {
cancelPaymentIntentMutation.mutate({
id: stripePaymentIntent.id,
payload: {
cancellation_reason: 'requested_by_customer',
},
})
router.push(routes.campaigns.viewCampaignBySlug(campaign.slug))
}}
title="Наистина ли искате да откажете дарението?"
content="Така ще изгубите всички въведени данни."
confirmButtonLabel="Продължи дарението"
cancelButtonLabel="Откажи дарението"
handleConfirm={() => setShowCancelDialog(false)}
handleConfirm={() => {
setShowCancelDialog(false)
}}
/>
<Button
size="large"
Expand Down Expand Up @@ -246,8 +272,6 @@ export function DonationFlowForm() {
}
name="isAnonymous"
/>
{/* TODO: Handle the possible API and Stripe Errors */}
{/* {JSON.stringify(paymentError)} */}
<AcceptPrivacyPolicyField name="privacy" />
<Hidden mdUp>
<PaymentSummaryAlert
Expand All @@ -259,16 +283,26 @@ export function DonationFlowForm() {
/>
</Hidden>

<Stack direction={'column'}>
{paymentError ? (
<Alert sx={{ fontSize: theme.typography.fontSize, mb: 1 }} severity="error">
{paymentError.message}
</Alert>
) : null}
</Stack>
<SubmitButton
disabled={submitPaymentLoading || !isValid}
loading={submitPaymentLoading}
disabled={!isValid || submitPaymentLoading}
label="Donate"
fullWidth
/>
<Stack direction={'column'}>
<Box>{paymentError?.message}</Box>
</Stack>
<PersistFormikValues debounce={3000} storage="sessionStorage" name="donation-form" />
<PersistFormikValues
hashInitials={true}
ignoreValues={['authentication']}
debounce={3000}
storage="sessionStorage"
name="donation-form"
/>
</Form>
</Grid2>
<Hidden mdDown>
Expand Down
207 changes: 125 additions & 82 deletions src/components/donation-flow/DonationFlowStatusPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { createDonationWish } from 'service/donationWish'
import { AlertStore } from 'stores/AlertStore'
import { useCurrentPerson } from 'common/util/useCurrentPerson'
import { CampaignResponse } from 'gql/campaigns'
import FailGraphic from './icons/FailGraphic'

function LinkCard({ href, text }: { href: string; text: string }) {
return (
Expand Down Expand Up @@ -79,7 +80,7 @@ export default function DonationFlowStatusPage({ slug }: { slug: string }) {
if (bank_payment === 'true') {
// If we are redirected on that page means that the payment is a bank payment and we can clear the form state
sessionStorage.removeItem('donation-form')
setStatus(DonationFormPaymentStatus.SUCCEEDED)
setStatus(DonationFormPaymentStatus.REQUIRES_PAYMENT)
return
}
if (!stripe || !payment_intent_client_secret) {
Expand Down Expand Up @@ -108,90 +109,132 @@ export default function DonationFlowStatusPage({ slug }: { slug: string }) {
})
}, [])

return (
<DonationFlowLayout campaign={campaign}>
{status === DonationFormPaymentStatus.SUCCEEDED ? (
<Box>
<Typography textAlign="center" variant="h4" mb={1}>
{session.data?.user?.name && ', благодарим ви, за доверието и подкрепата!'}
Благодарим ви, за доверието и подкрепата!
const Success = () => (
<Box>
<Typography textAlign="center" variant="h4" mb={1}>
{session.data?.user
? `${session.data?.user?.given_name} ${session.data.user.family_name}, благодарим ви, за доверието и подкрепата`
: 'Благодарим ви, за доверието и подкрепата'}
!
</Typography>
<Typography display="flex" justifyContent="center" alignItems="center">
<Email sx={{ mr: 1, fill: theme.palette.grey[400] }} />
Пратихме Ви и мейл с повече информация на адреса, който сте посочили.
</Typography>
<SuccessGraphic />
<Formik
initialValues={{
wish: '',
}}
onSubmit={(values) => {
createDonationWishMutate({
message: values.wish,
campaignId: campaign.id,
personId: person?.id ? person.id : null,
})
}}
validateOnMount
validateOnBlur
innerRef={formikRef}>
{({ handleSubmit }) => (
<Form onSubmit={handleSubmit}>
<Stack alignItems="flex-end" direction="column">
<Box width="100%">
<Typography variant="h5" mb={2} color={theme.palette.primary.dark}>
Помогнете на бенефициента/ите със добро пожелание:
</Typography>
<FormTextField
type="text"
name="wish"
label="Напишете пожелание..."
multiline
fullWidth
disabled={disableWishForm}
rows={7}
/>
</Box>
<SubmitButton
loading={isWishSendLoading}
disabled={disableWishForm || isWishSendLoading}
sx={{ mt: 1 }}
label="Изпрати"
/>
</Stack>
</Form>
)}
</Formik>
<StepSplitter />
<Stack alignItems="flex-end">
<Box width="100%">
<Typography variant="h5" mb={1}>
Подкрепи на кампания като споделиш с приятели.
</Typography>
<Typography display="flex" justifyContent="center" alignItems="center">
<Email sx={{ mr: 1, fill: theme.palette.grey[400] }} />
Пратихме Ви и мейл с повече информация на адреса, който сте посочили.
<Typography mb={1}>
Кампаниите сподели във вашите социални мрежи могат да съберат много повече средства
</Typography>
<SuccessGraphic />
<Formik
initialValues={{
wish: '',
}}
onSubmit={(values) => {
createDonationWishMutate({
message: values.wish,
campaignId: campaign.id,
personId: person?.id ? person.id : null,
})
}}
validateOnMount
validateOnBlur
innerRef={formikRef}>
{({ handleSubmit }) => (
<Form onSubmit={handleSubmit}>
<Stack alignItems="flex-end" direction="column">
<Box width="100%">
<Typography variant="h5" mb={2} color={theme.palette.primary.dark}>
Помогнете на бенефициента/ите със добро пожелание:
</Typography>
<FormTextField
type="text"
name="wish"
label="Напишете пожелание..."
multiline
fullWidth
disabled={disableWishForm}
rows={7}
/>
</Box>
<SubmitButton
loading={isWishSendLoading}
disabled={disableWishForm || isWishSendLoading}
sx={{ mt: 1 }}
label="Изпрати"
/>
</Stack>
</Form>
)}
</Formik>
<StepSplitter />
<Stack alignItems="flex-end">
<Box width="100%">
<Typography variant="h5" mb={1}>
Подкрепи на кампания като споделиш с приятели.
</Typography>
<Typography mb={1}>
Кампаниите сподели във вашите социални мрежи могат да съберат много повече средства
</Typography>
</Box>
<SocialShareListButton
url={`${window.location.host}${routes.campaigns.viewCampaignBySlug(slug)}`}
/>
</Stack>
<StepSplitter />
<Grid2 spacing={2} container>
<Grid2 xs={12} md={6}>
<LinkCard href={routes.campaigns.viewCampaignBySlug(slug)} text="Виж кампанията" />
</Grid2>
<Grid2 xs={12} md={6}>
<LinkCard href={routes.campaigns.index} text="Виж други кампании" />
</Grid2>
<Grid2 xs={12} md={6}>
<LinkCard href={routes.profile.index} text="Твоите дарения" />
</Grid2>
<Grid2 xs={12} md={6}>
<LinkCard href={routes.support} text="Стани доброволец" />
</Grid2>
</Grid2>
</Box>
<SocialShareListButton
url={`${window.location.host}${routes.campaigns.viewCampaignBySlug(slug)}`}
/>
</Stack>
<StepSplitter />
<Grid2 spacing={2} container>
<Grid2 xs={12} md={6}>
<LinkCard href={routes.campaigns.viewCampaignBySlug(slug)} text="Виж кампанията" />
</Grid2>
<Grid2 xs={12} md={6}>
<LinkCard href={routes.campaigns.index} text="Виж други кампании" />
</Grid2>
<Grid2 xs={12} md={6}>
<LinkCard href={routes.profile.index} text="Твоите дарения" />
</Grid2>
<Grid2 xs={12} md={6}>
<LinkCard href={routes.support} text="Стани доброволец" />
</Grid2>
</Grid2>
</Box>
)

const Fail = () => (
<Box>
<Typography textAlign="center" variant="h4" mb={1}>
Нещо се обърка, молим Ви да опитате отново!
</Typography>
{/* TODO: Provide a better <FailGraphic> instead of just an X */}
<FailGraphic
sx={{
width: '100%',
height: 'auto',
maxHeight: '220px',
my: 2,
}}
/>
<StepSplitter />
<Grid2 spacing={2} container>
<Grid2 xs={12} md={6}>
<LinkCard href={routes.campaigns.donation(slug)} text="Опитай пак" />
</Grid2>
<Grid2 xs={12} md={6}>
<LinkCard
href={routes.campaigns.viewCampaignBySlug(slug)}
text="Върни се към капманията"
/>
</Grid2>
</Grid2>
</Box>
)

const StatusToRender = () =>
status === DonationFormPaymentStatus.SUCCEEDED ? (
<Success />
) : status === DonationFormPaymentStatus.REQUIRES_PAYMENT ? (
<Fail />
) : null

return (
<DonationFlowLayout campaign={campaign}>
{status ? (
<StatusToRender />
) : (
<Box height="calc(100vh - 88px)" display="flex" justifyContent="center" alignItems="center">
<CircularProgress size={100} />
Expand Down
29 changes: 29 additions & 0 deletions src/components/donation-flow/icons/FailGraphic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { SvgIcon, SvgIconProps } from '@mui/material'
import theme from 'common/theme'

function FailGraphic(props: SvgIconProps) {
return (
<SvgIcon
width="194"
height="194"
viewBox="0 0 194 194"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}>
<path
d="M96.565 193.13C149.896 193.13 193.13 149.896 193.13 96.565C193.13 43.2336 149.896 0 96.565 0C43.2336 0 0 43.2336 0 96.565C0 149.896 43.2336 193.13 96.565 193.13Z"
fill={theme.palette.warning.dark}
/>
<path
d="M75.7053 70.0485L70.0485 75.7053L117.425 123.081L123.081 117.425L75.7053 70.0485Z"
fill="white"
/>
<path
d="M123.081 75.7054L117.425 70.0486L70.0484 117.425L75.7053 123.082L123.081 75.7054Z"
fill="white"
/>
</SvgIcon>
)
}

export default FailGraphic
Loading