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

Mark user as Coordinator, Organizer and/or Beneficiary #1595

Merged
merged 6 commits into from
Sep 20, 2023
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
7 changes: 6 additions & 1 deletion public/locales/bg/person.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,12 @@
"address": "Адрес",
"organizer": "Организатор",
"coordinator": "Координатор",
"beneficiary": "Бенефициент"
"beneficiary": "Бенефициент",
"organizerRelation": "Отношения с организатор",
"countryCode": "Държава",
"city": "Град",
"description": "Описание",
"disabled-tooltip": "Ролята не може да бъде премахната, потребителя е {{role}} в {{campaigns}} кампании"
},
"cta": {
"create": "Създай",
Expand Down
7 changes: 6 additions & 1 deletion public/locales/en/person.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@
"address": "Address",
"organizer": "Organizer",
"coordinator": "Coordinator",
"beneficiary": "Beneficiary"
"beneficiary": "Beneficiary",
"organizerRelation": "Organizer relation",
"countryCode": "Country code",
"city": "City",
"description": "Description",
"disabled-tooltip": "Cannot remove role because the user is {{role}} in {{campaigns}} campaigns"
},
"cta": {
"create": "Create",
Expand Down
2 changes: 1 addition & 1 deletion src/components/admin/organizers/grid/DeleteModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default observer(function DeleteModal() {
const { hideDelete, selectedRecord } = ModalStore

const mutation = useMutation<AxiosResponse<OrganizerResponse>, AxiosError<ApiErrors>, string>({
mutationFn: deleteOrganizer(selectedRecord.id),
mutationFn: deleteOrganizer(),
onError: () => AlertStore.show(t('admin.alerts.delete-error'), 'error'),
onSuccess: () => {
hideDelete()
Expand Down
36 changes: 30 additions & 6 deletions src/components/common/form/CheckboxField.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,48 @@
import { ChangeEvent } from 'react'

import { useField } from 'formik'
import { useTranslation } from 'next-i18next'
import { Checkbox, FormControl, FormControlLabel, FormHelperText } from '@mui/material'
import { Checkbox, FormControl, FormControlLabel, FormHelperText, Tooltip } from '@mui/material'

import { TranslatableField, translateError } from 'common/form/validation'

export type CheckboxFieldProps = {
name: string
disabled?: boolean
onChange?: (e: ChangeEvent<HTMLInputElement>) => void
label: string | number | React.ReactElement
disabledTooltip?: string
}

export default function CheckboxField({ name, label }: CheckboxFieldProps) {
export default function CheckboxField({
name,
disabled,
onChange: handleChange,
label,
disabledTooltip,
}: CheckboxFieldProps) {
const { t } = useTranslation()
const [field, meta] = useField(name)
const helperText = meta.touched ? translateError(meta.error as TranslatableField, t) : ''
return (
<FormControl required component="fieldset" error={Boolean(meta.error) && Boolean(meta.touched)}>
<FormControlLabel
label={typeof label === 'string' ? `${t(label)}` : label}
control={<Checkbox color="primary" checked={Boolean(field.value)} {...field} />}
/>
<Tooltip title={disabled && disabledTooltip} arrow>
<FormControlLabel
label={typeof label === 'string' ? `${t(label)}` : label}
control={
<Checkbox
color="primary"
checked={Boolean(field.value)}
disabled={disabled}
{...field}
onChange={(e) => {
field.onChange(e)
if (handleChange) handleChange(e)
}}
/>
}
/>
</Tooltip>
{Boolean(meta.error) && <FormHelperText error>{helperText}</FormHelperText>}
</FormControl>
)
Expand Down
4 changes: 4 additions & 0 deletions src/components/common/person/PersonForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const validationSchema: yup.SchemaOf<PersonFormData> = yup.object().defined().sh
companyNumber: yup.string(),
legalPersonName: name,
address: yup.string(),
// Roles
isBeneficiary: yup.bool().notRequired(),
isCoordinator: yup.bool().notRequired(),
isOrganizer: yup.bool().notRequired(),
})

const defaults: PersonFormData = {
Expand Down
220 changes: 220 additions & 0 deletions src/components/common/person/grid/CreateForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import React, { useState } from 'react'
import * as yup from 'yup'
import { Grid } from '@mui/material'

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 } from 'gql/person'
import { useMutation } from '@tanstack/react-query'
import { AxiosError, AxiosResponse } from 'axios'
import { ApiErrors } from 'service/apiErrors'
import { useCreatePerson } from 'service/person'
import { AlertStore } from 'stores/AlertStore'
import { useRouter } from 'next/router'
import { useTranslation } from 'next-i18next'
import { routes } from 'common/routes'
import CheckboxField from 'components/common/form/CheckboxField'
import { useCreateCoordinator } from 'service/coordinator'
import { CoordinatorResponse, CoorinatorInput } from 'gql/coordinators'
import { createOrganizer } from 'service/organizer'
import { OrganizerInput } from 'gql/organizer'
import { BeneficiaryFormData, BeneficiaryListResponse } from 'gql/beneficiary'
import { BeneficiaryType } from 'components/admin/beneficiary/BeneficiaryTypes'
import { useCreateBeneficiary } from 'service/beneficiary'
import OrganizerRelationSelect from 'components/admin/beneficiary/OrganizerRelationSelect'
import CountrySelect from 'components/admin/countries/CountrySelect'
import CitySelect from 'components/admin/cities/CitySelect'

const validationSchema = yup
.object()
.defined()
.shape({
firstName: name.required(),
lastName: name.required(),
email: email.required(),
phone: phone.notRequired(),
isCoordinator: yup.bool().required(),
isOrganizer: yup.bool().required(),
isBeneficiary: yup.bool().required(),
description: yup.string().notRequired(),
cityId: yup.string().when('isBeneficiary', {
is: true,
then: yup.string().required(),
otherwise: yup.string().notRequired(),
}),
countryCode: yup.string().when('isBeneficiary', {
is: true,
then: yup.string().required(),
otherwise: yup.string().notRequired(),
}),
organizerRelation: yup.string().when('isBeneficiary', {
is: true,
then: yup.string().required(),
otherwise: yup.string().notRequired(),
}),
})

const initialValues: AdminPersonFormData = {
firstName: '',
lastName: '',
email: '',
phone: '',
isOrganizer: false,
isCoordinator: false,
isBeneficiary: false,
countryCode: 'BG',
cityId: '',
description: '',
organizerRelation: 'none',
}

export default function CreateForm() {
const router = useRouter()
const { t } = useTranslation()
const [showBenefactor, setShowBenefactor] = useState<boolean>(false)

const mutation = useMutation<
AxiosResponse<AdminPersonResponse>,
AxiosError<ApiErrors>,
AdminPersonFormData
>({
mutationFn: useCreatePerson(),
onError: () => AlertStore.show(t('common:alerts.error'), 'error'),
})

const coordinatorCreateMutation = useMutation<
AxiosResponse<CoordinatorResponse>,
AxiosError<ApiErrors>,
CoorinatorInput
>({
mutationFn: useCreateCoordinator(),
onError: () => AlertStore.show(t('common:alerts.error'), 'error'),
})

const organizerCreateMutation = useMutation<
AxiosResponse<CoordinatorResponse>,
AxiosError<ApiErrors>,
OrganizerInput
>({
mutationFn: createOrganizer(),
onError: () => AlertStore.show(t('common:alerts.error'), 'error'),
})

const beneficiaryCreateMutation = useMutation<
AxiosResponse<BeneficiaryListResponse>,
AxiosError<ApiErrors>,
BeneficiaryFormData
>({
mutationFn: useCreateBeneficiary(),
onError: () => AlertStore.show(t('common:alerts.error'), 'error'),
})

async function handleSubmit(values: AdminPersonFormData) {
const { data: userResponse } = await mutation.mutateAsync(values)

if (values.isCoordinator) coordinatorCreateMutation.mutate({ personId: userResponse.id })

if (values.isOrganizer) organizerCreateMutation.mutate({ personId: userResponse.id })

if (values.isBeneficiary)
beneficiaryCreateMutation.mutate({
type: BeneficiaryType.individual,
personId: userResponse.id,
countryCode: values.countryCode,
cityId: values.cityId,
organizerRelation: values.organizerRelation,
description: values.description,
campaigns: [],
})

AlertStore.show(t('common:alerts.success'), 'success')
router.push(routes.admin.person.index)
}

return (
<Grid container direction="column" component="section">
<GenericForm
onSubmit={handleSubmit}
initialValues={initialValues}
validationSchema={validationSchema}>
<Grid container spacing={3}>
<Grid item xs={12} sm={6}>
<FormTextField
autoFocus
type="text"
label="person:admin.fields.first-name"
name="firstName"
autoComplete="first-name"
/>
</Grid>
<Grid item xs={12} sm={6}>
<FormTextField
type="text"
label="person:admin.fields.last-name"
name="lastName"
autoComplete="family-name"
/>
</Grid>
<Grid item xs={12}>
<EmailField label="person:admin.fields.email" name="email" />
</Grid>
<Grid item xs={12}>
<FormTextField
type="tel"
name="phone"
inputMode="tel"
autoComplete="tel"
label="person:admin.fields.phone"
/>
</Grid>
<Grid item xs={4}>
<CheckboxField name="isOrganizer" label="person:admin.fields.organizer" />
</Grid>
<Grid item xs={4}>
<CheckboxField name="isCoordinator" label="person:admin.fields.coordinator" />
</Grid>
<Grid item xs={4}>
<CheckboxField
name="isBeneficiary"
label="person:admin.fields.beneficiary"
onChange={(e) => {
setShowBenefactor(e.target.checked)
}}
/>
</Grid>
{showBenefactor && (
<>
<Grid item xs={12}>
<OrganizerRelationSelect
name="organizerRelation"
label="person:admin.fields.organizerRelation"
/>
</Grid>
<Grid item xs={6}>
<CountrySelect />
</Grid>
<Grid item xs={6}>
<CitySelect name="cityId" />
</Grid>
<Grid item xs={12}>
<FormTextField
type="text"
name="description"
label="person:admin.fields.description"
multiline
rows={2}
/>
</Grid>
</>
)}
<Grid item xs={4} margin="auto">
<SubmitButton fullWidth label="person:admin.cta.create" />
</Grid>
</Grid>
</GenericForm>
</Grid>
)
}
4 changes: 2 additions & 2 deletions src/components/common/person/grid/CreatePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Container } from '@mui/material'

import AdminLayout from 'components/common/navigation/AdminLayout'
import AdminContainer from 'components/common/navigation/AdminContainer'
import PersonForm from './PersonForm'
import CreateForm from './CreateForm'

export default function CreatePage() {
const { t } = useTranslation()
Expand All @@ -13,7 +13,7 @@ export default function CreatePage() {
<AdminLayout>
<AdminContainer title={t('person:admin.headings.create')}>
<Container maxWidth="md" sx={{ py: 5 }}>
<PersonForm />
<CreateForm />
</Container>
</AdminContainer>
</AdminLayout>
Expand Down
Loading
Loading