@plusscommunities/pluss-maintenance-app 6.0.7-auth.0 → 6.0.8-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 (61) 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 +5 -4
  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 +8 -5
  12. package/dist/module/components/WidgetSmall.js.map +1 -1
  13. package/dist/module/feature.config.js +21 -36
  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/reducers/JobsReducer.js +2 -1
  18. package/dist/module/reducers/JobsReducer.js.map +1 -1
  19. package/dist/module/screens/MaintenancePage.js +7 -3
  20. package/dist/module/screens/MaintenancePage.js.map +1 -1
  21. package/dist/module/screens/RequestDetail.js +106 -31
  22. package/dist/module/screens/RequestDetail.js.map +1 -1
  23. package/dist/module/screens/RequestNotes.js +2 -1
  24. package/dist/module/screens/RequestNotes.js.map +1 -1
  25. package/dist/module/screens/ServiceRequest.js +581 -86
  26. package/dist/module/screens/ServiceRequest.js.map +1 -1
  27. package/dist/module/values.config.a.js +30 -0
  28. package/dist/module/values.config.a.js.map +1 -0
  29. package/dist/module/values.config.b.js +30 -0
  30. package/dist/module/values.config.b.js.map +1 -0
  31. package/dist/module/values.config.c.js +30 -0
  32. package/dist/module/values.config.c.js.map +1 -0
  33. package/dist/module/values.config.d.js +30 -0
  34. package/dist/module/values.config.d.js.map +1 -0
  35. package/dist/module/values.config.default.js +35 -0
  36. package/dist/module/values.config.default.js.map +1 -0
  37. package/dist/module/values.config.forms.js +35 -0
  38. package/dist/module/values.config.forms.js.map +1 -0
  39. package/dist/module/values.config.js +35 -0
  40. package/dist/module/values.config.js.map +1 -0
  41. package/package.json +9 -6
  42. package/src/actions/types.js +5 -3
  43. package/src/apis/maintenanceActions.js +30 -14
  44. package/src/components/MaintenanceList.js +6 -4
  45. package/src/components/MaintenanceListItem.js +7 -2
  46. package/src/components/MaintenanceWidgetItem.js +2 -1
  47. package/src/components/WidgetSmall.js +8 -5
  48. package/src/feature.config.js +30 -40
  49. package/src/index.js +15 -8
  50. package/src/reducers/JobsReducer.js +2 -1
  51. package/src/screens/MaintenancePage.js +5 -4
  52. package/src/screens/RequestDetail.js +101 -33
  53. package/src/screens/RequestNotes.js +2 -1
  54. package/src/screens/ServiceRequest.js +625 -152
  55. package/src/values.config.a.js +30 -0
  56. package/src/values.config.b.js +30 -0
  57. package/src/values.config.c.js +30 -0
  58. package/src/values.config.d.js +30 -0
  59. package/src/values.config.default.js +35 -0
  60. package/src/values.config.forms.js +35 -0
  61. package/src/values.config.js +35 -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 = '';
@@ -185,6 +347,7 @@ class MaintenanceRequest extends Component {
185
347
  fail: false,
186
348
  error: null,
187
349
  showError: false,
350
+ loadingTypes: values.forceCustomFields,
188
351
  userName: '',
189
352
  roomNumber: '',
190
353
  phone: '',
@@ -200,9 +363,20 @@ class MaintenanceRequest extends Component {
200
363
  currentVideoUrl: '',
201
364
  isHome: false,
202
365
  types: [],
203
- confirmationToShow: false
366
+ confirmationToShow: false,
367
+ customFields: [],
368
+ customFieldImages: {},
369
+ isDateTimePickerVisible: false,
370
+ popUpType: 'date',
371
+ dateFieldId: null,
372
+ imageFieldId: null
204
373
  };
205
374
  this.checkThumb = null;
375
+ this.keyboardTypes = {
376
+ phone: 'phone-pad',
377
+ email: 'email-address',
378
+ text: 'default'
379
+ };
206
380
  }
