| 1 | + | import React, { FunctionComponent, useState } from 'react'; |
| 2 | + | import { Field, Form, Formik } from 'formik'; |
| 3 | + | import Drawer from '@mui/material/Drawer'; |
| 4 | + | import Typography from '@mui/material/Typography'; |
| 5 | + | import Button from '@mui/material/Button'; |
| 6 | + | import IconButton from '@mui/material/IconButton'; |
| 7 | + | import makeStyles from '@mui/styles/makeStyles'; |
| 8 | + | import Fab from '@mui/material/Fab'; |
| 9 | + | import { Add, Close } from '@mui/icons-material'; |
| 10 | + | import * as Yup from 'yup'; |
| 11 | + | import { graphql } from 'react-relay'; |
| 12 | + | import { FormikHelpers } from 'formik/dist/types'; |
| 13 | + | import { ConnectionHandler, RecordProxy, RecordSourceSelectorProxy } from 'relay-runtime'; |
| 14 | + | import { useFormatter } from '../../../../components/i18n'; |
| 15 | + | import { commitMutation, handleErrorInForm } from '../../../../relay/environment'; |
| 16 | + | import TextField from '../../../../components/TextField'; |
| 17 | + | import CreatedByField, { CreatedBy } from '../../common/form/CreatedByField'; |
| 18 | + | import ObjectLabelField, { ObjectLabel } from '../../common/form/ObjectLabelField'; |
| 19 | + | import ObjectMarkingField, { ObjectMarking } from '../../common/form/ObjectMarkingField'; |
| 20 | + | import MarkDownField from '../../../../components/MarkDownField'; |
| 21 | + | import { Theme } from '../../../../components/Theme'; |
| 22 | + | import { PaginationOptions } from '../../../../components/list_lines'; |
| 23 | + | import ExternalReferencesField, { ExternalReference } from '../../common/form/ExternalReferencesField'; |
| 24 | + | |
| 25 | + | const styles = makeStyles<Theme>((theme) => ({ |
| 26 | + | drawerPaper: { |
| 27 | + | minHeight: '100vh', |
| 28 | + | width: '50%', |
| 29 | + | position: 'fixed', |
| 30 | + | transition: theme.transitions.create('width', { |
| 31 | + | easing: theme.transitions.easing.sharp, |
| 32 | + | duration: theme.transitions.duration.enteringScreen, |
| 33 | + | }), |
| 34 | + | padding: 0, |
| 35 | + | }, |
| 36 | + | dialogActions: { |
| 37 | + | padding: '0 17px 20px 0', |
| 38 | + | }, |
| 39 | + | createButton: { |
| 40 | + | position: 'fixed', |
| 41 | + | bottom: 30, |
| 42 | + | right: 30, |
| 43 | + | }, |
| 44 | + | createButtonContextual: { |
| 45 | + | position: 'fixed', |
| 46 | + | bottom: 30, |
| 47 | + | right: 30, |
| 48 | + | zIndex: 2000, |
| 49 | + | }, |
| 50 | + | buttons: { |
| 51 | + | marginTop: 20, |
| 52 | + | textAlign: 'right', |
| 53 | + | }, |
| 54 | + | button: { |
| 55 | + | marginLeft: theme.spacing(2), |
| 56 | + | }, |
| 57 | + | header: { |
| 58 | + | backgroundColor: theme.palette.background.nav, |
| 59 | + | padding: '20px 20px 20px 60px', |
| 60 | + | }, |
| 61 | + | closeButton: { |
| 62 | + | position: 'absolute', |
| 63 | + | top: 12, |
| 64 | + | left: 5, |
| 65 | + | color: 'inherit', |
| 66 | + | }, |
| 67 | + | importButton: { |
| 68 | + | position: 'absolute', |
| 69 | + | top: 15, |
| 70 | + | right: 20, |
| 71 | + | }, |
| 72 | + | container: { |
| 73 | + | padding: '10px 20px 20px 20px', |
| 74 | + | }, |
| 75 | + | })); |
| 76 | + | |
| 77 | + | const countryMutation = graphql` |
| 78 | + | mutation CountryCreationMutation($input: CountryAddInput!) { |
| 79 | + | countryAdd(input: $input) { |
| 80 | + | ...CountryLine_node |
| 81 | + | } |
| 82 | + | } |
| 83 | + | `; |
| 84 | + | |
| 85 | + | const countryValidation = (t: (message: string) => string) => Yup.object() |
| 86 | + | .shape({ |
| 87 | + | name: Yup.string() |
| 88 | + | .required(t('This field is required')), |
| 89 | + | description: Yup.string() |
| 90 | + | .min(3, t('The value is too short')) |
| 91 | + | .max(5000, t('The value is too long')) |
| 92 | + | .required(t('This field is required')), |
| 93 | + | }); |
| 94 | + | |
| 95 | + | const sharedUpdater = (store: RecordSourceSelectorProxy, userId: string, paginationOptions: PaginationOptions, newEdge: RecordProxy) => { |
| 96 | + | const userProxy = store.get(userId); |
| 97 | + | if (userProxy) { |
| 98 | + | const conn: RecordProxy | null | undefined = ConnectionHandler.getConnection( |
| 99 | + | userProxy, |
| 100 | + | 'Pagination_countries', |
| 101 | + | paginationOptions, |
| 102 | + | ); |
| 103 | + | if (conn) { |
| 104 | + | ConnectionHandler.insertEdgeBefore(conn, newEdge); |
| 105 | + | } |
| 106 | + | } |
| 107 | + | }; |
| 108 | + | |
| 109 | + | interface CountryCreationProps { |
| 110 | + | paginationOptions: PaginationOptions |
| 111 | + | } |
| 112 | + | |
| 113 | + | interface CountriesFormProps { |
| 114 | + | name: string, |
| 115 | + | description: string, |
| 116 | + | createdBy: CreatedBy, |
| 117 | + | objectMarking: Array<ObjectMarking>, |
| 118 | + | objectLabel: Array<ObjectLabel>, |
| 119 | + | externalReferences: Array<ExternalReference> |
| 120 | + | } |
| 121 | + | |
| 122 | + | const CountryCreation: FunctionComponent<CountryCreationProps> = ({ |
| 123 | + | paginationOptions, |
| 124 | + | }) => { |
| 125 | + | const { t } = useFormatter(); |
| 126 | + | const classes = styles(); |
| 127 | + | |
| 128 | + | const [open, setOpen] = useState(false); |
| 129 | + | const initialValues: CountriesFormProps = { |
| 130 | + | name: '', |
| 131 | + | description: '', |
| 132 | + | createdBy: {} as CreatedBy, |
| 133 | + | objectMarking: [], |
| 134 | + | objectLabel: [], |
| 135 | + | externalReferences: [], |
| 136 | + | }; |
| 137 | + | |
| 138 | + | const handleOpen = () => { |
| 139 | + | setOpen(true); |
| 140 | + | }; |
| 141 | + | |
| 142 | + | const handleClose = () => { |
| 143 | + | setOpen(false); |
| 144 | + | }; |
| 145 | + | |
| 146 | + | const onSubmit = ( |
| 147 | + | values: CountriesFormProps, |
| 148 | + | { setSubmitting, setErrors, resetForm }: FormikHelpers<CountriesFormProps>, |
| 149 | + | ) => { |
| 150 | + | const finalValues = { |
| 151 | + | name: values.name, |
| 152 | + | description: values.description, |
| 153 | + | createdBy: values.createdBy?.value, |
| 154 | + | objectMarking: values.objectMarking.map((v) => v.value), |
| 155 | + | objectLabel: values.objectLabel.map((v) => v.value), |
| 156 | + | externalReferences: values.objectLabel.map((v) => v.value), |
| 157 | + | }; |
| 158 | + | commitMutation({ |
| 159 | + | mutation: countryMutation, |
| 160 | + | variables: { |
| 161 | + | input: finalValues, |
| 162 | + | }, |
| 163 | + | updater: (store: RecordSourceSelectorProxy) => { |
| 164 | + | const payload: RecordProxy | null = store.getRootField('countryAdd'); |
| 165 | + | if (payload) { |
| 166 | + | const newEdge: RecordProxy = payload.setLinkedRecord(payload, 'node'); |
| 167 | + | const container: RecordProxy = store.getRoot(); |
| 168 | + | sharedUpdater( |
| 169 | + | store, |
| 170 | + | container.getDataID(), |
| 171 | + | paginationOptions, |
| 172 | + | newEdge, |
| 173 | + | ); |
| 174 | + | } |
| 175 | + | }, |
| 176 | + | onError: (error: Error) => { |
| 177 | + | handleErrorInForm(error, setErrors); |
| 178 | + | setSubmitting(false); |
| 179 | + | }, |
| 180 | + | setSubmitting, |
| 181 | + | onCompleted: () => { |
| 182 | + | setSubmitting(false); |
| 183 | + | resetForm(); |
| 184 | + | handleClose(); |
| 185 | + | }, |
| 186 | + | optimisticUpdater: undefined, |
| 187 | + | optimisticResponse: undefined, |
| 188 | + | }); |
| 189 | + | }; |
| 190 | + | |
| 191 | + | const onReset = () => { |
| 192 | + | handleClose(); |
| 193 | + | }; |
| 194 | + | |
| 195 | + | return ( |
| 196 | + | <div> |
| 197 | + | <Fab |
| 198 | + | onClick={handleOpen} |
| 199 | + | color="secondary" |
| 200 | + | aria-label="Add" |
| 201 | + | className={classes.createButton} |
| 202 | + | > |
| 203 | + | <Add /> |
| 204 | + | </Fab> |
| 205 | + | <Drawer |
| 206 | + | open={open} |
| 207 | + | anchor="right" |
| 208 | + | elevation={1} |
| 209 | + | sx={{ zIndex: 1202 }} |
| 210 | + | classes={{ paper: classes.drawerPaper }} |
| 211 | + | onClose={handleClose} |
| 212 | + | > |
| 213 | + | <div className={classes.header}> |
| 214 | + | <IconButton |
| 215 | + | aria-label="Close" |
| 216 | + | className={classes.closeButton} |
| 217 | + | onClick={handleClose} |
| 218 | + | size="large" |
| 219 | + | color="primary" |
| 220 | + | > |
| 221 | + | <Close fontSize="small" color="primary" /> |
| 222 | + | </IconButton> |
| 223 | + | <Typography variant="h6">{t('Create a Country')}</Typography> |
| 224 | + | </div> |
| 225 | + | <div className={classes.container}> |
| 226 | + | <Formik |
| 227 | + | initialValues={initialValues} |
| 228 | + | validationSchema={countryValidation(t)} |
| 229 | + | onSubmit={onSubmit} |
| 230 | + | onReset={onReset} |
| 231 | + | > |
| 232 | + | {({ |
| 233 | + | submitForm, |
| 234 | + | handleReset, |
| 235 | + | isSubmitting, |
| 236 | + | setFieldValue, |
| 237 | + | values, |
| 238 | + | }) => ( |
| 239 | + | <Form style={{ margin: '20px 0 20px 0' }}> |
| 240 | + | <Field |
| 241 | + | component={TextField} |
| 242 | + | variant="standard" |
| 243 | + | name="name" |
| 244 | + | label={t('Name')} |
| 245 | + | fullWidth={true} |
| 246 | + | detectDuplicate={['Country']} |
| 247 | + | /> |
| 248 | + | <Field |
| 249 | + | component={MarkDownField} |
| 250 | + | name="description" |
| 251 | + | label={t('Description')} |
| 252 | + | fullWidth={true} |
| 253 | + | multiline={true} |
| 254 | + | rows="4" |
| 255 | + | style={{ marginTop: 20 }} |
| 256 | + | /> |
| 257 | + | <CreatedByField |
| 258 | + | name="createdBy" |
| 259 | + | style={{ |
| 260 | + | marginTop: 20, |
| 261 | + | width: '100%', |
| 262 | + | }} |
| 263 | + | setFieldValue={setFieldValue} |
| 264 | + | /> |
| 265 | + | <ObjectLabelField |
| 266 | + | name="objectLabel" |
| 267 | + | style={{ |
| 268 | + | marginTop: 20, |
| 269 | + | width: '100%', |
| 270 | + | }} |
| 271 | + | setFieldValue={setFieldValue} |
| 272 | + | values={values.objectLabel} |
| 273 | + | /> |
| 274 | + | <ObjectMarkingField |
| 275 | + | name="objectMarking" |
| 276 | + | style={{ |
| 277 | + | marginTop: 20, |
| 278 | + | width: '100%', |
| 279 | + | }} |
| 280 | + | /> |
| 281 | + | <ExternalReferencesField |
| 282 | + | name="externalReferences" |
| 283 | + | style={{ |
| 284 | + | marginTop: 20, |
| 285 | + | width: '100%', |
| 286 | + | }} |
| 287 | + | setFieldValue={setFieldValue} |
| 288 | + | values={values.externalReferences} |
| 289 | + | /> |
| 290 | + | <div className={classes.buttons}> |
| 291 | + | <Button |
| 292 | + | variant="contained" |
| 293 | + | onClick={handleReset} |
| 294 | + | disabled={isSubmitting} |
| 295 | + | classes={{ root: classes.button }} |
| 296 | + | > |
| 297 | + | {t('Cancel')} |
| 298 | + | </Button> |
| 299 | + | <Button |
| 300 | + | variant="contained" |
| 301 | + | color="secondary" |
| 302 | + | onClick={submitForm} |
| 303 | + | disabled={isSubmitting} |
| 304 | + | classes={{ root: classes.button }} |
| 305 | + | > |
| 306 | + | {t('Create')} |
| 307 | + | </Button> |
| 308 | + | </div> |
| 309 | + | </Form> |
| 310 | + | )} |
| 311 | + | </Formik> |
| 312 | + | </div> |
| 313 | + | </Drawer> |
| 314 | + | </div> |
| 315 | + | ); |
| 316 | + | }; |
| 317 | + | |
| 318 | + | export default CountryCreation; |
| 319 | + | |