Skip to content

Commit

Permalink
feat: campaign application fixes
Browse files Browse the repository at this point in the history
- move the field cause (aka goal - "за какво са нужни средствата") down as per request from team campaigns
- add a "at least one document" hint to the document
- make the beneficiary relationship required (as it is on the API side)
- add validation error for not accepted terms
- add validation for missing document
- reset touched state after first step b/c that first step's submit makes all fields on steps 2 and 3 touched and shows errors immediately
  • Loading branch information
gparlakov committed Jan 18, 2025
1 parent 01c2d1e commit 50b7ea7
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 20 deletions.
3 changes: 2 additions & 1 deletion public/locales/bg/campaign-application.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
},
"photos": "Снимков/видео материал",
"documents": "Документи",
"disclaimer": "Приемат се до 10 документа. Всеки от документите може да бъде до 30МБ"
"disclaimer": "Приемат се до 10 документа. Всеки от документите може да бъде до 30МБ",
"documents-hint": "Моля, приложете поне един документ в подкрепа на нуждата ви, като например: медицинска документация, оферти за ремонт, обучение или лечение, писма или становища от институции, детайлен бюджет, оферти за необходими артикули или услуги, както и други документи, свързани с каузата ви."
},
"admin": {
"title": "Администраторска редакция",
Expand Down
3 changes: 2 additions & 1 deletion public/locales/en/campaign-application.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
},
"photos": "Photo/Video material",
"documents": "Documents",
"disclaimer": "Up to 10 documents accepted. Each of the documents can be up to 30MB is size"
"disclaimer": "Up to 10 documents accepted. Each of the documents can be up to 30MB is size",
"documents-hint": "Please attach at least one document to stake your claim like: medical documents, offers for repair learning or healthcare, letters from institutions, detailed budget, offers for required items or services, as well as any other documents to do with your cause."
},
"admin": {
"title": "Admin edit",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export default function CampaignApplicationForm({

const handleSubmit = async (
formData: CampaignApplicationFormData,
{ resetForm }: FormikHelpers<CampaignApplicationFormData>,
{ resetForm, setTouched }: FormikHelpers<CampaignApplicationFormData>,
) => {
if (activeStep === Steps.CREATED_DETAILS && camApp?.id != null) {
router.push(routes.campaigns.applicationEdit(camApp?.id)) // go to the edit page
Expand All @@ -100,6 +100,26 @@ export default function CampaignApplicationForm({
}
} else {
setActiveStep((prevActiveStep) => prevActiveStep + 1)

// reset the touched state b/c every step gets "submitted" in terms of formik and that in turn marks all values as touched
// the effect is that every step after the first immediately marks all required fields as errored even before the user has touched them
setTouched({
applicationDetails: {
description: false,
currentStatus: false,
cause: false,
documents: false,
},
applicationBasic: {
beneficiaryNames: false,
title: false,
campaignType: false,
funds: false,
campaignEnd: false,
campaignEndDate: false,
organizerBeneficiaryRelationship: false,
},
})
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ export type CampaignApplicationBasic = {
funds: number
campaignEnd: string
campaignEndDate?: string
organizerBeneficiaryRelationship?: string
}

export type CampaignApplicationDetails = {
cause: string
organizerBeneficiaryRelationship?: string
description?: string
currentStatus?: string
documents?: File[]
}

// keep in sync with api repo/podkrepi.dbml -> Enum CampaignApplicationState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,16 @@ const basicSchema: yup.SchemaOf<CampaignApplicationBasic> = yup.object().shape({
funds: yup.number().required(),
title: yup.string().required(),
campaignEndDate: yup.string().optional(),
organizerBeneficiaryRelationship: yup.string().required(),
})

const detailsSchema: yup.SchemaOf<CampaignApplicationDetails> = yup.object().shape({
cause: yup.string().required(),
campaignGuarantee: yup.string().optional(),
currentStatus: yup.string().optional(),
description: yup.string().optional(),
documents: yup.array().optional(),
documents: yup.array().min(1, 'documents-hint').required(),
links: yup.array().optional(),
organizerBeneficiaryRelationship: yup.string().optional(),
otherFinancialSources: yup.string().optional(),
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export default function CampaignApplicationBasic() {
<StyledFormTextField
label={t('steps.application.beneficiaryRelationship')}
type="text"
name="applicationDetails.organizerBeneficiaryRelationship"
name="applicationBasic.organizerBeneficiaryRelationship"
/>
</Grid>
<Grid item xs={12}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { Grid, Typography } from '@mui/material'
import { FormControl, FormHelperText, Grid, Typography } from '@mui/material'
import { useTranslation } from 'next-i18next'

import FormTextField from 'components/common/form/FormTextField'
import { StyledStepHeading } from '../helpers/campaignApplication.styled'

import FileList from 'components/common/file-upload/FileList'
import FileUpload from 'components/common/file-upload/FileUpload'
import { Dispatch, SetStateAction } from 'react'
import { Dispatch, SetStateAction, useEffect } from 'react'
import { useFormikContext } from 'formik'
import { CampaignApplicationFormData } from '../helpers/campaignApplication.types'

export type Props = {
files: File[]
Expand All @@ -15,6 +17,11 @@ export type Props = {

export default function CampaignApplicationDetails({ files, setFiles }: Props) {
const { t } = useTranslation('campaign-application')
const { setFieldValue, errors, touched } = useFormikContext<CampaignApplicationFormData>()

useEffect(() => {
setFieldValue('applicationDetails.documents', files, false)
}, [files])

return (
<Grid container spacing={6} justifyContent="center" direction="column" alignContent="center">
Expand Down Expand Up @@ -51,13 +58,20 @@ export default function CampaignApplicationDetails({ files, setFiles }: Props) {
/>
</Grid>
<Grid item xs={12}>
<FileUpload
buttonLabel={t('steps.details.documents')}
onUpload={(newFiles) => {
setFiles((prevFiles) => [...prevFiles, ...newFiles])
}}
accept="text/plain,application/json,application/pdf,image/png,image/jpeg,application/xml,text/xml,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
/>
<FormControl>
<Typography variant="h6">{t('steps.details.documents-hint')}</Typography>
<FileUpload
buttonLabel={t('steps.details.documents')}
onUpload={(newFiles) => {
setFiles((prevFiles) => [...prevFiles, ...newFiles])
setFieldValue('applicationDetails.documents', newFiles)
}}
accept="text/plain,application/json,application/pdf,image/png,image/jpeg,application/xml,text/xml,application/msword,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
/>
{touched.applicationDetails?.documents && errors.applicationDetails?.documents && (
<FormHelperText error>{t('steps.details.documents-hint')}</FormHelperText>
)}
</FormControl>
<Typography>{t('steps.details.disclaimer')}</Typography>
<FileList
files={files}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { useTranslation } from 'next-i18next'

import { FormControl, Grid, Typography } from '@mui/material'
import { FormControl, FormHelperText, Grid, Typography } from '@mui/material'

import CheckboxField from 'components/common/form/CheckboxField'
import AcceptTermsField from 'components/common/form/AcceptTermsField'
import AcceptPrivacyPolicyField from 'components/common/form/AcceptPrivacyPolicyField'

import { StyledStepHeading, StyledFormTextField } from '../helpers/campaignApplication.styled'
import { useFormikContext } from 'formik'
import { CampaignApplicationFormData } from '../helpers/campaignApplication.types'

type Props = {
isAdmin?: boolean
}

export default function CampaignApplicationOrganizer({ isAdmin }: Props) {
const { t } = useTranslation('campaign-application')
const { errors, touched } = useFormikContext<CampaignApplicationFormData>()

return (
<Grid container spacing={6} justifyContent="center" direction="column" alignContent="center">
Expand Down Expand Up @@ -68,6 +71,12 @@ export default function CampaignApplicationOrganizer({ isAdmin }: Props) {
name="organizer.personalInformationProcessingAccepted"
disabled={isAdmin}
/>
{touched.organizer?.personalInformationProcessingAccepted &&
errors.organizer?.personalInformationProcessingAccepted && (
<FormHelperText error>
{t(errors.organizer.personalInformationProcessingAccepted)}
</FormHelperText>
)}
</Grid>
</Grid>
</Grid>
Expand Down
2 changes: 1 addition & 1 deletion src/components/common/form/AcceptPrivacyPolicyField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { routes } from 'common/routes'
import ExternalLink from 'components/common/ExternalLink'
import CheckboxField, { CheckboxFieldProps } from 'components/common/form/CheckboxField'

export type AcceptGDPRFieldProps = {
export type AcceptGDPRFieldProps = Omit<CheckboxFieldProps, 'label'> & {
name: string
showFieldError?: boolean
disabled?: boolean
Expand Down
4 changes: 2 additions & 2 deletions src/service/campaign-application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,12 @@ export function mapExistingOrNew(
funds: isNaN(parseInt(existing?.amount ?? '')) ? 0 : parseInt(existing?.amount ?? '0'),
campaignEnd: existing?.campaignEnd ?? CampaignEndTypes.FUNDS,
campaignEndDate: existing?.campaignEndDate,
organizerBeneficiaryRelationship: existing?.organizerBeneficiaryRel ?? '',
},
applicationDetails: {
cause: existing?.goal ?? '',
currentStatus: existing?.history ?? '',
description: existing?.description ?? '',
organizerBeneficiaryRelationship: existing?.organizerBeneficiaryRel ?? '',
},
admin: {
archived: existing?.archived ?? false,
Expand All @@ -292,7 +292,7 @@ export function mapCreateOrEditInput(i: CampaignApplicationFormData): CampaignAp
amount: i.applicationBasic.funds?.toString() ?? '',
goal: i.applicationDetails.cause,
description: i.applicationDetails.description,
organizerBeneficiaryRel: i.applicationDetails.organizerBeneficiaryRelationship ?? '-',
organizerBeneficiaryRel: i.applicationBasic.organizerBeneficiaryRelationship ?? '-',
history: i.applicationDetails.currentStatus,
campaignEnd: i.applicationBasic.campaignEnd,
campaignEndDate: i.applicationBasic.campaignEndDate,
Expand Down

0 comments on commit 50b7ea7

Please sign in to comment.