207
381
  componentDidMount() {
208
382
  if (this.props.userType !== 'KIOSK') {
@@ -221,7 +395,7 @@ class MaintenanceRequest extends Component {
221
395
  Services.navigation.goBack();
222
396
  }
223
397
  onPressType() {
224
- Services.navigation.navigate('jobTypePicker', {
398
+ Services.navigation.navigate(values.screenJobTypePicker, {
225
399
  currentType: this.state.type,
226
400
  types: this.state.types,
227
401
  onSelectType: this.pickType.bind(this)
@@ -247,32 +421,66 @@ class MaintenanceRequest extends Component {
247
421
  }],
248
422
  submitting: false,
249
423
  success: false,
250
- fail: false
251
- });
424
+ fail: false,
425
+ customFields: [],
426
+ customFieldImages: {},
427
+ isDateTimePickerVisible: false,
428
+ popUpType: 'date',
429
+ dateFieldId: null,
430
+ imageFieldId: null
431
+ }, () => this.pickType(this.state.type));
252
432
  }
253
433
  getJobTypes() {
254
- const self = this;
255
434
  maintenanceActions.getJobTypes(Helper.getSite(this.props.site)).then(res => {
256
- self.setState({
435
+ this.setState({
257
436
  types: res.data
258
437
  });
259
- self.getDefaultJob();
438
+ console.log(res.data);
439
+ this.getDefaultJob();
260
440
  }).catch(() => {});
261
441
  }
262
- getDefaultJob() {
263
- if (this.state.types.length !== 0 && this.state.jobId == null) {
442
+ pickType(type) {
443
+ const {
444
+ types
445
+ } = this.state;
446
+ const selected = types.find(t => t.typeName === type) || {};
447
+ if (values.forceCustomFields && !selected.hasCustomFields) {
448
+ console.log(selected);
264
449
  this.setState({
265
- type: this.state.types[0].typeName
450
+ type,
451
+ customFields: [],
452
+ noType: true
266
453
  });
454
+ return;
267
455
  }
268
- }
269
- pickType(type) {
270
456
  this.setState({
271
- type
457
+ type,
458
+ customFields: selected.hasCustomFields && selected.customFields.length > 0 ? _.cloneDeep(selected.customFields) : [],
459
+ loadingTypes: false
272
460
  });
273
461
  }
462
+ getDefaultJob() {
463
+ const {
464
+ types,
465
+ jobId
466
+ } = this.state;
467
+ if (types.length !== 0 && jobId == null) {
468
+ const defaultType = types[0];
469
+ this.pickType(defaultType.typeName);
470
+ }
471
+ }
274
472
  submitRequest() {
275
- if (this.state.submitting || !this.props.connected) {
473
+ const {
474
+ customFields,
475
+ submitting,
476
+ uploadingImage,
477
+ title,
478
+ roomNumber,
479
+ isHome,
480
+ times
481
+ } = this.state;
482
+ const hasCustomFields = customFields && customFields.length > 0;
483
+ if (submitting || !this.props.connected) {
276
484
  if (!this.props.connected) {
277
485
  this.setState({
278
486
  error: {
@@ -282,25 +490,41 @@ class MaintenanceRequest extends Component {
282
490
  }
283
491
  return;
284
492
  }
285
- if (this.state.uploadingImage) {
286
- return;
287
- }
493
+ if (uploadingImage) return;
288
494
  this.setState({
289
495
  error: null,
290
496
  showError: false
291
497
  });
292
- if (this.state.title.length === 0 || !this.state.roomNumber || this.state.roomNumber.length === 0) {
293
- this.setState({
294
- showError: true
498
+ if (title.length === 0 || !roomNumber || roomNumber.length === 0) {
499
+ console.log('submitRequest - error', {
500
+ title,
501
+ roomNumber
295
502
  });
296
- return;
297
- }
298
- if (this.state.isHome && this.state.times.length < 2) {
299
503
  this.setState({
300
504
  showError: true
301
505
  });
302
506
  return;
303
507
  }
508
+ if (hasCustomFields) {
509
+ if (!this.validateCustomFields()) {
510
+ console.log('submitRequest - custom fields error');
511
+ this.setState({
512
+ showError: true
513
+ });
514
+ return;
515
+ }
516
+ } else {
517
+ if (isHome && times.length < 2) {
518
+ console.log('submitRequest - error', {
519
+ isHome,
520
+ times
521
+ });
522
+ this.setState({
523
+ showError: true
524
+ });
525
+ return;
526
+ }
527
+ }
304
528
  this.submit();
305
529
  }
306
530
  renderUploadMenu() {
@@ -323,14 +547,15 @@ class MaintenanceRequest extends Component {
323
547
  hideLibrary: true
324
548
  });
325
549
  }
326
- renderImage(item, index) {
550
+ renderImage(item, index, fieldId = null) {
327
551
  const isVideoUrl = Helper.isVideo(item.url);
552
+ const imagesList = this.getImages(fieldId);
328
553
  if (item.add) {
329
554
  return /*#__PURE__*/React.createElement(TouchableOpacity, {
330
555
  activeOpacity: 0.8,
331
- onPress: this.showUploadMenu
556
+ onPress: () => this.showUploadMenu(fieldId)
332
557
  }, /*#__PURE__*/React.createElement(View, {
333
- style: [styles.imageContainer, this.state.images.length > 1 && styles.imageContainerNotEmpty, index % 3 === 0 && {
558
+ style: [styles.imageContainer, imagesList.length > 1 && styles.imageContainerNotEmpty, index % 3 === 0 && {
334
559
  marginLeft: 0
335
560
  }, index > 2 && {
336
561
  marginTop: 8
@@ -344,7 +569,7 @@ class MaintenanceRequest extends Component {
344
569
  }))));
345
570
  }
346
571
  return /*#__PURE__*/React.createElement(View, {
347
- style: [styles.imageContainer, this.state.images.length > 1 && styles.imageContainerNotEmpty, index % 3 === 0 && {
572
+ style: [styles.imageContainer, imagesList.length > 1 && styles.imageContainerNotEmpty, index % 3 === 0 && {
348
573
  marginLeft: 0
349
574
  }, index > 2 && {
350
575
  marginTop: 8
@@ -366,7 +591,7 @@ class MaintenanceRequest extends Component {
366
591
  iconStyle: styles.imageControlIcon
367
592
  }))), /*#__PURE__*/React.createElement(TouchableOpacity, {
368
593
  style: styles.removeImage,
369
- onPress: this.removeImage.bind(this, index)
594
+ onPress: () => this.removeImage(index, fieldId)
370
595
  }, /*#__PURE__*/React.createElement(Icon, {
371
596
  name: "remove",
372
597
  type: "font-awesome",
@@ -385,24 +610,200 @@ class MaintenanceRequest extends Component {
385
610
  style: styles.requestSuccess
386
611
  }, "Your request has been submitted. Thank you."));
387
612
  }
388
- renderImageList() {
613
+ renderImageList(fieldId = null) {
614
+ const imagesList = this.getImages(fieldId);
389
615
  return /*#__PURE__*/React.createElement(View, {
390
616
  style: styles.imageSection
391
617
  }, /*#__PURE__*/React.createElement(View, {
392
- style: [styles.imageListContainer, this.state.images.length < 2 && styles.imageListContainerEmpty]
618
+ style: [styles.imageListContainer, imagesList.length < 2 && styles.imageListContainerEmpty]
393
619
  }, /*#__PURE__*/React.createElement(FlatList, {
394
620
  keyboardShouldPersistTaps: "always",
395
621
  enableEmptySections: true,
396
- data: this.state.images,
622
+ data: imagesList,
397
623
  renderItem: ({
398
624
  item,
399
625
  index
400
- }) => this.renderImage(item, index),
626
+ }) => this.renderImage(item, index, fieldId),
401
627
  keyExtractor: (item, index) => index,
402
628
  numColumns: 3
403
629
  })));
404
630
  }
631
+ renderDateField(field, fieldId, sectionStyle) {
632
+ let displayText, placeHolder, icon, errorText;
633
+ if (field.type === 'date') {
634
+ displayText = field.answer ? moment(field.answer, 'YYYY-MM-DD').format('DD MMM YYYY') : '';
635
+ placeHolder = 'dd mmm yyyy';
636
+ icon = 'calendar';
637
+ errorText = 'Not a valid date';
638
+ } else {
639
+ displayText = field.answer ? moment(field.answer, 'HH:mm').format('h:mm a') : '';
640
+ placeHolder = '--:-- --';
641
+ icon = 'clock-o';
642
+ errorText = 'Not a valid time';
643
+ }
644
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
645
+ key: fieldId,
646
+ label: field.label,
647
+ sectionStyle: sectionStyle,
648
+ isValid: () => this.isFieldValid(field, fieldId),
649
+ required: field.mandatory,
650
+ showError: this.state.showError,
651
+ errorText: errorText
652
+ }, /*#__PURE__*/React.createElement(View, {
653
+ style: styles.dateContainer
654
+ }, /*#__PURE__*/React.createElement(TouchableOpacity, {
655
+ style: styles.dateFieldButton,
656
+ onPress: () => this.onOpenDatePicker(field, fieldId)
657
+ }, /*#__PURE__*/React.createElement(View, {
658
+ style: styles.dateFieldContainer
659
+ }, /*#__PURE__*/React.createElement(Text, {
660
+ style: styles.dateText
661
+ }, displayText || placeHolder), /*#__PURE__*/React.createElement(Icon, {
662
+ type: "font-awesome",
663
+ name: icon,
664
+ iconStyle: styles.dateIcon
665
+ }))), displayText ? /*#__PURE__*/React.createElement(TouchableOpacity, {
666
+ style: styles.dateClearButton,
667
+ onPress: () => this.onClearDate(fieldId)
668
+ }, /*#__PURE__*/React.createElement(Icon, {
669
+ type: "font-awesome",
670
+ name: "times",
671
+ iconStyle: styles.removeIcon
672
+ })) : null));
673
+ }
674
+ renderField(field, fieldId) {
675
+ const sectionStyle = {
676
+ marginTop: fieldId === 0 ? 24 : 0,
677
+ marginBottom: 24
678
+ };
679
+ switch (field.type) {
680
+ case 'yn':
681
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
682
+ key: fieldId,
683
+ label: field.label,
684
+ sectionStyle: sectionStyle,
685
+ inputType: "toggle",
686
+ value: field.answer,
687
+ onChange: answer => this.onChangeToggleAnswer(fieldId, answer),
688
+ isValid: () => this.isFieldValid(field, fieldId),
689
+ showError: this.state.showError,
690
+ required: field.mandatory
691
+ });
692
+ case 'multichoice':
693
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
694
+ key: fieldId,
695
+ label: field.label,
696
+ sectionStyle: sectionStyle,
697
+ inputType: "radio",
698
+ value: field.answer,
699
+ onChange: answer => this.onChangeToggleAnswer(fieldId, answer),
700
+ options: field.values.map(o => {
701
+ return {
702
+ Label: o,
703
+ Value: o
704
+ };
705
+ }),
706
+ isValid: () => this.isFieldValid(field, fieldId),
707
+ showError: this.state.showError,
708
+ required: field.mandatory
709
+ });
710
+ case 'checkbox':
711
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
712
+ key: fieldId,
713
+ label: field.label,
714
+ sectionStyle: sectionStyle,
715
+ isValid: () => this.isFieldValid(field, fieldId),
716
+ showError: this.state.showError,
717
+ required: field.mandatory
718
+ }, field.values.map((o, i) => {
719
+ const isActive = field.answer && _.includes(field.answer, o);
720
+ return /*#__PURE__*/React.createElement(TouchableOpacity, {
721
+ onPress: () => this.onChangeCheckboxAnswer(fieldId, o),
722
+ key: i,
723
+ style: styles.multiChoiceOption,
724
+ hitSlop: {
725
+ top: 8,
726
+ left: 8,
727
+ bottom: 8,
728
+ right: 8
729
+ }
730
+ }, isActive ? /*#__PURE__*/React.createElement(Components.TickIcon, {
731
+ style: styles.tick,
732
+ size: 20,
733
+ color: this.props.colourBrandingMain
734
+ }) : /*#__PURE__*/React.createElement(View, {
735
+ style: styles.unticked
736
+ }), /*#__PURE__*/React.createElement(Text, {
737
+ style: styles.multiChoiceText
738
+ }, o));
739
+ }));
740
+ case 'text':
741
+ case 'email':
742
+ case 'phone':
743
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
744
+ key: fieldId,
745
+ label: field.label,
746
+ placeholder: field.placeHolder,
747
+ value: field.answer,
748
+ onChangeText: val => this.onChangeAnswer(fieldId, val),
749
+ editable: true,
750
+ squaredCorners: true,
751
+ isValid: () => this.isFieldValid(field, fieldId),
752
+ showError: this.state.showError,
753
+ errorText: field.type === 'email' ? 'Not a valid email' : undefined,
754
+ required: field.mandatory,
755
+ sectionStyle: sectionStyle,
756
+ autoCapitalize: "sentences",
757
+ keyboardType: this.keyboardTypes[field.type]
758
+ });
759
+ case 'staticTitle':
760
+ return /*#__PURE__*/React.createElement(Text, {
761
+ key: fieldId,
762
+ style: [styles.staticTitle, {
763
+ color: this.props.colourBrandingMain
764
+ }, sectionStyle]
765
+ }, field.label);
766
+ case 'staticText':
767
+ return /*#__PURE__*/React.createElement(View, {
768
+ key: fieldId,
769
+ style: [styles.staticText, sectionStyle]
770
+ }, Helper.toParagraphed(field.label, styles.staticText));
771
+ case 'date':
772
+ case 'time':
773
+ return this.renderDateField(field, fieldId, sectionStyle);
774
+ case 'image':
775
+ return /*#__PURE__*/React.createElement(Components.GenericInputSection, {
776
+ key: fieldId,
777
+ label: field.label,
778
+ sectionStyle: sectionStyle,
779
+ isValid: () => this.isFieldValid(field, fieldId),
780
+ required: field.mandatory,
781
+ showError: this.state.showError
782
+ }, this.renderImageList(fieldId));
783
+ default:
784
+ return null;
785
+ }
786
+ }
787
+ renderCustomFields() {
788
+ const {
789
+ customFields
790
+ } = this.state;
791
+ if (!customFields || customFields.length === 0) return null;
792
+ return /*#__PURE__*/React.createElement(Components.FormCard, {
793
+ style: {
794
+ marginTop: 16
795
+ }
796
+ }, customFields.map((field, i) => this.renderField(field, i)));
797
+ }
405
798
  renderForm() {
799
+ const {
800
+ customFields,
801
+ loadingTypes
802
+ } = this.state;
803
+ const hasCustomFields = customFields && customFields.length > 0;
804
+ if (loadingTypes) {
805
+ return /*#__PURE__*/React.createElement(Components.Spinner, null);
806
+ }
406
807
  return /*#__PURE__*/React.createElement(View, {
407
808
  style: {
408
809
  flex: 1
@@ -464,7 +865,7 @@ class MaintenanceRequest extends Component {
464
865
  },
465
866
  required: true,
466
867
  errorText: "Please provide your address.",
467
- showError: this.state.showError && this.state.roomNumber && this.state.roomNumber.length < 2
868
+ showError: this.state.showError && (!this.state.roomNumber || this.state.roomNumber.length < 2)
468
869
  })), /*#__PURE__*/React.createElement(Components.FormCard, {
469
870
  style: {
470
871
  marginTop: 16,
@@ -493,7 +894,7 @@ class MaintenanceRequest extends Component {
493
894
  fontSize: 20,
494
895
  color: this.props.colourBrandingMain
495
896
  }]
496
- })))), /*#__PURE__*/React.createElement(Components.FormCard, {
897
+ })))), hasCustomFields ? this.renderCustomFields() : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Components.FormCard, {
497
898
  style: {
498
899
  marginTop: 16
499
900
  }
@@ -576,11 +977,11 @@ class MaintenanceRequest extends Component {
576
977
  autoCorrect: true,
577
978
  multiline: true,
578
979
  autoGrow: true
579
- })), this.renderImageList())));
980
+ })), this.renderImageList()))));
580
981
  }
