@plusscommunities/pluss-maintenance-app 6.0.5 → 6.0.6-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/module/actions/types.js +4 -3
  2. package/dist/module/actions/types.js.map +1 -1
  3. package/dist/module/apis/maintenanceActions.js +17 -15
  4. package/dist/module/apis/maintenanceActions.js.map +1 -1
  5. package/dist/module/components/MaintenanceList.js +4 -3
  6. package/dist/module/components/MaintenanceList.js.map +1 -1
  7. package/dist/module/components/MaintenanceListItem.js +3 -2
  8. package/dist/module/components/MaintenanceListItem.js.map +1 -1
  9. package/dist/module/components/MaintenanceWidgetItem.js +2 -1
  10. package/dist/module/components/MaintenanceWidgetItem.js.map +1 -1
  11. package/dist/module/components/WidgetSmall.js +6 -3
  12. package/dist/module/components/WidgetSmall.js.map +1 -1
  13. package/dist/module/feature.config.js +18 -33
  14. package/dist/module/feature.config.js.map +1 -1
  15. package/dist/module/index.js +15 -10
  16. package/dist/module/index.js.map +1 -1
  17. package/dist/module/screens/MaintenancePage.js +7 -3
  18. package/dist/module/screens/MaintenancePage.js.map +1 -1
  19. package/dist/module/screens/RequestDetail.js +105 -30
  20. package/dist/module/screens/RequestDetail.js.map +1 -1
  21. package/dist/module/screens/RequestNotes.js +2 -1
  22. package/dist/module/screens/RequestNotes.js.map +1 -1
  23. package/dist/module/screens/ServiceRequest.js +552 -84
  24. package/dist/module/screens/ServiceRequest.js.map +1 -1
  25. package/dist/module/values.config.a.js +30 -0
  26. package/dist/module/values.config.a.js.map +1 -0
  27. package/dist/module/values.config.b.js +30 -0
  28. package/dist/module/values.config.b.js.map +1 -0
  29. package/dist/module/values.config.c.js +30 -0
  30. package/dist/module/values.config.c.js.map +1 -0
  31. package/dist/module/values.config.d.js +30 -0
  32. package/dist/module/values.config.d.js.map +1 -0
  33. package/dist/module/values.config.default.js +30 -0
  34. package/dist/module/values.config.default.js.map +1 -0
  35. package/dist/module/values.config.js +30 -0
  36. package/dist/module/values.config.js.map +1 -0
  37. package/package.json +9 -3
  38. package/src/actions/types.js +5 -3
  39. package/src/apis/maintenanceActions.js +30 -14
  40. package/src/components/MaintenanceList.js +5 -3
  41. package/src/components/MaintenanceListItem.js +7 -2
  42. package/src/components/MaintenanceWidgetItem.js +2 -1
  43. package/src/components/WidgetSmall.js +6 -3
  44. package/src/feature.config.js +18 -30
  45. package/src/index.js +15 -8
  46. package/src/screens/MaintenancePage.js +5 -4
  47. package/src/screens/RequestDetail.js +100 -32
  48. package/src/screens/RequestNotes.js +2 -1
  49. package/src/screens/ServiceRequest.js +591 -149
  50. package/src/values.config.a.js +30 -0
  51. package/src/values.config.b.js +30 -0
  52. package/src/values.config.c.js +30 -0
  53. package/src/values.config.d.js +30 -0
  54. package/src/values.config.default.js +30 -0
  55. package/src/values.config.js +30 -0
@@ -3,46 +3,120 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
3
3
  function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
4
4
  import React, { Component } from 'react';
5
5
  import { Dimensions, Platform, KeyboardAvoidingView, ScrollView, Text, TouchableOpacity, View, Switch, FlatList, ImageBackground, Keyboard } from 'react-native';
6
- import _ from 'lodash';
6
+ import DateTimePicker from 'react-native-modal-datetime-picker';
7
7
  import { Icon } from 'react-native-elements';
8
+ import _ from 'lodash';
9
+ import moment from 'moment';
8
10
  import { connect } from 'react-redux';
9
11
  import { jobAdded } from '../actions';
10
12
  import { maintenanceActions } from '../apis';
11
13
  import { Services } from '../feature.config';
12
14
  import { Components, Colours, Helper, Config } from '../core.config';
15
+ import { values } from '../values.config';
13
16
  const PHOTO_SIZE = (Dimensions.get('window').width - 64) / 3;
