jonahkh
2020-06-16 3cf2d17add33f2cb95cfa365860d84706aa110c2
commit | author | age
0736f9 1 import {AnimalService} from "../Services/AnimalService";
J 2 import React, {FormEvent} from "react";
3 import {
4     ActionGroup,
5     Alert,
6     AlertActionCloseButton, Button, ButtonType,
7     Form,
8     FormGroup,
9     FormSelect,
10     FormSelectOption,
11     TextInput
12 } from "@patternfly/react-core";
13 import {AnimalNotificationRequest} from "../Models/AnimalNotificationRequest";
14 import {ApproximateSize} from "../Models/ApproximateSize";
15 import BullseyeSpinner from "./BullseyeSpinner";
16 import {RESTConnectionError} from "../Services/RESTService";
17
18 type NotificationRequestFormProps = {
19     animalService: AnimalService;
20 }
21
22 type NotificationRequestFormState = {
23     notificationRequest: AnimalNotificationRequest;
24     showInvalidFormAlert: boolean;
25     showSubmitSuccessAlert: boolean;
26     showSubmitErrorAlert: boolean;
27     submitError: {
28         title: string,
29         description: string
30     }
31     isSubmitting: boolean;
32     loading: boolean,
33     error: {
34         isActive: boolean,
35         header: string,
36         message: string
37     }
38 }
39
d4efcf 40 export default class NotificationRequestForm
JR 41     extends React.Component<NotificationRequestFormProps, NotificationRequestFormState> {
0736f9 42
J 43     constructor(props: NotificationRequestFormProps) {
44         super(props);
45         this.state = {
46             notificationRequest: NotificationRequestForm.getEmptyFields(),
47             showInvalidFormAlert: false,
48             showSubmitSuccessAlert: false,
49             showSubmitErrorAlert: false,
50             submitError: {
51                 title: "",
52                 description: ""
53             },
54             isSubmitting: false,
55             loading: false,
56             error: {
57                 isActive: false,
58                 header: "",
59                 message: ""
60             }
d4efcf 61         };
0736f9 62     }
J 63
64     private static getEmptyFields(): AnimalNotificationRequest {
65         return {
66             username: "",
67             email: "",
68             breed: "",
69             minWeight: 0,
70             maxWeight: 200,
71             approximateSize: ApproximateSize.S
72         };
73     }
74
75     private resetFormFields() {
76         this.setState({
77             notificationRequest: NotificationRequestForm.getEmptyFields()
78         });
79     }
80
81     public renderLoader() {
82         return <BullseyeSpinner/>;
83     }
84
85     public render() {
86         return (
87             <React.Fragment>
88                 {this.state.isSubmitting ? this.renderLoader() : this.renderForm()}
89             </React.Fragment>
d4efcf 90         );
0736f9 91     }
J 92
93     public renderForm() {
94         let notificationRequest = this.state.notificationRequest;
95         return (
96             <Form onSubmit={this.handleFormSubmit.bind(this)}>
97                 {this.renderCreationSuccessAlert()}
98                 {this.renderCreationErrorAlert()}
99                 {this.state.showInvalidFormAlert &&
100                 <Alert
101                     id="myalert"
102                     className="popup"
103                     variant="danger"
104                     title="Invalid form"
105                     action={<AlertActionCloseButton
106                         onClose={this.handleCloseInvalidFormAlert.bind(this)}
107                     />}>
108                     Please complete required fields
109                 </Alert>}
110                 <FormGroup
111                     label="Name"
112                     isRequired
113                     fieldId="adoption-form-username"
114                     helperText="Please provide your name"
115                 >
116                     <TextInput
117                         isRequired
118                         type="text"
119                         id="adoption-form-username"
120                         name="adoption-form-username"
121                         aria-describedby="adoption-form-name-helper"
122                         value={notificationRequest.username}
123                         onChange={this.handleNameChange.bind(this)}
124                     />
125                 </FormGroup>
126                 <FormGroup label="Email" isRequired fieldId="adoption-form-email">
127                     <TextInput
128                         isRequired
129                         type="email"
130                         id="adoption-form-email"
131                         name="adoption-form-email"
132                         value={notificationRequest.email}
133                         onChange={this.handleEmailChange.bind(this)}
134                     />
135                 </FormGroup>
136                 <FormGroup
137                     label="Breed"
138                     fieldId="animal-form-breed"
139                     helperText="Please provide the breed"
140                 >
141                     <TextInput
142                         type="text"
143                         id="animal-form-breed"
144                         name="animal-form-breed"
145                         aria-describedby="animal-form-breed-helper"
146                         value={notificationRequest.breed}
147                         onChange={this.handleBreedChange.bind(this)}
148                     />
149                 </FormGroup>
150                 <FormGroup label="Animal Minimum Weight" isRequired fieldId="animal-form-min-weight">
151                     <TextInput
152                         isRequired
153                         type="number"
154                         id="animal-form-min-weight"
155                         name="animal-form-min-weight"
156                         value={notificationRequest.minWeight}
157                         onChange={this.handleMinWeightChange.bind(this)}
158                     />
159                 </FormGroup>
160                 <FormGroup label="Animal Maximum Weight" isRequired fieldId="animal-form-max-weight">
161                     <TextInput
162                         isRequired
163                         type="number"
164                         id="animal-form-max-weight"
165                         name="animal-form-max-weight"
166                         value={notificationRequest.maxWeight}
167                         onChange={this.handleMaxWeightChange.bind(this)}
168                     />
169                 </FormGroup>
170                 <FormGroup
171                     label="Approximate Size"
172                     fieldId="animal-form-approximate-size"
173                     helperText="Please provide approximate size"
174                 >
175                     <FormSelect
176                         value={notificationRequest.approximateSize}
177                         onChange={this.handleApproximateSizeChange.bind(this)}
178                         aria-label="Select approximate size">
179                         <FormSelectOption label={"None"}
d4efcf 180                             value={null}
JR 181                             key={"None"}/>
0736f9 182                         {Object.keys(ApproximateSize).map((approximateSize) => {
J 183                             return <FormSelectOption
184                                 key={approximateSize}
185                                 value={approximateSize}
186                                 label={approximateSize}
d4efcf 187                             />;
0736f9 188                         })}
J 189                     </FormSelect>
190                 </FormGroup>
191                 <ActionGroup>
192                     <Button variant="primary" type={ButtonType.submit}>Subscribe</Button>
193                 </ActionGroup>
194             </Form>
d4efcf 195         );
0736f9 196     }
J 197
198     private handleNameChange(username: string) {
199         // Immutability: instead of modifying the state,
200         // we make a copy with the new value, and then
201         // set the new state
202         const notificationRequest = {...this.state.notificationRequest, username};
203         this.setState({
204             notificationRequest
205         });
206     }
207
208     private handleBreedChange(breed: string) {
209         // Immutability: instead of modifying the state,
210         // we make a copy with the new value, and then
211         // set the new state
212         const notificationRequest = {...this.state.notificationRequest, breed};
213         this.setState({
214             notificationRequest
215         });
216     }
217
218     private handleEmailChange(email: string) {
219         // Immutability: instead of modifying the state,
220         // we make a copy with the new value, and then
221         // set the new state
222         const notificationRequest = { ...this.state.notificationRequest, email };
223         this.setState({
224             notificationRequest
225         });
226     }
227
228     private handleApproximateSizeChange(approximateSize: string) {
229         // Immutability: instead of modifying the state,
230         // we make a copy with the new value, and then
231         // set the new state
232         const notificationRequest = {...this.state.notificationRequest, approximateSize};
233         this.setState({
234             notificationRequest
235         });
236     }
237
238     private handleMinWeightChange(minWeight: string) {
239         // Immutability: instead of modifying the state,
240         // we make a copy with the new value, and then
241         // set the new state
242         const notificationRequest = {
243             ...this.state.notificationRequest,
244             weight: parseInt(minWeight)
245         };
246         this.setState({
247             notificationRequest
248         });
249     }
250
251     private handleMaxWeightChange(maxWeight: string) {
252         // Immutability: instead of modifying the state,
253         // we make a copy with the new value, and then
254         // set the new state
255         const notificationRequest = {
256             ...this.state.notificationRequest,
257             weight: parseInt(maxWeight)
258         };
259         this.setState({
260             notificationRequest
261         });
262     }
263
264     private renderCreationErrorAlert(): React.ReactNode | null {
265         if (this.state.showSubmitErrorAlert) {
266             return (
267                 <Alert
268                     variant="danger"
269                     title={`Error creating animal: ${this.state.submitError.title}`}
270                 >
271                     {this.state.submitError.description}
272                 </Alert>
273             );
274         }
275         return null;
276     }
277
278     private showErrorAlert(error: Error) {
279         let submitError = {
280             title: error.name,
281             description: error.message
282         };
283         if (error instanceof RESTConnectionError) {
284             submitError.description = error.description;
285         }
286         this.setState({
287             showSubmitErrorAlert: true,
288             submitError,
289             showSubmitSuccessAlert: false,
290             showInvalidFormAlert: false
291         });
292         this.hideAlertsAfter(3000);
293     }
294
295     private hideAlertsAfter(millis: number) {
296         setTimeout(() => {
297             this.setState({
298                 showSubmitSuccessAlert: false,
299                 showSubmitErrorAlert: false,
300                 submitError: {
301                     title: "",
302                     description: ""
303                 }
304             });
305         }, millis);
306     }
307     private async handleFormSubmit(event: FormEvent) {
308         if (this.isFormValid()) {
309             this.setState({isSubmitting: true});
310             try {
311                 await this.props.animalService.subscribeNotifications(this.state.notificationRequest);
312                 // const animalId = await this.props.animalService.create(this.state.animal);
313                 // TODO photo input and then write file to server
314                 // Maybe have some embedded database like SQLite?
315
316                 this.showSuccessAlert();
317                 this.resetFormFields();
318             } catch (error) {
319                 this.showErrorAlert(error);
320             } finally {
321                 this.setState({isSubmitting: false});
322             }
323         } else {
324             this.setState({showInvalidFormAlert: true});
325         }
326         event.preventDefault();
327     }
328
329     private isFormValid() {
330         const {notificationRequest} = this.state;
331         const fieldIsEmpty = (field: string) => notificationRequest[field as keyof AnimalNotificationRequest] === "";
332
333         const hasEmptyFields = Object
334             .keys(notificationRequest)
335             .some(fieldIsEmpty);
336
337         return !hasEmptyFields;
338     }
339
340
341     private showConnectionError(error: RESTConnectionError) {
342         this.setState({
343             error: {
344                 isActive: true,
345                 header: error.message,
346                 message: error.description,
347             }
348         });
349     }
350
351     private closeErrorAlert = () => {
352         this.setState({
353             error: {
354                 isActive: false,
355                 message: "",
356                 header: ""
357             }
358         });
359     }
360
361     private showSuccessAlert() {
362         this.setState({
363             showSubmitSuccessAlert: true,
364             showSubmitErrorAlert: false,
365             showInvalidFormAlert: false
366         });
367         this.hideAlertsAfter(3000);
368     }
369
370     private handleCloseInvalidFormAlert() {
371         this.setState({showInvalidFormAlert: false});
372     }
373
374     private renderCreationSuccessAlert(): React.ReactNode | null {
375         if (this.state.showSubmitSuccessAlert) {
376             return <Alert
377                 variant="success"
378                 title="Notification Subscription Created"
379             />;
380         }
381         return null;
382     }
3cf2d1 383 }