581
982
  renderRegisterConfirmation() {
582
983
  return /*#__PURE__*/React.createElement(Components.ConfirmationPopup, {
583
- confirmText: 'Request submitted',
984
+ confirmText: `${values.textEntityName} submitted`,
584
985
  repeatText: 'Submit another',
585
986
  visible: this.state.confirmationToShow,
586
987
  onClose: this.onCloseConfirmationPopup.bind(this),
@@ -600,7 +1001,25 @@ class MaintenanceRequest extends Component {
600
1001
  onClose: this.toggleFullscreenVideo
601
1002
  });
602
1003
  }
1004
+ renderNoType() {
1005
+ if (!this.state.noType) {
1006
+ return null;
1007
+ }
1008
+ return /*#__PURE__*/React.createElement(Components.WarningPopup, {
1009
+ confirmText: 'No forms are available',
1010
+ infoText: 'Check back later for forms.',
1011
+ visible: this.state.noType,
1012
+ onClose: this.onPressBack.bind(this),
1013
+ padHorizontal: true
1014
+ });
1015
+ }
603
1016
  render() {
1017
+ const {
1018
+ submitting,
1019
+ success,
1020
+ isDateTimePickerVisible,
1021
+ popUpType
1022
+ } = this.state;
604
1023
  return /*#__PURE__*/React.createElement(KeyboardAvoidingView, {
605
1024
  behavior: Platform.OS === 'ios' && 'padding',
606
1025
  style: styles.viewContainer
@@ -609,11 +1028,19 @@ class MaintenanceRequest extends Component {
609
1028
  }, /*#__PURE__*/React.createElement(Components.Header, {
610
1029
  leftIcon: "angle-left",
611
1030
  onPressLeft: this.onPressBack.bind(this),
612
- text: Config.env.strings.MAINTENANCE,
613
- rightText: this.state.submitting || this.state.success ? null : 'Done',
1031
+ text: this.props.strings[`${values.featureKey}_textFeatureTitle`] || values.textFeatureTitle,
1032
+ rightText: submitting || success ? null : 'Done',
614
1033
  onPressRight: this.submitRequest.bind(this),
615
1034
  absoluteRight: true
616
- }), this.renderForm()), this.renderRegisterConfirmation(), this.renderVideoPlayerPopup());
1035
+ }), this.renderForm()), this.renderRegisterConfirmation(), this.renderVideoPlayerPopup(), this.renderNoType(), /*#__PURE__*/React.createElement(DateTimePicker, {
1036
+ isVisible: isDateTimePickerVisible,
1037
+ onConfirm: this.onDateSelected,
1038
+ onCancel: () => this.setState({
1039
+ isDateTimePickerVisible: false
1040
+ }),
1041
+ mode: popUpType,
1042
+ headerTextIOS: `Pick a ${popUpType}`
1043
+ }));
617
1044
  }