14
17
  class MaintenanceRequest extends Component {
15
18
  constructor(props) {
16
19
  super(props);
20
+ _defineProperty(this, "onChangeAnswer", (fieldId, answer) => {
21
+ const update = {
22
+ customFields: _.cloneDeep(this.state.customFields)
23
+ };
24
+ const field = update.customFields[fieldId];
25
+ field.answer = answer;
26
+ if (field.isTitle) update.title = field.answer;
27
+ this.setState(update);
28
+ });
29
+ _defineProperty(this, "onChangeToggleAnswer", (fieldId, answer) => {
30
+ const update = {
31
+ customFields: _.cloneDeep(this.state.customFields)
32
+ };
33
+ const field = update.customFields[fieldId];
34
+ field.answer = field.answer === answer ? undefined : answer;
35
+ if (field.isTitle) update.title = field.answer;
36
+ this.setState(update);
37
+ });
38
+ _defineProperty(this, "onChangeCheckboxAnswer", (fieldId, answer) => {
39
+ const update = {
40
+ customFields: _.cloneDeep(this.state.customFields)
41
+ };
42
+ const field = update.customFields[fieldId];
43
+ field.answer = _.xor(field.answer || [], [answer]);
44
+ if (field.isTitle) update.title = field.answer.join(', ');
45
+ this.setState(update);
46
+ });
47
+ _defineProperty(this, "onOpenDatePicker", (field, fieldId) => {
48
+ Keyboard.dismiss();
49
+ this.setState({
50
+ dateFieldId: fieldId,
51
+ popUpType: field.type,
52
+ isDateTimePickerVisible: true
53
+ });
54
+ });
55
+ _defineProperty(this, "onClearDate", fieldId => {
56
+ const update = {
57
+ customFields: _.cloneDeep(this.state.customFields)
58
+ };
59
+ const field = update.customFields[fieldId];
60
+ field.answer = undefined;
61
+ if (field.isTitle) update.title = field.answer;
62
+ this.setState(update);
63
+ });
64
+ _defineProperty(this, "onDateSelected", date => {
65
+ const {
66
+ customFields,
67
+ dateFieldId,
68
+ popUpType
69
+ } = this.state;
70
+ const update = {
71
+ customFields: _.cloneDeep(customFields),
72
+ isDateTimePickerVisible: false,
73
+ fieldId: null
74
+ };
75
+ const field = update.customFields[dateFieldId];
76
+ const dateObj = moment(date);
77
+ if (popUpType === 'date') {
78
+ field.answer = dateObj.format('YYYY-MM-DD');
79
+ if (field.isTitle) update.title = dateObj.format('DD MMM YYYY');
80
+ } else {
81
+ field.answer = dateObj.format('HH:mm');
82
+ if (field.isTitle) update.title = dateObj.format('h:mm a');
83
+ }
84
+ this.setState(update);
85
+ });
17
86
  _defineProperty(this, "onUploadStarted", (uploadUri, imageUri) => {
18
- const images = [...this.state.images];
19
- images.splice(images.length - 1, 0, {
87
+ const {
88
+ imageFieldId
89
+ } = this.state;
90
+ const imagesUpdate = this.getImages(imageFieldId);
91
+ imagesUpdate.splice(imagesUpdate.length - 1, 0, {
20
92
  uploading: true,
21
93
  uploadProgress: '0%',
22
94
  uploadUri,
23
95
  imageUri,
24
96
  allowRetry: true
25
97
  });
26
- this.setState({
27
- images
28
- });
98
+ this.setImages(imagesUpdate, imageFieldId);
29
99
  });
30
100
  _defineProperty(this, "onUploadProgress", progress => {
31
- const images = [...this.state.images];
32
- images.map(img => {
101
+ const {
102
+ imageFieldId
103
+ } = this.state;
104
+ const imagesUpdate = this.getImages(imageFieldId);
105
+ imagesUpdate.map(img => {
33
106
  if (img.uploadUri === progress.uri) {
34
107
  img.uploadProgress = progress.percentage;
35
108
  img.uploading = true;
36
109
  img.allowRetry = true;
37
110
  }
38
111
  });
39
- this.setState({
40
- images
41
- });
112
+ this.setImages(imagesUpdate, imageFieldId);
42
113
  });
43
114
  _defineProperty(this, "onUploadSuccess", async (uri, uploadUri) => {
44
- const images = [...this.state.images];
45
- images.map(img => {
115
+ const {
116
+ imageFieldId
117
+ } = this.state;
118
+ const imagesUpdate = this.getImages(imageFieldId);
119
+ imagesUpdate.map(img => {
46
120
  if (img.uploadUri === uploadUri && img.uploading) {
47
121
  img.url = uri.replace('/general/', '/general1400/');
48
122
  img.thumbNailExists = false;
@@ -50,41 +124,81 @@ class MaintenanceRequest extends Component {
50
124
  img.allowRetry = true;
51
125
  }
52
126
  });
53
- this.setState({
54
- images
55
- }, () => this.waitForThumbnails());
127
+ this.setImages(imagesUpdate, imageFieldId, () => this.waitForThumbnails());
56
128
  });
57
129
  _defineProperty(this, "onUploadFailed", uploadUri => {
58
- const images = [...this.state.images];
59
- images.map(img => {
130
+ const {
131
+ imageFieldId
132
+ } = this.state;
133
+ const imagesUpdate = this.getImages(imageFieldId);
134
+ imagesUpdate.map(img => {
60
135
  if (img.uploadUri === uploadUri) {
61
136
  img.uploading = true; // Requried for retry
62
137
  img.uploadProgress = '';
63
138
  img.allowRetry = true;
64
139
  }
65
140
  });
66
- this.setState({
67
- images
68
- });
141
+ this.setImages(imagesUpdate, imageFieldId);
69
142
  });
70
143
  _defineProperty(this, "onLibrarySelected", uri => {
71
- const images = [...this.state.images];
72
- images.splice(images.length - 1, 0, {
144
+ const {
145
+ imageFieldId
146
+ } = this.state;
147
+ const imagesUpdate = this.getImages(imageFieldId);
148
+ imagesUpdate.splice(imagesUpdate.length - 1, 0, {
73
149
  uploading: false,
74
150
  allowRetry: false,
75
151
  url: Helper.get1400(uri),
76
152
  thumbNailExists: true,
77
153
  thumbNailUrl: Helper.getThumb300(uri)
78
154
  });
79
- this.setState({
80
- images
81
- });
155
+ this.setImages(imagesUpdate, imageFieldId);
82
156
  });
83
- _defineProperty(this, "showUploadMenu", () => {
157
+ _defineProperty(this, "isFieldValid", (field, fieldId) => {
158
+ const {
159
+ mandatory,
160
+ type,
161
+ answer
162
+ } = field;
163
+ if (['staticTitle', 'staticText'].includes(type)) return true;
164
+ const imagesList = type === 'image' ? this.getImageUrls(fieldId) : [];
165
+ const checkMandatory = () => {
166
+ if (!mandatory) return true;
167
+ switch (type) {
168
+ case 'yn':
169
+ return _.isBoolean(answer);
170
+ case 'image':
171
+ return imagesList.length > 0;
172
+ case 'checkbox':
173
+ return _.isArray(answer) && answer.length > 0;
174
+ default:
175
+ return !_.isNil(answer) && !_.isEmpty(answer);
176
+ }
177
+ };
178
+ const checkFormat = () => {
179
+ if (_.isNil(answer) || _.isEmpty(answer)) return true;
180
+ switch (type) {
181
+ case 'email':
182
+ return Helper.isEmail(answer);
183
+ case 'date':
184
+ return moment(answer, 'YYYY-MM-DD', true).isValid();
185
+ case 'time':
186
+ return moment(answer, 'HH:mm', true).isValid();
187
+ default:
188
+ return true;
189
+ }
190
+ };
191
+ const valid = checkMandatory() && checkFormat();
192
+ return valid;
193
+ });
194
+ _defineProperty(this, "showUploadMenu", fieldId => {
84
195
  Keyboard.dismiss();
85
196
  if (this.state.uploadingImage || this.state.submitting) {
86
197
  return;
87
198
  }
199
+ if (fieldId) this.setState({
200
+ imageFieldId: fieldId
201
+ });
88
202
  this.imageUploader.showUploadMenu();
89
203
  });
90
204
  _defineProperty(this, "submit", async () => {
@@ -100,12 +214,15 @@ class MaintenanceRequest extends Component {
100
214
  y: 0
101
215
  });
102
216
  }, 100);
103
- const images = _.filter(this.state.images, img => {
104
- return !img.uploading && !img.add;
105
- }).map(img => {
106
- return img.url;
217
+ const images = this.getImageUrls();
218
+
219
+ // Fix custom images field answers
220
+ const customFields = _.cloneDeep(this.state.customFields);
221
+ const updatedCustomFields = customFields.map((field, fieldId) => {
222
+ if (field.type === 'image') field.answer = this.getImageUrls(fieldId);
223
+ return field;
107
224
  });
108
- maintenanceActions.sendMaintenanceRequest(this.props.uid, this.state.userName, this.state.phone, this.state.roomNumber, this.state.title, description, null, this.state.type, images, Helper.getSite(this.props.site), this.state.isHome, this.state.times).then(res => {
225
+ maintenanceActions.sendMaintenanceRequest(this.props.uid, this.state.userName, this.state.phone, this.state.roomNumber, this.state.title, description, null, this.state.type, images, Helper.getSite(this.props.site), this.state.isHome, this.state.times, updatedCustomFields).then(res => {
109
226
  if (res.data.success) {
110
227
  this.refreshRequest(res.data.searchResult);
111
228
  if (this.props.onSubmissionSuccess) this.props.onSubmissionSuccess(res.data);
@@ -130,22 +247,71 @@ class MaintenanceRequest extends Component {
130
247
  _defineProperty(this, "refreshRequest", async id => {
131
248
  try {
132
249
  const job = await maintenanceActions.getJob(Helper.getSite(this.props.site), id);
133
- // console.log('refreshRequest', job?.data);
134
250
  this.props.jobAdded(job.data);
135
251
  } catch (error) {
136
252
  console.log('refreshRequest error', error);
137
253
  }
138
254
  });
255
+ _defineProperty(this, "validateCustomFields", () => {
256
+ const {
257
+ customFields
258
+ } = this.state;
259
+ if (!customFields || customFields.length === 0) return true;
260
+ return customFields.every((field, index) => {
261
+ const isValid = this.isFieldValid(field, index);
262
+ return isValid;
263
+ });
264
+ });
265
+ _defineProperty(this, "getImages", (fieldId = null) => {
266
+ const {
267
+ images,
268
+ customFieldImages
269
+ } = this.state;
270
+ const imagesList = _.cloneDeep(fieldId ? customFieldImages[fieldId] : images);
271
+ if (!imagesList || !Array.isArray(imagesList) || imagesList.length === 0) {
272
+ return [{
273
+ add: true
274
+ }];
275
+ }
276
+ return imagesList;
277
+ });
278
+ _defineProperty(this, "setImages", (imagesList, fieldId = null, callback = null) => {
279
+ let update = {};
280
+ if (fieldId) {
281
+ const customFieldImages = _.cloneDeep(this.state.customFieldImages);
282
+ customFieldImages[fieldId] = imagesList;
283
+ update = {
284
+ customFieldImages
285
+ };
286
+ } else {
287
+ update = {
288
+ images: imagesList
289
+ };
290
+ }
291
+ this.setState(update, callback);
292
+ });
293
+ _defineProperty(this, "getImageUrls", (fieldId = null) => {
294
+ const imagesList = this.getImages(fieldId);
295
+ return _.filter(imagesList, img => {
296
+ return !img.uploading && !img.add;
297
+ }).map(img => {
298
+ return img.url;
299
+ });
300
+ });
139
301
  _defineProperty(this, "waitForThumbnails", () => {
140
302
  if (this.checkThumb) return;
141
303
  this.checkThumb = setInterval(async () => {
142
- const images = [];
143
- await Promise.all(this.state.images.map(image => {
304
+ const {
305
+ imageFieldId
306
+ } = this.state;
307
+ const imagesList = this.getImages(imageFieldId);
308
+ const imagesUpdate = [];
309
+ await Promise.all(imagesList.map(image => {
144
310
  return new Promise(async resolve => {
145
311
  const newImage = {
146
312
  ...image
147
313
  };
148
- images.push(newImage);
314
+ imagesUpdate.push(newImage);
149
315
  if (newImage.url && !newImage.thumbNailExists) {
150
316
  newImage.uploading = false;
151
317
  newImage.allowRetry = false;
@@ -155,22 +321,18 @@ class MaintenanceRequest extends Component {
155
321
  resolve(true);
156
322
  });
157
323
  }));
158
- const thumbnailsExist = images.every(image => !image.url || image.thumbNailExists);
324
+ const thumbnailsExist = imagesUpdate.every(image => !image.url || image.thumbNailExists);
159
325
  if (thumbnailsExist) {
160
326
  clearInterval(this.checkThumb);
161
327
  this.checkThumb = null;
162
- this.setState({
163
- images
164
- });
328
+ this.setImages(imagesUpdate, imageFieldId);
165
329
  }
166
330
  }, 2000);
167
331
  });
168
- _defineProperty(this, "removeImage", index => {
169
- const images = [...this.state.images];
170
- images.splice(index, 1);
171
- this.setState({
172
- images
173
- });
332
+ _defineProperty(this, "removeImage", (index, fieldId) => {
333
+ const imagesUpdate = this.getImages(fieldId);
334
+ imagesUpdate.splice(index, 1);
335
+ this.setImages(imagesUpdate, fieldId);
174
336
  });
175
337
  _defineProperty(this, "toggleFullscreenVideo", url => {
176
338
  if (typeof url !== 'string') url = '';
@@ -200,9 +362,20 @@ class MaintenanceRequest extends Component {
200
362
  currentVideoUrl: '',
201
363
  isHome: false,
202
364
  types: [],
203
- confirmationToShow: false
365
+ confirmationToShow: false,
366
+ customFields: [],
367
+ customFieldImages: {},
368
+ isDateTimePickerVisible: false,
369
+ popUpType: 'date',
370
+ dateFieldId: null,
371
+ imageFieldId: null
204
372
  };
205
373
  this.checkThumb = null;
374
+ this.keyboardTypes = {
375
+ phone: 'phone-pad',
376
+ email: 'email-address',
377
+ text: 'default'
378
+ };
206
379
  }
207
380
  componentDidMount() {
208
381
  if (this.props.userType !== 'KIOSK') {
@@ -221,7 +394,7 @@ class MaintenanceRequest extends Component {
221
394
  Services.navigation.goBack();
222
395
  }
223
396
  onPressType() {
224
- Services.navigation.navigate('jobTypePicker', {
397
+ Services.navigation.navigate(values.screenJobTypePicker, {
225
398
  currentType: this.state.type,
226
399
  types: this.state.types,
227
400
  onSelectType: this.pickType.bind(this)
@@ -247,8 +420,14 @@ class MaintenanceRequest extends Component {
247
420
  }],
248
421
  submitting: false,
249
422
  success: false,
250
- fail: false
251
- });
423
+ fail: false,
424
+ customFields: [],
425
+ customFieldImages: {},
426
+ isDateTimePickerVisible: false,
427
+ popUpType: 'date',
428
+ dateFieldId: null,
429
+ imageFieldId: null
430
+ }, () => this.pickType(this.state.type));
252
431
  }
253
432
  getJobTypes() {
254
433
  const self = this;
@@ -259,20 +438,38 @@ class MaintenanceRequest extends Component {
259
438
  self.getDefaultJob();
260
439
  }).catch(() => {});
261
440
  }
262
- getDefaultJob() {
263
- if (this.state.types.length !== 0 && this.state.jobId == null) {
264
- this.setState({
265
- type: this.state.types[0].typeName
266
- });
267
- }
268
- }
269
441
  pickType(type) {
442
+ const {
443
+ types
444
+ } = this.state;
445
+ const selected = types.find(t => t.typeName === type) || {};
270
446
  this.setState({
271
- type
447
+ type,
448
+ customFields: selected.hasCustomFields && selected.customFields.length > 0 ? _.cloneDeep(selected.customFields) : []
272
449
  });
273
450
  }
451
+ getDefaultJob() {
452
+ const {
453
+ types,
454
+ jobId
455
+ } = this.state;
456
+ if (types.length !== 0 && jobId == null) {
457
+ const defaultType = types[0];
458
+ this.pickType(defaultType.typeName);
459
+ }
460
+ }
274
461
  submitRequest() {
275
- if (this.state.submitting || !this.props.connected) {
462
+ const {
463
+ customFields,
464
+ submitting,
465
+ uploadingImage,
466
+ title,
467
+ roomNumber,
468
+ isHome,
469
+ times
470
+ } = this.state;
471
+ const hasCustomFields = customFields && customFields.length > 0;
472
+ if (submitting || !this.props.connected) {
276
473
  if (!this.props.connected) {
277
474
  this.setState({
278
475
  error: {
@@ -282,25 +479,41 @@ class MaintenanceRequest extends Component {
282
479
  }
283
480
  return;
284
481
  }
285
- if (this.state.uploadingImage) {
286
- return;
287
- }
482
+ if (uploadingImage) return;
288
483
  this.setState({
289
484
  error: null,
290
485
  showError: false
291
486
  });
292
- if (this.state.title.length === 0 || !this.state.roomNumber || this.state.roomNumber.length === 0) {
293
- this.setState({
294
- showError: true
487
+ if (title.length === 0 || !roomNumber || roomNumber.length === 0) {
488
+ console.log('submitRequest - error', {
489
+ title,
490
+ roomNumber
295
491
  });
296
- return;
297
- }
298
- if (this.state.isHome && this.state.times.length < 2) {
299
492
  this.setState({
300
493
  showError: true
301
494
  });
302
495
  return;
303
496
  }
497
+ if (hasCustomFields) {
498
+ if (!this.validateCustomFields()) {
499
+ console.log('submitRequest - custom fields error');
500
+ this.setState({
501
+ showError: true
502
+ });
503
+ return;
504
+ }
505
+ } else {
506
+ if (isHome && times.length < 2) {
507
+ console.log('submitRequest - error', {
508
+ isHome,
509
+ times
510
+ });
511
+ this.setState({
512
+ showError: true
513
+ });
514
+ return;
515
+ }
516
+ }
304
517
  this.submit();
305
518
  }
306
519
  renderUploadMenu() {
@@ -323,14 +536,15 @@ class MaintenanceRequest extends Component {
323
536
  hideLibrary: true
324
537
  });
325
538
  }
326
- renderImage(item, index) {
539
+ renderImage(item, index, fieldId = null) {
327
540
  const isVideoUrl = Helper.isVideo(item.url);
541
+ const imagesList = this.getImages(fieldId);
328
542
  if (item.add) {
329
543
  return /*#__PURE__*/React.createElement(TouchableOpacity, {
330
544
  activeOpacity: 0.8,
331
- onPress: this.showUploadMenu
545
+ onPress: () => this.showUploadMenu(fieldId)
332
546
  }, /*#__PURE__*/React.createElement(View, {
333
- style: [styles.imageContainer, this.state.images.length > 1 && styles.imageContainerNotEmpty, index % 3 === 0 && {
547
+ style: [styles.imageContainer, imagesList.length > 1 && styles.imageContainerNotEmpty, index % 3 === 0 && {
334
548
  marginLeft: 0
335
549
  }, index > 2 && {
336
550
  marginTop: 8
@@ -344,7 +558,7 @@ class MaintenanceRequest extends Component {
344
558
  }))));
345
559
  }
346
560
  return /*#__PURE__*/React.createElement(View, {
347
- style: [styles.imageContainer, this.state.images.length > 1 && styles.imageContainerNotEmpty, index % 3 === 0 && {
561
+ style: [styles.imageContainer, imagesList.length > 1 && styles.imageContainerNotEmpty, index % 3 === 0 && {
348
562
  marginLeft: 0
349
563
  }, index > 2 && {
350
564
  marginTop: 8
@@ -366,7 +580,7 @@ class MaintenanceRequest extends Component {
366
580
  iconStyle: styles.imageControlIcon
367
581
  }))), /*#__PURE__*/React.createElement(TouchableOpacity, {
368
582
  style: styles.removeImage,
369
- onPress: this.removeImage.bind(this, index)
583
+ onPress: () => this.removeImage(index, fieldId)
370
584
  }, /*#__PURE__*/React.createElement(Icon, {
371
585
  name: "remove",
372
586
  type: "font-awesome",
@@ -385,24 +599,196 @@ class MaintenanceRequest extends Component {
385
599
  style: styles.requestSuccess
386
600
  }, "Your request has been submitted. Thank you."));
387
601
  }
388
- renderImageList() {
602
+ renderImageList(fieldId = null) {
603
+ const imagesList = this.getImages(fieldId);
389
604
  return /*#__PURE__*/React.createElement(View, {
390
605
  style: styles.imageSection
391
606
  }, /*#__PURE__*/React.createElement(View, {
392
- style: [styles.imageListContainer, this.state.images.length < 2 && styles.imageListContainerEmpty]
607
+ style: [styles.imageListContainer, imagesList.length < 2 && styles.imageListContainerEmpty]
393
608
  }, /*#__PURE__*/React.createElement(FlatList, {
394
609
  keyboardShouldPersistTaps: "always",
395
610
  enableEmptySections: true,
396
- data: this.state.images,
611
+ data: imagesList,
397
612
  renderItem: ({
398
613
  item,
399
614
  index
400
- }) => this.renderImage(item, index),
615
+ }) => this.renderImage(item, index, fieldId),
401
616
  keyExtractor: (item, index) => index,
402
617
  numColumns: 3
403
618
  })));
404
619
  }
620
+ renderDateField(field, fieldId, sectionStyle) {
621
+ let displayText, placeHolder, icon, errorText;
622
+ if (field.type === 'date') {
623
+ displayText = field.answer ? moment(field.answer, 'YYYY-MM-DD').format('DD MMM YYYY') : '';
624
+ placeHolder = 'dd mmm yyyy';
625
+ icon = 'calendar';
626
+ errorText = 'Not a valid date';
627
+ } else {
628
+ displayText = field.answer ? moment(field.answer, 'HH:mm').format('h:mm a') : '';
629
+ placeHolder = '--:-- --';
630
+ icon = 'clock-o';
631
+ errorText = 'Not a valid time';
632
+ }
633
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
634
+ key: fieldId,
635
+ label: field.label,
636
+ sectionStyle: sectionStyle,
637
+ isValid: () => this.isFieldValid(field, fieldId),
638
+ required: field.mandatory,
639
+ showError: this.state.showError,
640
+ errorText: errorText
641
+ }, /*#__PURE__*/React.createElement(View, {
642
+ style: styles.dateContainer
643
+ }, /*#__PURE__*/React.createElement(TouchableOpacity, {
644
+ style: styles.dateFieldButton,
645
+ onPress: () => this.onOpenDatePicker(field, fieldId)
646
+ }, /*#__PURE__*/React.createElement(View, {
647
+ style: styles.dateFieldContainer
648
+ }, /*#__PURE__*/React.createElement(Text, {
649
+ style: styles.dateText
650
+ }, displayText || placeHolder), /*#__PURE__*/React.createElement(Icon, {
651
+ type: "font-awesome",
652
+ name: icon,
653
+ iconStyle: styles.dateIcon
654
+ }))), displayText ? /*#__PURE__*/React.createElement(TouchableOpacity, {
655
+ style: styles.dateClearButton,
656
+ onPress: () => this.onClearDate(fieldId)
657
+ }, /*#__PURE__*/React.createElement(Icon, {
658
+ type: "font-awesome",
659
+ name: "times",
660
+ iconStyle: styles.removeIcon
661
+ })) : null));
662
+ }
663
+ renderField(field, fieldId) {
664
+ const sectionStyle = {
665
+ marginTop: fieldId === 0 ? 24 : 0,
666
+ marginBottom: 24
667
+ };
668
+ switch (field.type) {
669
+ case 'yn':
670
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
671
+ key: fieldId,
672
+ label: field.label,
673
+ sectionStyle: sectionStyle,
674
+ inputType: "toggle",
675
+ value: field.answer,
676
+ onChange: answer => this.onChangeToggleAnswer(fieldId, answer),
677
+ isValid: () => this.isFieldValid(field, fieldId),
678
+ showError: this.state.showError,
679
+ required: field.mandatory
680
+ });
681
+ case 'multichoice':
682
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
683
+ key: fieldId,
684
+ label: field.label,
685
+ sectionStyle: sectionStyle,
686
+ inputType: "radio",
687
+ value: field.answer,
688
+ onChange: answer => this.onChangeToggleAnswer(fieldId, answer),
689
+ options: field.values.map(o => {
690
+ return {
691
+ Label: o,
692
+ Value: o
693
+ };
694
+ }),
695
+ isValid: () => this.isFieldValid(field, fieldId),
696
+ showError: this.state.showError,
697
+ required: field.mandatory
698
+ });
699
+ case 'checkbox':
700
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
701
+ key: fieldId,
702
+ label: field.label,
703
+ sectionStyle: sectionStyle,
704
+ isValid: () => this.isFieldValid(field, fieldId),
705
+ showError: this.state.showError,
706
+ required: field.mandatory
707
+ }, field.values.map((o, i) => {
708
+ const isActive = field.answer && _.includes(field.answer, o);
709
+ return /*#__PURE__*/React.createElement(TouchableOpacity, {
710
+ onPress: () => this.onChangeCheckboxAnswer(fieldId, o),
711
+ key: i,
712
+ style: styles.multiChoiceOption,
713
+ hitSlop: {
714
+ top: 8,
715
+ left: 8,
716
+ bottom: 8,
717
+ right: 8
718
+ }
719
+ }, isActive ? /*#__PURE__*/React.createElement(Components.TickIcon, {
720
+ style: styles.tick,
721
+ size: 20,
722
+ color: this.props.colourBrandingMain
723
+ }) : /*#__PURE__*/React.createElement(View, {
724
+ style: styles.unticked
725
+ }), /*#__PURE__*/React.createElement(Text, {
726
+ style: styles.multiChoiceText
727
+ }, o));
728
+ }));
729
+ case 'text':
730
+ case 'email':
731
+ case 'phone':
732
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
733
+ key: fieldId,
734
+ label: field.label,
735
+ placeholder: field.placeHolder,
736
+ value: field.answer,
737
+ onChangeText: val => this.onChangeAnswer(fieldId, val),
738
+ editable: true,
739
+ squaredCorners: true,
740
+ isValid: () => this.isFieldValid(field, fieldId),
741
+ showError: this.state.showError,
742
+ errorText: field.type === 'email' ? 'Not a valid email' : undefined,
743
+ required: field.mandatory,
744
+ sectionStyle: sectionStyle,
745
+ autoCapitalize: "sentences",
746
+ keyboardType: this.keyboardTypes[field.type]
747
+ });
748
+ case 'staticTitle':
749
+ return /*#__PURE__*/React.createElement(Text, {
750
+ key: fieldId,
751
+ style: [styles.staticTitle, {
752
+ color: this.props.colourBrandingMain
753
+ }, sectionStyle]
754
+ }, field.label);
755
+ case 'staticText':
756
+ return /*#__PURE__*/React.createElement(View, {
757
+ key: fieldId,
758
+ style: [styles.staticText, sectionStyle]
759
+ }, Helper.toParagraphed(field.label, styles.staticText));
760
+ case 'date':
761
+ case 'time':
762
+ return this.renderDateField(field, fieldId, sectionStyle);
763
+ case 'image':
764
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
765
+ key: fieldId,
766
+ label: field.label,
767
+ sectionStyle: sectionStyle,
768
+ isValid: () => this.isFieldValid(field, fieldId),
769
+ required: field.mandatory,
770
+ showError: this.state.showError
771
+ }, this.renderImageList(fieldId));
772
+ default:
773
+ return null;
774
+ }
775
+ }
776
+ renderCustomFields() {
777
+ const {
778
+ customFields
779
+ } = this.state;
780
+ if (!customFields || customFields.length === 0) return null;
781
+ return /*#__PURE__*/React.createElement(Components.FormCard, {
782
+ style: {
783
+ marginTop: 16
784
+ }
785
+ }, customFields.map((field, i) => this.renderField(field, i)));
786
+ }
405
787
  renderForm() {
788
+ const {
789
+ customFields
790
+ } = this.state;
791
+ const hasCustomFields = customFields && customFields.length > 0;
406
792
  return /*#__PURE__*/React.createElement(View, {
407
793
  style: {
408
794
  flex: 1
@@ -464,7 +850,7 @@ class MaintenanceRequest extends Component {
464
850
  },
465
851
  required: true,
466
852
  errorText: "Please provide your address.",
467
- showError: this.state.showError && this.state.roomNumber && this.state.roomNumber.length < 2
853
+ showError: this.state.showError && (!this.state.roomNumber || this.state.roomNumber.length < 2)
468
854
  })), /*#__PURE__*/React.createElement(Components.FormCard, {
469
855
  style: {
470
856
  marginTop: 16,
@@ -493,7 +879,7 @@ class MaintenanceRequest extends Component {
493
879
  fontSize: 20,
494
880
  color: this.props.colourBrandingMain
495
881
  }]
496
- })))), /*#__PURE__*/React.createElement(Components.FormCard, {
882
+ })))), hasCustomFields ? this.renderCustomFields() : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Components.FormCard, {
497
883
  style: {
498
884
  marginTop: 16
499
885
  }
@@ -576,7 +962,7 @@ class MaintenanceRequest extends Component {
576
962
  autoCorrect: true,
577
963
  multiline: true,
578
964
  autoGrow: true
579
- })), this.renderImageList())));
965
+ })), this.renderImageList()))));
580
966
  }
581
967
  renderRegisterConfirmation() {
582
968
  return /*#__PURE__*/React.createElement(Components.ConfirmationPopup, {
@@ -601,6 +987,12 @@ class MaintenanceRequest extends Component {
601
987
  });
602
988
  }
603
989
  render() {
990
+ const {
991
+ submitting,
992
+ success,
993
+ isDateTimePickerVisible,
994
+ popUpType
995
+ } = this.state;
604
996
  return /*#__PURE__*/React.createElement(KeyboardAvoidingView, {
605
997
  behavior: Platform.OS === 'ios' && 'padding',
606
998
  style: styles.viewContainer
@@ -609,11 +1001,19 @@ class MaintenanceRequest extends Component {
609
1001
  }, /*#__PURE__*/React.createElement(Components.Header, {
610
1002
  leftIcon: "angle-left",
611
1003
  onPressLeft: this.onPressBack.bind(this),
612
- text: Config.env.strings.MAINTENANCE,
613
- rightText: this.state.submitting || this.state.success ? null : 'Done',
1004
+ text: this.props.strings[`${values.featureKey}_textFeatureTitle`] || values.textFeatureTitle,
1005
+ rightText: submitting || success ? null : 'Done',
614
1006
  onPressRight: this.submitRequest.bind(this),
615
1007
  absoluteRight: true
616
- }), this.renderForm()), this.renderRegisterConfirmation(), this.renderVideoPlayerPopup());
1008
+ }), this.renderForm()), this.renderRegisterConfirmation(), this.renderVideoPlayerPopup(), /*#__PURE__*/React.createElement(DateTimePicker, {
1009
+ isVisible: isDateTimePickerVisible,
1010
+ onConfirm: this.onDateSelected,
1011
+ onCancel: () => this.setState({
1012
+ isDateTimePickerVisible: false
1013
+ }),
1014
+ mode: popUpType,
1015
+ headerTextIOS: `Pick a ${popUpType}`
1016
+ }));
617
1017
  }
618
1018
  }
619
1019
  const styles = {
@@ -715,9 +1115,76 @@ const styles = {
715
1115
  height: 40,
716
1116
  alignItems: 'center',
717
1117
  justifyContent: 'center'
1118
+ },
1119
+ staticTitle: {
1120
+ fontSize: 20,
1121
+ fontFamily: 'sf-semibold',
1122
+ color: Colours.TEXT_DARKEST
1123
+ },
1124
+ staticText: {
1125
+ fontSize: 17,
1126
+ fontFamily: 'sf-regular',
1127
+ color: Colours.TEXT_DARKEST,
1128
+ lineHeight: 24
1129
+ },
1130
+ multiChoiceOption: {
1131
+ marginTop: 16,
1132
+ flexDirection: 'row',
1133
+ alignItems: 'center',
1134
+ minHeight: 20
1135
+ },
1136
+ multiChoiceText: {
1137
+ flex: 1,
1138
+ fontFamily: 'sf-medium',
1139
+ fontSize: 14,
1140
+ color: Colours.TEXT_DARK
1141
+ },
1142
+ tick: {
1143
+ marginRight: 10,
1144
+ borderRadius: 4
1145
+ },
1146
+ unticked: {
1147
+ marginRight: 10,
1148
+ width: 20,
1149
+ height: 20,
1150
+ borderColor: Colours.LINEGREY,
1151
+ borderWidth: 1,
1152
+ borderRadius: 4
1153
+ },
1154
+ dateContainer: {
1155
+ flexDirection: 'row',
1156
+ alignItems: 'center'
1157
+ },
1158
+ dateFieldButton: {
1159
+ flex: 1
1160
+ },
1161
+ dateFieldContainer: {
1162
+ flexDirection: 'row',
1163
+ borderRadius: 2,
1164
+ backgroundColor: '#ebeff2',
1165
+ padding: 8,
1166
+ marginTop: 8
1167
+ },
1168
+ dateText: {
1169
+ flex: 1,
1170
+ fontFamily: 'sf-regular',
1171
+ fontSize: 16,
1172
+ color: '#65686D'
1173
+ },
1174
+ dateIcon: {
1175
+ fontSize: 18,
1176
+ color: Colours.TEXT_BLUEGREY
1177
+ },
1178
+ dateClearButton: {
1179
+ paddingLeft: 12
1180
+ },
1181
+ removeIcon: {
1182
+ fontSize: 26,
1183
+ color: Colours.TEXT_BLUEGREY
718
1184
  }
719
1185
  };
720
1186
  const mapStateToProps = state => {
1187
+ var _state$strings;
721
1188
  const {
722
1189
  user,
723
1190
  connection
@@ -739,7 +1206,8 @@ const mapStateToProps = state => {
739
1206
  site,
740
1207
  unit,
741
1208
  phoneNumber,
742
- colourBrandingMain: Colours.getMainBrandingColourFromState(state)
1209
+ colourBrandingMain: Colours.getMainBrandingColourFromState(state),
1210
+ strings: ((_state$strings = state.strings) === null || _state$strings === void 0 ? void 0 : _state$strings.config) || {}
743
1211
  };
744
1212
  };
745
1213
  export default connect(mapStateToProps, {