Skip to content

Commit

Permalink
Create campaign application files (#1916)
Browse files Browse the repository at this point in the history
* feat: add files to create camApp

- camApp === campaign application

* fix: missing files state for application details

* fix: types fix
  • Loading branch information
gparlakov authored Sep 13, 2024
1 parent 4baaa42 commit 662b6f9
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 100 deletions.
3 changes: 3 additions & 0 deletions public/locales/bg/campaign-application.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,8 @@
"terms": "Общи условия ",
"faq": "Често задавани въпроси"
}
},
"alerts": {
"successfully-created": "Успешно създадена кампания. "
}
}
3 changes: 3 additions & 0 deletions public/locales/en/campaign-application.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,8 @@
"terms": "Terms and Conditions ",
"faq": "Frequently Asked Questions"
}
},
"alerts": {
"successfully-created": "Campaign application successfully created."
}
}
5 changes: 4 additions & 1 deletion src/components/admin/campaign-applications/EditPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import AdminContainer from 'components/common/navigation/AdminContainer'
import AdminLayout from 'components/common/navigation/AdminLayout'
import CampaignApplicationAdminPropsEdit from './CampaignApplicationAdminPropsEdit'
import { CampaignApplicationAdminEdit } from './campaignApplicationAdmin.types'
import { useState } from 'react'