618
1045
  }
619
1046
  const styles = {
@@ -715,9 +1142,76 @@ const styles = {
715
1142
  height: 40,
716
1143
  alignItems: 'center',
717
1144
  justifyContent: 'center'
1145
+ },
1146
+ staticTitle: {
1147
+ fontSize: 20,
1148
+ fontFamily: 'sf-semibold',
1149
+ color: Colours.TEXT_DARKEST
1150
+ },
1151
+ staticText: {
1152
+ fontSize: 17,
1153
+ fontFamily: 'sf-regular',
1154
+ color: Colours.TEXT_DARKEST,
1155
+ lineHeight: 24
1156
+ },
1157
+ multiChoiceOption: {
1158
+ marginTop: 16,
1159
+ flexDirection: 'row',
1160
+ alignItems: 'center',
1161
+ minHeight: 20
1162
+ },
1163
+ multiChoiceText: {
1164
+ flex: 1,
1165
+ fontFamily: 'sf-medium',
1166
+ fontSize: 14,
1167
+ color: Colours.TEXT_DARK
1168
+ },
1169
+ tick: {
1170
+ marginRight: 10,
1171
+ borderRadius: 4
1172
+ },
1173
+ unticked: {
1174
+ marginRight: 10,
1175
+ width: 20,
1176
+ height: 20,
1177
+ borderColor: Colours.LINEGREY,
1178
+ borderWidth: 1,
1179
+ borderRadius: 4
1180
+ },
1181
+ dateContainer: {
1182
+ flexDirection: 'row',
1183
+ alignItems: 'center'
1184
+ },
1185
+ dateFieldButton: {
1186
+ flex: 1
1187
+ },
1188
+ dateFieldContainer: {
1189
+ flexDirection: 'row',
1190
+ borderRadius: 2,
1191
+ backgroundColor: '#ebeff2',
1192
+ padding: 8,
1193
+ marginTop: 8
1194
+ },
1195
+ dateText: {
1196
+ flex: 1,
1197
+ fontFamily: 'sf-regular',
1198
+ fontSize: 16,
1199
+ color: '#65686D'
1200
+ },
1201
+ dateIcon: {
1202
+ fontSize: 18,
1203
+ color: Colours.TEXT_BLUEGREY
1204
+ },
1205
+ dateClearButton: {
1206
+ paddingLeft: 12
1207
+ },
1208
+ removeIcon: {
1209
+ fontSize: 26,
1210
+ color: Colours.TEXT_BLUEGREY
718
1211
  }
719
1212
  };
720
1213
  const mapStateToProps = state => {
1214
+ var _state$strings;
721
1215
  const {
722
1216
  user,
723
1217
  connection
@@ -739,7 +1233,8 @@ const mapStateToProps = state => {
739
1233
  site,
740
1234
  unit,
741
1235
  phoneNumber,
742
- colourBrandingMain: Colours.getMainBrandingColourFromState(state)
1236
+ colourBrandingMain: Colours.getMainBrandingColourFromState(state),
1237
+ strings: ((_state$strings = state.strings) === null || _state$strings === void 0 ? void 0 : _state$strings.config) || {}
743
1238
  };
744
1239
  };
745
1240
  export default connect(mapStateToProps, {