Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create campaign application files #1916

Merged
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
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
Loading