redux-form-validation
[arvados-workbench2.git] / src / views-components / dialog-create / dialog-project-create.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as React from 'react';
6 import { reduxForm, Field } from 'redux-form';
7 import { compose } from 'redux';
8 import TextField from '@material-ui/core/TextField';
9 import Dialog from '@material-ui/core/Dialog';
10 import DialogActions from '@material-ui/core/DialogActions';
11 import DialogContent from '@material-ui/core/DialogContent';
12 import DialogTitle from '@material-ui/core/DialogTitle';
13 import { Button, StyleRulesCallback, WithStyles, withStyles, CircularProgress } from '@material-ui/core';
14
15 import { NAME, DESCRIPTION } from '../../validators/create-project/create-project-validator';
16 import { isUniqName } from '../../validators/is-uniq-name';
17
18 interface ProjectCreateProps {
19     open: boolean;
20     pending: boolean;
21     handleClose: () => void;
22     onSubmit: (data: { name: string, description: string }) => void;
23     handleSubmit: any;
24 }
25
26 interface TextFieldProps {
27     label: string;
28     floatinglabeltext: string;
29     className?: string;
30     input?: string;
31     meta?: any;
32 }
33
34 class DialogProjectCreate extends React.Component<ProjectCreateProps & WithStyles<CssRules>> {
35     /*componentWillReceiveProps(nextProps: ProjectCreateProps) {
36         const { error } = nextProps;
37
38         TODO: Validation for other errors
39         if (this.props.error !== error && error && error.includes("UniqueViolation")) {
40             this.setState({ isUniqName: error });
41         }
42 }*/
43
44     render() {
45         const { classes, open, handleClose, pending, handleSubmit, onSubmit } = this.props;
46
47         return (
48             <Dialog
49                 open={open}
50                 onClose={handleClose}>
51                 <div className={classes.dialog}>
52                     <form onSubmit={handleSubmit((data: any) => onSubmit(data))}>
53                         <DialogTitle id="form-dialog-title" className={classes.dialogTitle}>Create a project</DialogTitle>
54                         <DialogContent className={classes.formContainer}>
55                             <Field name="name"
56                                 component={this.renderTextField}
57                                 floatinglabeltext="Project Name"
58                                 validate={NAME}
59                                 className={classes.textField}
60                                 label="Project Name" />
61                             <Field name="description"
62                                 component={this.renderTextField}
63                                 floatinglabeltext="Description"
64                                 validate={DESCRIPTION}
65                                 className={classes.textField}
66                                 label="Description" />
67                         </DialogContent>
68                         <DialogActions>
69                             <Button onClick={handleClose} className={classes.button} color="primary" disabled={pending}>CANCEL</Button>
70                             <Button type="submit"
71                                 className={classes.lastButton}
72                                 color="primary"
73                                 disabled={pending}
74                                 variant="contained">
75                                 CREATE A PROJECT
76                             </Button>
77                             {pending && <CircularProgress size={20} className={classes.createProgress} />}
78                         </DialogActions>
79                     </form>
80                 </div>
81             </Dialog>
82         );
83     }
84
85     // TODO Make it separate file
86     renderTextField = ({ input, label, meta: { touched, error }, ...custom }: TextFieldProps) => (
87         <TextField
88             helperText={touched && error ? error : void 0}
89             label={label}
90             className={this.props.classes.textField}
91             error={touched && !!error} 
92             autoComplete='off'
93             {...input}
94             {...custom}
95         />
96     )
97 }
98
99 type CssRules = "button" | "lastButton" | "formContainer" | "textField" | "dialog" | "dialogTitle" | "createProgress";
100
101 const styles: StyleRulesCallback<CssRules> = theme => ({
102     button: {
103         marginLeft: theme.spacing.unit
104     },
105     lastButton: {
106         marginLeft: theme.spacing.unit,
107         marginRight: "20px",
108     },
109     formContainer: {
110         display: "flex",
111         flexDirection: "column",
112         marginTop: "20px",
113     },
114     dialogTitle: {
115         paddingBottom: "0"
116     },
117     textField: {
118         marginTop: "32px",
119     },
120     dialog: {
121         minWidth: "600px",
122         minHeight: "320px"
123     },
124     createProgress: {
125         position: "absolute",
126         minWidth: "20px",
127         right: "95px"
128     },
129 });
130
131 export default compose(
132     reduxForm({ form: 'projectCreateDialog',/* asyncValidate: isUniqName, asyncBlurFields: ["name"] */}),
133     withStyles(styles)
134 )(DialogProjectCreate);