init collection edit dialog, add reducers, modify store, refactor code
[arvados-workbench2.git] / src / views-components / dialog-update / dialog-collection-update.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 { ArvadosTheme } from '../../common/custom-theme';
9 import { Dialog, DialogActions, DialogContent, DialogTitle, TextField, StyleRulesCallback, withStyles, WithStyles, Button, CircularProgress } from '../../../node_modules/@material-ui/core';
10 import { COLLECTION_NAME_VALIDATION, COLLECTION_DESCRIPTION_VALIDATION } from '../../validators/create-project/create-project-validator';
11
12 type CssRules = 'content' | 'actions' | 'textField' | 'buttonWrapper' | 'saveButton' | 'circularProgress';
13
14 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
15     content: {
16         display: 'flex',
17         flexDirection: 'column'
18     },
19     actions: {
20         margin: 0,
21         padding: `${theme.spacing.unit}px ${theme.spacing.unit * 3 - theme.spacing.unit / 2}px 
22                 ${theme.spacing.unit * 3}px ${theme.spacing.unit * 3}px`
23     },
24     textField: {
25         marginBottom: theme.spacing.unit * 3
26     },
27     buttonWrapper: {
28         position: 'relative'
29     },
30     saveButton: {
31         boxShadow: 'none'
32     },
33     circularProgress: {
34         position: 'absolute',
35         top: 0,
36         bottom: 0,
37         left: 0,
38         right: 0,
39         margin: 'auto'
40     }
41 });
42
43 interface DialogCollectionDataProps {
44     open: boolean;
45     handleSubmit: any;
46     submitting: boolean;
47     invalid: boolean;
48     pristine: boolean;
49 }
50
51 interface DialogCollectionAction {
52     handleClose: () => void;
53     onSubmit: (data: { name: string, description: string }) => void;
54 }
55
56 type DialogCollectionProps = DialogCollectionDataProps & DialogCollectionAction & WithStyles<CssRules>;
57
58 interface TextFieldProps {
59     label: string;
60     floatinglabeltext: string;
61     className?: string;
62     input?: string;
63     meta?: any;
64 }
65
66 export const DialogCollectionUpdate = compose(
67     reduxForm({ form: 'collectionEditDialog' }),
68     withStyles(styles))(
69
70         class DialogCollectionUpdate extends React.Component<DialogCollectionProps> {
71
72             render() {
73                 const { classes, open, handleClose, handleSubmit, onSubmit, submitting, invalid, pristine } = this.props;
74                 return (
75                     <Dialog open={open}
76                         onClose={handleClose}
77                         fullWidth={true}
78                         maxWidth='sm'
79                         disableBackdropClick={true}
80                         disableEscapeKeyDown={true}>
81
82                         <form onSubmit={handleSubmit((data: any) => onSubmit(data))}>
83                             <DialogTitle>Edit Collection</DialogTitle>
84                             <DialogContent className={classes.content}>
85                                 <Field name="name"
86                                     disabled={submitting}
87                                     component={this.renderTextField}
88                                     floatinglabeltext="Collection Name"
89                                     validate={COLLECTION_NAME_VALIDATION}
90                                     className={classes.textField}
91                                     label="Collection Name" />
92                                 <Field name="description"
93                                     disabled={submitting}
94                                     component={this.renderTextField}
95                                     floatinglabeltext="Description - optional"
96                                     validate={COLLECTION_DESCRIPTION_VALIDATION}
97                                     className={classes.textField}
98                                     label="Description - optional" />
99                             </DialogContent>
100                             <DialogActions className={classes.actions}>
101                                 <Button onClick={handleClose} color="primary"
102                                     disabled={submitting}>CANCEL</Button>
103                                 <div className={classes.buttonWrapper}>
104                                     <Button type="submit" className={classes.saveButton}
105                                         color="primary"
106                                         disabled={invalid || submitting || pristine}
107                                         variant="contained">
108                                         SAVE
109                                     </Button>
110                                     {submitting && <CircularProgress size={20} className={classes.circularProgress} />}
111                                 </div>
112                             </DialogActions>
113                         </form>
114                     </Dialog>
115                 );
116             }
117
118             renderTextField = ({ input, label, meta: { touched, error }, ...custom }: TextFieldProps) => (
119                 <TextField
120                     helperText={touched && error}
121                     label={label}
122                     className={this.props.classes.textField}
123                     error={touched && !!error}
124                     autoComplete='off'
125                     {...input}
126                     {...custom}
127                 />
128             )
129         }
130     );