export default function EditPage() {
const [files, setFiles] = useState<File[]>([])

const initialValues = {
organizer: {
name: 'Some organizer',
Expand All @@ -30,7 +33,7 @@ export default function EditPage() {
<div>.</div>
<CampaignApplication />
<div>.</div>
<CampaignApplicationDetails />
<CampaignApplicationDetails files={files} setFiles={setFiles} />
<div>.</div>
<CampaignApplicationAdminPropsEdit />
</GenericForm>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback, useState } from 'react'
import { Grid, StepLabel } from '@mui/material'
import { Person } from 'gql/person'
import { useCallback, useState } from 'react'

import {
CampaignApplicationFormData,
Expand All @@ -9,48 +9,49 @@ import {
} from './helpers/campaignApplication.types'

import GenericForm from 'components/common/form/GenericForm'
import CampaignApplicationStepperIcon from './CampaignApplicationStepperIcon'
import CampaignApplicationOrganizer from './steps/CampaignApplicationOrganizer'
import CampaignApplicationDetails from './steps/CampaignApplicationDetails'
import CampaignApplication from './steps/CampaignApplication'
import CampaignApplicationFormActions from './CampaignApplicationFormActions'
import CampaignApplicationRemark from './CampaignApplicationRemark'
import stepsHandler from './helpers/stepsHandler'
import CampaignApplicationStepperIcon from './CampaignApplicationStepperIcon'
import CampaignApplication from './steps/CampaignApplication'
import CampaignApplicationDetails from './steps/CampaignApplicationDetails'
import CampaignApplicationOrganizer from './steps/CampaignApplicationOrganizer'

import { validationSchema } from './helpers/validation-schema'

import {
StyledCampaignApplicationStep,
StyledCampaignApplicationStepper,
StyledStepConnector,
} from './helpers/campaignApplication.styled'
import { useMutation } from '@tanstack/react-query'
import { AxiosError, AxiosResponse, isAxiosError } from 'axios'
import { FormikHelpers } from 'formik'
import {
CreateCampaignApplicationInput,
CreateCampaignApplicationResponse,
UploadCampaignApplicationFilesRequest,
UploadCampaignApplicationFilesResponse,
} from 'gql/campaign-applications'
import { AxiosError, AxiosResponse, isAxiosError } from 'axios'
import { ApiErrors, matchValidator } from 'service/apiErrors'
import { useCreateCampaignApplication } from 'service/campaign-application'
import { AlertStore } from 'stores/AlertStore'
import { CampaignTypesResponse } from 'gql/campaign-types'
import { t } from 'i18next'
import { CampaignTypeCategory } from 'components/common/campaign-types/categories'
import { FormikHelpers } from 'formik'
import { useTranslation } from 'next-i18next'
import { ApiErrors, matchValidator } from 'service/apiErrors'
import {
useCreateCampaignApplication,
useUploadCampaignApplicationFiles,
} from 'service/campaign-application'
import { useCampaignTypesList } from 'service/campaignTypes'
import { CampaignTypesResponse } from 'gql/campaign-types'
import { AlertStore } from 'stores/AlertStore'
import {
StyledCampaignApplicationStep,
StyledCampaignApplicationStepper,
StyledStepConnector,
} from './helpers/campaignApplication.styled'

const steps: StepType[] = [
{
title: 'campaign-application:steps.organizer.title',
component: <CampaignApplicationOrganizer />,
},
{
title: 'campaign-application:steps.campaign-application.title',
component: <CampaignApplication />,
},
{
title: 'campaign-application:steps.campaign-application-details.title',
component: <CampaignApplicationDetails />,
},
]

Expand All @@ -59,8 +60,10 @@ type Props = {
}

export default function CampaignApplicationForm({ person }: Props) {
const { t } = useTranslation('campaign-application')
const [activeStep, setActiveStep] = useState<Steps>(Steps.ORGANIZER)
const isLast = activeStep === Steps.CAMPAIGN_DETAILS
const [submitting, setSubmitting] = useState(false)

const initialValues: CampaignApplicationFormData = {
organizer: {
Expand Down Expand Up @@ -90,18 +93,30 @@ export default function CampaignApplicationForm({ person }: Props) {
},
}

const [files, setFiles] = useState<File[]>([])

const { data } = useCampaignTypesList()
const { mutation } = useCreateApplication()
const create = useCreateApplication()
const handleSubmit = async (
formData: CampaignApplicationFormData,
{ setFieldError, resetForm }: FormikHelpers<CampaignApplicationFormData>,
) => {
if (isLast) {
if (submitting) {
return
}
setSubmitting(true)
try {
await mutation.mutateAsync(mapCreateInput(formData, data ?? []))
const createInput = mapCreateInput(formData, data ?? [])
await create(createInput, files)

resetForm()
setSubmitting(false)
AlertStore.show(t('alerts.successfully-created'), 'success')

// take user to next campaign application page
} catch (error) {
setSubmitting(false)
console.error(error)
if (isAxiosError(error)) {
const { response } = error as AxiosError<ApiErrors>
Expand All @@ -111,7 +126,7 @@ export default function CampaignApplicationForm({ person }: Props) {
}
}
} else {
stepsHandler({ activeStep, setActiveStep })
setActiveStep((prevActiveStep) => prevActiveStep + 1)
}
}

Expand All @@ -136,13 +151,18 @@ export default function CampaignApplicationForm({ person }: Props) {
</StyledCampaignApplicationStepper>
<Grid container>
<Grid container item xs={12}>
{activeStep < steps.length && steps[activeStep].component}
{activeStep === Steps.ORGANIZER && <CampaignApplicationOrganizer />}
{activeStep === Steps.CAMPAIGN && <CampaignApplication />}
{activeStep === Steps.CAMPAIGN_DETAILS && (
<CampaignApplicationDetails files={files} setFiles={setFiles} />
)}
</Grid>
<Grid container item alignContent="center">
<CampaignApplicationFormActions
activeStep={activeStep}
onBack={handleBack}
isLast={isLast}
submitting={submitting}
/>
</Grid>
</Grid>
Expand All @@ -155,7 +175,7 @@ export default function CampaignApplicationForm({ person }: Props) {
}

const useCreateApplication = () => {
const mutation = useMutation<
const create = useMutation<
AxiosResponse<CreateCampaignApplicationResponse>,
AxiosError<ApiErrors>,
CreateCampaignApplicationInput
Expand All @@ -165,15 +185,23 @@ const useCreateApplication = () => {
onSuccess: () => AlertStore.show(t('common:alerts.message-sent'), 'success'),
})

// const fileUploadMutation = useMutation<
// AxiosResponse<CampaignUploadImage[]>,
// AxiosError<ApiErrors>,
// UploadCampaignFiles
// >({
// mutationFn: useUploadCampaignFiles(),
// })
const fileUpload = useMutation<
AxiosResponse<UploadCampaignApplicationFilesResponse>,
AxiosError<ApiErrors>,
UploadCampaignApplicationFilesRequest
>({
mutationFn: useUploadCampaignApplicationFiles(),
})

return async (i: CreateCampaignApplicationInput, files: File[]) => {
const {
data: { id },
} = await create.mutateAsync(i)

return { mutation }
await fileUpload.mutateAsync({ campaignApplicationId: id, files })

return { id }
}
}

function mapCreateInput(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ type CampaignApplicationFormActionsProps = {
activeStep: number
onBack?: (event: MouseEvent) => void
isLast: boolean
submitting: boolean
}

export default function CampaignApplicationFormActions({
onBack,
activeStep,
isLast,
submitting,
}: CampaignApplicationFormActionsProps) {
const { t } = useTranslation('campaign-application')

Expand Down Expand Up @@ -51,6 +53,7 @@ export default function CampaignApplicationFormActions({
fullWidth
label={t(isLast ? 'cta.submit' : 'cta.next')}
endIcon={<ArrowForwardIosIcon fontSize="small" />}
disabled={submitting}
/>
</Grid>
</Root>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export type Step = {
title: string
component: JSX.Element
}

export enum Steps {
Expand Down
30 changes: 0 additions & 30 deletions src/components/client/campaign-application/helpers/stepsHandler.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import { useTranslation } from 'next-i18next'
import { Grid, Typography } from '@mui/material'
import { useTranslation } from 'next-i18next'

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

import theme from 'common/theme'
import FileList from 'components/common/file-upload/FileList'
import FileUpload from 'components/common/file-upload/FileUpload'
import { Dispatch, SetStateAction } from 'react'

export type Props = {
files: File[]
setFiles: Dispatch<SetStateAction<File[]>>
}

export default function CampaignApplicationDetails() {
export default function CampaignApplicationDetails({ files, setFiles }: Props) {
const { t } = useTranslation('campaign-application')

return (
Expand Down Expand Up @@ -85,18 +92,22 @@ export default function CampaignApplicationDetails() {
</Grid>
<Grid item xs={12}>
<FileUpload
buttonLabel={t('steps.details.photos')}
onUpload={(files) => {
return
buttonLabel={t('steps.details.documents')}
onUpload={(newFiles) => {
setFiles((prevFiles) => [...prevFiles, ...newFiles])
}}
/>
</Grid>
<Grid item xs={12}>
<FileUpload
buttonLabel={t('steps.details.documents')}
onUpload={(files) => {
return
<FileList
files={files}
onDelete={(deletedFile) =>
setFiles((prevFiles) => prevFiles.filter((file) => file.name !== deletedFile.name))
}
rolesList={{}}
onSetFileRole={() => {
// we have no roles for the campaign application - it's all a document
return undefined
}}
filesRole={[]}
/>
</Grid>
</Grid>
Expand Down
38 changes: 20 additions & 18 deletions src/components/common/file-upload/FileList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,26 @@ function FileList({ rolesList, files, onDelete, onSetFileRole, filesRole = [] }:
</Avatar>
</ListItemAvatar>
<ListItemText primary={file.name} />
<FormControl>
<InputLabel id="choose-type-label">{'Избери роля'}</InputLabel>
<Select<CampaignFileRole>
id="choose-type"
size="small"
label="Избери роля"
labelId="choose-type-label"
value={
filesRole.find((f) => f.file === file.name)?.role ?? CampaignFileRole.background
}
onChange={setFileRole(file)}>
{Object.values(rolesList).map((role) => (
<MenuItem key={role} value={role}>
{role}
</MenuItem>
))}
</Select>
</FormControl>
{Array.isArray(filesRole) && filesRole.length > 0 && (
<FormControl>
<InputLabel id="choose-type-label">{'Избери роля'}</InputLabel>
<Select<CampaignFileRole>
id="choose-type"
size="small"
label="Избери роля"
labelId="choose-type-label"
value={
filesRole.find((f) => f.file === file.name)?.role ?? CampaignFileRole.background
}
onChange={setFileRole(file)}>
{Object.values(rolesList).map((role) => (
<MenuItem key={role} value={role}>
{role}
</MenuItem>
))}
</Select>
</FormControl>
)}
</ListItem>
))}
</List>
Expand Down
Loading

0 comments on commit 662b6f9

Please sign in to comment.