@plusscommunities/pluss-maintenance-web-forms 1.2.4-beta.0 → 1.2.5-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 (48) hide show
  1. package/dist/{index.esm.js → index.js} +3307 -2844
  2. package/dist/index.js.map +1 -0
  3. package/package.json +18 -26
  4. package/.babelrc +0 -3
  5. package/dist/index.cjs.js +0 -6232
  6. package/dist/index.umd.js +0 -6225
  7. package/rollup.config.js +0 -59
  8. package/src/actions/JobsActions.js +0 -150
  9. package/src/actions/index.js +0 -1
  10. package/src/actions/types.js +0 -8
  11. package/src/apis/index.js +0 -10
  12. package/src/apis/maintenanceActions.js +0 -203
  13. package/src/apis/reactionActions.js +0 -46
  14. package/src/components/ActivityText.js +0 -57
  15. package/src/components/AnalyticsHub.js +0 -167
  16. package/src/components/Configuration.js +0 -392
  17. package/src/components/JobList.js +0 -1108
  18. package/src/components/JobTypes.js +0 -198
  19. package/src/components/PreviewFull.js +0 -33
  20. package/src/components/PreviewGrid.js +0 -29
  21. package/src/components/PreviewWidget.js +0 -35
  22. package/src/components/ViewFull.js +0 -25
  23. package/src/components/ViewWidget.js +0 -23
  24. package/src/feature.config.js +0 -127
  25. package/src/helper/index.js +0 -26
  26. package/src/images/forms/full.png +0 -0
  27. package/src/images/forms/fullNoTitle.png +0 -0
  28. package/src/images/forms/previewWidget.png +0 -0
  29. package/src/images/forms/widget.png +0 -0
  30. package/src/images/full.png +0 -0
  31. package/src/images/fullNoTitle.png +0 -0
  32. package/src/images/previewWidget.png +0 -0
  33. package/src/images/widget.png +0 -0
  34. package/src/index.js +0 -29
  35. package/src/maintenancePriority.json +0 -5
  36. package/src/maintenanceStatus.json +0 -20
  37. package/src/reducers/MaintenanceReducer.js +0 -49
  38. package/src/screens/AddJob.js +0 -1138
  39. package/src/screens/AddJobType.js +0 -865
  40. package/src/screens/Job.js +0 -1531
  41. package/src/screens/RequestsHub.js +0 -237
  42. package/src/values.config.a.js +0 -63
  43. package/src/values.config.default.js +0 -75
  44. package/src/values.config.enquiry.js +0 -76
  45. package/src/values.config.feedback.js +0 -74
  46. package/src/values.config.food.js +0 -74
  47. package/src/values.config.forms.js +0 -74
  48. package/src/values.config.js +0 -74
@@ -1,1138 +0,0 @@
1
- import _ from "lodash";
2
- import moment from "moment";
3
- import React, { Component } from "react";
4
- import { DropdownButton, DropdownItem } from "react-bootstrap";
5
- import FontAwesome from "react-fontawesome";
6
- import { withRouter } from "react-router";
7
- import { connect } from "react-redux";
8
- import { jobsLoaded, jobsUpdate } from "../actions";
9
- import { PlussCore } from "../feature.config";
10
- import { maintenanceActions, userActions } from "../apis";
11
- import { values } from "../values.config";
12
-
13
- const { Actions, Components, Helper, Session, Colours, Apis } = PlussCore;
14
-
15
- class AddJob extends Component {
16
- constructor(props) {
17
- super(props);
18
- this.imageInput = null;
19
- this.customImageInputs = {};
20
- this.customDocumentInputs = {};
21
- this.state = {
22
- jobId: Helper.safeReadParams(this.props, "jobId")
23
- ? this.props.match.params.jobId
24
- : null,
25
- job: null,
26
- showingSelector: false,
27
- updating: false,
28
- connected: false,
29
- types: [],
30
- users: [],
31
- images: [],
32
- userSearch: "",
33
- userFilterOpen: false,
34
- selectedUser: null,
35
- id: null,
36
- userID: "",
37
- userName: "",
38
- room: "",
39
- phone: "",
40
- location: this.props.auth.site,
41
- title: "",
42
- description: "",
43
- isHome: false,
44
- homeText: "",
45
- prevType: "General",
46
- type: "General",
47
- image: null,
48
- thumbnail: null,
49
- showWarnings: false,
50
- success: false,
51
- prevCustomFileds: [],
52
- customFields: [],
53
- showDate: {},
54
- };
55
- }
56
-
57
- UNSAFE_componentWillMount() {
58
- Session.checkLoggedIn(this, this.props.auth);
59
- }
60
-
61
- componentDidMount() {
62
- this.getJobTypes();
63
- this.getUsers();
64
- if (this.state.jobId) this.getJob();
65
- this.props.addRecentlyCreated(values.featureKey);
66
- }
67
-
68
- getJob = async () => {
69
- try {
70
- const res = await maintenanceActions.getJob(
71
- this.props.auth.site,
72
- this.state.jobId,
73
- );
74
- res.data.location = res.data.site;
75
- const { userID, userName, userProfilePic, type, customFields } = res.data;
76
- this.setState({
77
- ...res.data,
78
- prevType: type,
79
- prevCustomFileds: customFields,
80
- type,
81
- customFields,
82
- selectedUser: {
83
- userId: userID,
84
- displayName: userName,
85
- profilePic: userProfilePic,
86
- },
87
- });
88
- this.checkSetImages(this.imageInput, res.data.images);
89
- if (customFields) {
90
- customFields.forEach((field, index) => {
91
- if (field.type === "image" && field.answer) {
92
- this.checkSetImages(this.customImageInputs[index], field.answer);
93
- }
94
- });
95
- }
96
- } catch (error) {
97
- console.error("getJob", error);
98
- }
99
- };
100
-
101
- checkSetImages(imageRef, images) {
102
- if (imageRef) {
103
- if (!_.isEmpty(images)) {
104
- imageRef.setValue(images);
105
- }
106
- } else {
107
- setTimeout(() => {
108
- this.checkSetImages(images);
109
- }, 100);
110
- }
111
- }
112
-
113
- getJobTypes = async () => {
114
- try {
115
- const res = await maintenanceActions.getJobTypes(this.props.auth.site);
116
- this.setState({ types: res.data });
117
- this.getDefaultJob();
118
- } catch (error) {
119
- console.error("getJobTypes", error);
120
- }
121
- };
122
-
123
- getUsers = async () => {
124
- try {
125
- const res = await userActions.fetchUsers(this.props.auth.site);
126
- if (res.userFetchFail) return;
127
- if (res.data != null && !_.isEmpty(res.data.results.Items)) {
128
- let items = res.data.results.Items;
129
- if (this.props.optionOnlyForResidents) {
130
- items = _.filter(items, (u) => u.category === "resident");
131
- }
132
- this.setState({
133
- users: _.sortBy(items, (u) => {
134
- return (u.displayName || "").toLowerCase();
135
- }),
136
- });
137
- }
138
- } catch (error) {
139
- console.error("getUsers", error);
140
- }
141
- };
142
-
143
- getDefaultJob = () => {
144
- const { types, jobId } = this.state;
145
- if (jobId == null) {
146
- if (types.length !== 0) {
147
- const defaultType = types[0];
148
- this.setState({
149
- type: defaultType.typeName,
150
- customFields:
151
- defaultType.hasCustomFields && defaultType.customFields.length > 0
152
- ? defaultType.customFields
153
- : [],
154
- });
155
- } else if (values.forceCustomFields) {
156
- this.setState({ noTypes: true });
157
- } else {
158
- this.setState({ type: "General" });
159
- }
160
- }
161
- };
162
-
163
- onSelectType = (key, e) => {
164
- const { types, prevType, prevCustomFileds } = this.state;
165
- const selectedType = types.find((t) => t.typeName === key);
166
- // If selected type had previously saved custom fields, use the previous version
167
- const hasPrevCustomFields =
168
- prevType === selectedType.typeName &&
169
- prevCustomFileds &&
170
- prevCustomFileds.length > 0;
171
- const update = {
172
- type: selectedType.typeName,
173
- customFields: hasPrevCustomFields
174
- ? prevCustomFileds
175
- : selectedType.hasCustomFields
176
- ? selectedType.customFields
177
- : [],
178
- };
179
-
180
- if (
181
- !_.isEmpty(update.customFields) &&
182
- !_.some(update.customFields, "isTitle")
183
- ) {
184
- update.title = this.state.selectedUser
185
- ? this.state.selectedUser.displayName
186
- : "";
187
- }
188
-
189
- this.setState(update);
190
- };
191
-
192
- renderTypeOptions() {
193
- const { types, type } = this.state;
194
- return types.map((ev) => {
195
- if (ev != null) {
196
- return (
197
- <DropdownItem
198
- key={ev.typeName}
199
- eventKey={ev.typeName}
200
- ive={type === ev.typeName}
201
- >
202
- {ev.typeName}
203
- </DropdownItem>
204
- );
205
- }
206
- return null;
207
- });
208
- }
209
-
210
- onHandleChange = (event) => {
211
- var stateChange = {};
212
- stateChange[event.target.getAttribute("id")] = event.target.value;
213
- this.setState(stateChange);
214
- };
215
-
216
- onOpenUserSelector = () => {
217
- this.setState({ userFilterOpen: true });
218
- };
219
-
220
- onCloseUserSelector = () => {
221
- this.setState({ userFilterOpen: false });
222
- };
223
-
224
- onSelectUser = (user) => {
225
- const update = {
226
- selectedUser: user,
227
- userID: user.userId,
228
- userName: user.displayName,
229
- userFilterOpen: false,
230
- };
231
- if (
232
- !_.isEmpty(this.state.customFields) &&
233
- !_.some(this.state.customFields, "isTitle")
234
- ) {
235
- update.title = user.displayName;
236
- }
237
-
238
- // Update UI immediately (non-blocking)
239
- this.setState(update);
240
-
241
- // PC-1255: Auto-populate contact details when staff create requests on behalf of residents
242
- // Requires userManagement permission - gracefully falls back to manual entry if denied
243
- // Fetch in background to avoid blocking UI
244
- userActions
245
- .fetchUser(this.props.auth.site, user.userId)
246
- .then((response) => {
247
- if (response.data && response.data.user) {
248
- const contactUpdate = {};
249
- // Auto-populate phone and room from user profile
250
- if (response.data.user.phoneNumber) {
251
- contactUpdate.phone = response.data.user.phoneNumber;
252
- }
253
- if (response.data.user.unit) {
254
- contactUpdate.room = response.data.user.unit;
255
- }
256
- // Update contact fields when data arrives
257
- if (Object.keys(contactUpdate).length > 0) {
258
- this.setState(contactUpdate);
259
- }
260
- }
261
- })
262
- .catch((error) => {
263
- // Permission denied (403) or other error - continue without auto-population
264
- // Staff can still create the request, just need to enter contact details manually
265
- console.log("Could not fetch user details for auto-population:", error);
266
- });
267
- };
268
-
269
- onUnselectUser = () => {
270
- const update = {
271
- selectedUser: null,
272
- userID: "",
273
- userName: "",
274
- phone: "",
275
- room: "",
276
- };
277
- if (
278
- !_.isEmpty(this.state.customFields) &&
279
- !_.some(this.state.customFields, "isTitle")
280
- ) {
281
- update.title = "";
282
- }
283
- this.setState(update);
284
- };
285
-
286
- onChangeAnswer = (qId, answer) => {
287
- const update = { customFields: _.cloneDeep(this.state.customFields) };
288
- const field = update.customFields[qId];
289
- field.answer = answer;
290
- if (field.isTitle) update.title = field.answer;
291
- this.setState(update);
292
- };
293
-
294
- onChangeToggleAnswer = (qId, answer) => {
295
- const update = { customFields: _.cloneDeep(this.state.customFields) };
296
- const field = update.customFields[qId];
297
- field.answer = field.answer === answer ? undefined : answer;
298
- if (field.isTitle) update.title = field.answer;
299
- this.setState(update);
300
- };
301
-
302
- onChangeCheckboxAnswer = (qId, answer) => {
303
- const update = { customFields: _.cloneDeep(this.state.customFields) };
304
- const field = update.customFields[qId];
305
- field.answer = _.xor(field.answer || [], [answer]);
306
- if (field.isTitle) update.title = field.answer.join(", ");
307
- this.setState(update);
308
- };
309
-
310
- onChangeDateAnswer = (qId, answer, togglePicker = true) => {
311
- const update = { customFields: _.cloneDeep(this.state.customFields) };
312
- const field = update.customFields[qId];
313
- field.answer = answer;
314
- if (field.isTitle)
315
- update.title = moment(field.answer, "YYYY-MM-DD").format("DD-MMM-YYYY");
316
- this.setState(update);
317
-
318
- if (togglePicker) this.onToggleDatePicker(qId);
319
- };
320
-
321
- onChangeTimeAnswer = (qId, answer) => {
322
- const update = { customFields: _.cloneDeep(this.state.customFields) };
323
- const field = update.customFields[qId];
324
- field.answer = answer;
325
- if (field.isTitle)
326
- update.title = moment(field.answer, "HH:mm").format("h:mm a");
327
- this.setState(update);
328
- };
329
-
330
- onChangeImageAnswer = (qId, answer) => {
331
- const update = { customFields: _.cloneDeep(this.state.customFields) };
332
- const field = update.customFields[qId];
333
- field.answer = answer;
334
- this.setState(update);
335
- };
336
-
337
- onRemoveDocumentAnswer = (qId, document) => {
338
- const update = { customFields: _.cloneDeep(this.state.customFields) };
339
- const field = update.customFields[qId];
340
- field.answer = _.filter(field.answer, (d) => d.url !== document.url);
341
- this.setState(update);
342
- };
343
-
344
- onHandlePDFFileChange = (event, qId) => {
345
- const file = event.target.files[0];
346
- if (!file) return;
347
-
348
- const update = { customFields: _.cloneDeep(this.state.customFields) };
349
- const field = update.customFields[qId];
350
- const attachments = field.answer || [];
351
- const [name, ext] = file.name.split(".");
352
- const newAttachment = {
353
- uploading: true,
354
- name,
355
- ext: ext.toLowerCase(),
356
- };
357
- attachments.push(newAttachment);
358
- field.answer = attachments;
359
- this.setState(update);
360
-
361
- Apis.fileActions
362
- .uploadMediaAsync(file, file.name)
363
- .then((fileRes) => {
364
- newAttachment.url = fileRes;
365
- delete newAttachment.uploading;
366
- this.setState(update);
367
- })
368
- .catch((uploadErrorRes) => {
369
- console.log(uploadErrorRes);
370
- delete newAttachment.uploading;
371
- this.setState(update);
372
- });
373
- event.target.value = "";
374
- };
375
-
376
- onToggleDatePicker = (qId) => {
377
- const showDate = { ...this.state.showDate };
378
- showDate[qId] = !showDate[qId];
379
- this.setState({ showDate });
380
- };
381
-
382
- onSave = () => {
383
- this.setState({ showWarnings: false });
384
- if (!this.validateForm()) {
385
- this.setState({ showWarnings: true });
386
- return;
387
- }
388
- if (this.state.updating) return;
389
- this.setState({ updating: true });
390
-
391
- const job = {
392
- id: this.state.id,
393
- userID: this.state.userID,
394
- userName: this.state.userName,
395
- room: this.state.room,
396
- phone: this.state.phone,
397
- location: this.state.location,
398
- title: this.state.title,
399
- description: this.state.description,
400
- isHome: this.state.isHome,
401
- homeText: this.state.homeText,
402
- type: this.state.type,
403
- date: null,
404
- images: this.state.images,
405
- customFields: this.state.customFields,
406
- };
407
-
408
- if (this.state.id != null) {
409
- maintenanceActions
410
- .editJob(job, this.props.auth.site)
411
- .then((res) => {
412
- this.setState({
413
- success: true,
414
- updating: false,
415
- });
416
- this.props.jobsLoaded([job]);
417
- })
418
- .catch((res) => {
419
- this.setState({ updating: false });
420
- alert("Something went wrong with the request. Please try again.");
421
- });
422
- } else {
423
- // Create New Job
424
- maintenanceActions
425
- .createJob(job)
426
- .then((res) => {
427
- this.setState({
428
- success: true,
429
- updating: false,
430
- });
431
- this.props.jobsUpdate(this.props.auth.site);
432
- })
433
- .catch((res) => {
434
- this.setState({ updating: false });
435
- alert("Something went wrong with the request. Please try again.");
436
- });
437
- }
438
- };
439
-
440
- renderSuccess() {
441
- if (!this.state.success) return null;
442
-
443
- const title =
444
- this.props.strings[`${values.featureKey}_textTitleRequests`] ||
445
- values.textTitleRequests;
446
- return (
447
- <Components.SuccessPopup
448
- text={`${values.textEntityName} has been ${this.state.id != null ? "edited" : "added"}`}
449
- buttons={[
450
- {
451
- type: "outlined",
452
- onClick: () => {
453
- window.history.back();
454
- },
455
- text: `Back to ${title}`,
456
- },
457
- ]}
458
- />
459
- );
460
- }
461
-
462
- isFieldValid = (field) => {
463
- const { mandatory, type, answer } = field;
464
- if (["staticTitle", "staticText"].includes(type)) return true;
465
-
466
- const checkMandatory = () => {
467
- if (!mandatory) return true;
468
- switch (type) {
469
- case "yn":
470
- return _.isBoolean(answer);
471
- case "image":
472
- case "document":
473
- case "checkbox":
474
- return _.isArray(answer) && answer.length > 0;
475
- default:
476
- return !_.isNil(answer) && !_.isEmpty(answer);
477
- }
478
- };
479
- const checkFormat = () => {
480
- if (_.isNil(answer) || _.isEmpty(answer)) return true;
481
- switch (type) {
482
- case "email":
483
- return Helper.isEmail(answer);
484
- case "date":
485
- return moment(answer, "YYYY-MM-DD", true).isValid();
486
- case "time":
487
- return moment(answer, "HH:mm", true).isValid();
488
- default:
489
- return true;
490
- }
491
- };
492
-
493
- const valid = checkMandatory() && checkFormat();
494
- return valid;
495
- };
496
-
497
- validateCustomFields() {
498
- const { customFields } = this.state;
499
- if (!customFields || customFields.length === 0) return true;
500
-
501
- return customFields.every((field) => {
502
- const isValid = this.isFieldValid(field);
503
- return isValid;
504
- });
505
- }
506
-
507
- validateForm() {
508
- if (_.isEmpty(this.state.userID)) return false;
509
- if (_.isEmpty(this.state.userName)) return false;
510
- if (_.isEmpty(this.state.room)) return false;
511
- if (_.isEmpty(this.state.title)) return false;
512
- if (this.state.isHome && _.isEmpty(this.state.homeText)) return false;
513
- if (!this.validateCustomFields()) return false;
514
- return true;
515
- }
516
-
517
- getFieldContainerClass = (isValid = true) => {
518
- const showError = this.state.showWarnings && !isValid;
519
- return `genericInputContainer ${isValid ? "genericInput-valid" : ""} ${showError ? "genericInput-error" : ""}`.trim();
520
- };
521
-
522
- renderSubmit() {
523
- if (this.state.updating) {
524
- return (
525
- <Components.Button buttonType="secondary">Saving...</Components.Button>
526
- );
527
- }
528
-
529
- return (
530
- <div>
531
- <Components.Button
532
- inline
533
- buttonType="tertiary"
534
- onClick={() => this.props.history.push(values.routeRequestsHub)}
535
- isActive
536
- style={{ marginRight: 16 }}
537
- >
538
- Cancel
539
- </Components.Button>
540
- <Components.Button
541
- inline
542
- buttonType="primary"
543
- onClick={this.onSave}
544
- isActive={this.validateForm()}
545
- >
546
- Save
547
- </Components.Button>
548
- </div>
549
- );
550
- }
551
-
552
- renderSelectUser() {
553
- const { showWarnings, selectedUser } = this.state;
554
- const isValid = !_.isNil(selectedUser);
555
- return (
556
- <div className={this.getFieldContainerClass(isValid)}>
557
- <div style={styles.userLabelContainer}>
558
- <div className="fieldLabel">User</div>
559
- {showWarnings && !isValid ? (
560
- <div className="fieldLabel fieldLabel-warning">Required</div>
561
- ) : null}
562
- </div>
563
- <div style={styles.fieldContainer}>
564
- <div className="inputRequired " />
565
- {selectedUser ? (
566
- <Components.Tag
567
- className="marginRight-10"
568
- rightIcon="close"
569
- rightClick={this.onUnselectUser}
570
- >
571
- <Components.UserListing
572
- size={15}
573
- user={selectedUser}
574
- textClass="tag_text"
575
- />
576
- </Components.Tag>
577
- ) : (
578
- <Components.Tag
579
- onClick={this.onOpenUserSelector}
580
- text="Select User"
581
- />
582
- )}
583
- </div>
584
- </div>
585
- );
586
- }
587
-
588
- renderDefaultFields() {
589
- return (
590
- <div>
591
- <Components.GenericInput
592
- id="title"
593
- label="Title for the work required"
594
- type="textarea"
595
- placeholder="Title for the work required"
596
- value={this.state.title}
597
- onChange={(e) => this.onHandleChange(e)}
598
- inputStyle={{
599
- height: 80,
600
- }}
601
- isRequired
602
- isValid={() => {
603
- return !_.isEmpty(this.state.title);
604
- }}
605
- showError={() => {
606
- return this.state.showWarnings && _.isEmpty(this.state.title);
607
- }}
608
- alwaysShowLabel
609
- />
610
- <Components.GenericInput
611
- id="description"
612
- label="Description of work required"
613
- type="textarea"
614
- placeholder="Description of work required"
615
- value={this.state.description}
616
- onChange={(e) => this.onHandleChange(e)}
617
- inputStyle={{
618
- height: 80,
619
- }}
620
- alwaysShowLabel
621
- />
622
- <div className="marginBottom-16">
623
- <Components.Text type="formLabel" className="marginBottom-4">
624
- Images
625
- </Components.Text>
626
- <Components.ImageInput
627
- ref={(ref) => {
628
- this.imageInput = ref;
629
- }}
630
- multiple
631
- refreshCallback={(images) => {
632
- this.setState({ images });
633
- }}
634
- />
635
- </div>
636
- <Components.RadioButton
637
- label="Person must be home during work?"
638
- isActive={this.state.isHome}
639
- options={[
640
- {
641
- Label: "No",
642
- Value: false,
643
- onChange: () => this.setState({ isHome: false }),
644
- },
645
- {
646
- Label: "Yes",
647
- Value: true,
648
- onChange: () => this.setState({ isHome: true }),
649
- },
650
- ]}
651
- />
652
- {this.state.isHome && (
653
- <Components.GenericInput
654
- style={{ marginTop: 16 }}
655
- label="Description of person's available times"
656
- id="homeText"
657
- type="textarea"
658
- placeholder="Description of person's available times"
659
- value={this.state.homeText}
660
- onChange={(e) => this.onHandleChange(e)}
661
- inputStyle={{
662
- height: 80,
663
- }}
664
- isRequired
665
- isValid={() => {
666
- return !_.isEmpty(this.state.homeText);
667
- }}
668
- showError={() => {
669
- return this.state.showWarnings && _.isEmpty(this.state.homeText);
670
- }}
671
- alwaysShowLabel
672
- />
673
- )}
674
- </div>
675
- );
676
- }
677
-
678
- renderField(field, fieldId) {
679
- switch (field.type) {
680
- case "yn":
681
- return (
682
- <div
683
- key={fieldId}
684
- className={`visitorSignIn_question ${this.getFieldContainerClass(this.isFieldValid(field))}`}
685
- style={styles.fieldContainer}
686
- >
687
- {field.mandatory ? <div className="inputRequired " /> : null}
688
- <Components.RadioButton
689
- label={field.label}
690
- labelStyle={{ color: this.props.colour }}
691
- isActive={field.answer}
692
- noHoverHighlight
693
- highlightColour={this.props.colour}
694
- options={[
695
- {
696
- Label: "Yes",
697
- Value: true,
698
- onChange: this.onChangeToggleAnswer.bind(this, fieldId, true),
699
- },
700
- {
701
- Label: "No",
702
- Value: false,
703
- onChange: this.onChangeToggleAnswer.bind(
704
- this,
705
- fieldId,
706
- false,
707
- ),
708
- },
709
- ]}
710
- />
711
- </div>
712
- );
713
- case "multichoice":
714
- return (
715
- <div
716
- key={fieldId}
717
- className={`visitorSignIn_question ${this.getFieldContainerClass(this.isFieldValid(field))}`}
718
- style={styles.fieldContainer}
719
- >
720
- {field.mandatory ? <div className="inputRequired " /> : null}
721
- <Components.RadioButton
722
- label={field.label}
723
- labelStyle={{ color: this.props.colour }}
724
- isActive={field.answer}
725
- noHoverHighlight
726
- highlightColour={this.props.colour}
727
- options={field.values.map((o) => {
728
- return {
729
- Label: o,
730
- Value: o,
731
- onChange: this.onChangeToggleAnswer.bind(this, fieldId, o),
732
- };
733
- })}
734
- rowStyle={{ flexDirection: "column" }}
735
- buttonStyle={{ marginTop: "5px", marginBottom: "5px" }}
736
- />
737
- </div>
738
- );
739
- case "checkbox":
740
- return (
741
- <div
742
- key={fieldId}
743
- className={this.getFieldContainerClass(this.isFieldValid(field))}
744
- style={styles.fieldContainer}
745
- >
746
- {field.mandatory ? <div className="inputRequired " /> : null}
747
- <div className="visitorSignIn_question" style={{ flex: 1 }}>
748
- <div
749
- className="fieldLabel"
750
- style={{ marginBottom: "5px", color: this.props.colour }}
751
- >
752
- {field.label}
753
- </div>
754
- {field.values.map((option, optionIndex) => {
755
- return (
756
- <Components.CheckBox
757
- key={optionIndex}
758
- label={option}
759
- isActive={field.answer && field.answer.includes(option)}
760
- highlightColour={this.props.colour}
761
- noHoverHighlight
762
- onChange={() =>
763
- this.onChangeCheckboxAnswer(fieldId, option)
764
- }
765
- />
766
- );
767
- })}
768
- </div>
769
- </div>
770
- );
771
- case "text":
772
- case "email":
773
- case "phone":
774
- return (
775
- <div key={fieldId}>
776
- <Components.GenericInput
777
- id={fieldId}
778
- type="text"
779
- label={field.label}
780
- placeholder={field.placeHolder}
781
- value={field.answer}
782
- onChange={(e) => this.onChangeAnswer(fieldId, e.target.value)}
783
- isRequired={field.mandatory}
784
- isValid={() => this.isFieldValid(field)}
785
- showError={() =>
786
- this.state.showWarnings && !this.isFieldValid(field)
787
- }
788
- errorMessage={
789
- field.type === "email" ? "Not a valid email" : undefined
790
- }
791
- alwaysShowLabel
792
- />
793
- </div>
794
- );
795
- case "staticTitle":
796
- return (
797
- <p
798
- className="visitorSignIn_text-staticTitle"
799
- style={{ color: this.props.colour }}
800
- key={fieldId}
801
- >
802
- {field.label}
803
- </p>
804
- );
805
- case "staticText":
806
- return (
807
- <p className="visitorSignIn_text-staticText" key={fieldId}>
808
- {Helper.toParagraphed(field.label, { marginTop: 10 })}
809
- </p>
810
- );
811
- case "date":
812
- return (
813
- <div key={fieldId}>
814
- <Components.GenericInput
815
- id={fieldId}
816
- label={field.label}
817
- placeholder={"DD-MMM-YYYY"}
818
- value={
819
- field.answer
820
- ? moment(field.answer, "YYYY-MM-DD").format("DD-MMM-YYYY")
821
- : ""
822
- }
823
- onClick={(e) => this.onToggleDatePicker(fieldId)}
824
- isRequired={field.mandatory}
825
- isValid={() => this.isFieldValid(field)}
826
- showError={() =>
827
- this.state.showWarnings && !this.isFieldValid(field)
828
- }
829
- errorMessage="Not a valid date"
830
- alwaysShowLabel
831
- readOnly
832
- rightContent={
833
- !_.isEmpty(field.answer) && (
834
- <Components.SVGIcon
835
- colour={Colours.COLOUR_DUSK_LIGHT}
836
- icon="close"
837
- className="timepicker_clear"
838
- onClick={() =>
839
- this.onChangeDateAnswer(fieldId, undefined, false)
840
- }
841
- />
842
- )
843
- }
844
- />
845
- {this.state.showDate[fieldId] && (
846
- <Components.DatePicker
847
- selectedDate={field.answer}
848
- selectDate={(date) => this.onChangeDateAnswer(fieldId, date)}
849
- />
850
- )}
851
- </div>
852
- );
853
- case "time":
854
- return (
855
- <div key={fieldId}>
856
- <Components.GenericInput
857
- id={fieldId}
858
- label={field.label}
859
- placeholder={"--:-- --"}
860
- value={
861
- field.answer
862
- ? moment(field.answer, "HH:mm").format("h:mm a")
863
- : ""
864
- }
865
- type="time"
866
- isRequired={field.mandatory}
867
- isValid={() => this.isFieldValid(field)}
868
- showError={() =>
869
- this.state.showWarnings && !this.isFieldValid(field)
870
- }
871
- errorMessage="Not a valid time"
872
- alwaysShowLabel
873
- inputComponent={
874
- <Components.TimePicker
875
- selectedTime={field.answer}
876
- selectTime={(time) => this.onChangeTimeAnswer(fieldId, time)}
877
- className="timepicker-condensed"
878
- callbackFormat="HH:mm"
879
- style={{ width: "100%" }}
880
- />
881
- }
882
- rightContent={
883
- !_.isEmpty(field.answer) && (
884
- <Components.SVGIcon
885
- colour={Colours.COLOUR_DUSK_LIGHT}
886
- icon="close"
887
- className="timepicker_clear"
888
- onClick={() => this.onChangeTimeAnswer(fieldId, undefined)}
889
- />
890
- )
891
- }
892
- />
893
- </div>
894
- );
895
- case "image":
896
- return (
897
- <div
898
- key={fieldId}
899
- className={this.getFieldContainerClass(this.isFieldValid(field))}
900
- style={styles.fieldContainer}
901
- >
902
- {field.mandatory ? <div className="inputRequired " /> : null}
903
- <div className="visitorSignIn_question" style={{ flex: 1 }}>
904
- <Components.Text type="formLabel" className="marginBottom-4">
905
- {field.label}
906
- </Components.Text>
907
- <Components.ImageInput
908
- ref={(ref) => (this.customImageInputs[fieldId] = ref)}
909
- multiple
910
- refreshCallback={(images) =>
911
- this.onChangeImageAnswer(fieldId, images)
912
- }
913
- />
914
- </div>
915
- </div>
916
- );
917
- case "document":
918
- const documents = field.answer || [];
919
- return (
920
- <div
921
- key={fieldId}
922
- className={this.getFieldContainerClass(this.isFieldValid(field))}
923
- style={styles.fieldContainer}
924
- >
925
- {field.mandatory ? <div className="inputRequired " /> : null}
926
- <div className="visitorSignIn_question" style={{ flex: 1 }}>
927
- <Components.Text type="formLabel" className="marginBottom-4">
928
- {field.label}
929
- </Components.Text>
930
- {documents.map((doc, index) => (
931
- <Components.Attachment
932
- key={index}
933
- uploading={doc.uploading}
934
- source={doc.url}
935
- title={doc.name}
936
- onRemove={() => this.onRemoveDocumentAnswer(fieldId, doc)}
937
- />
938
- ))}
939
- <input
940
- ref={(input) => (this.customDocumentInputs[fieldId] = input)}
941
- id={`documentInput-${fieldId}`}
942
- type="file"
943
- className="fileInput"
944
- onChange={(e) => this.onHandlePDFFileChange(e, fieldId)}
945
- accept="application/pdf"
946
- />
947
- <div
948
- className="iconTextButton marginBottom-16"
949
- onClick={() => this.customDocumentInputs[fieldId].click()}
950
- >
951
- <FontAwesome className="iconTextButton_icon" name="paperclip" />
952
- <p className="iconTextButton_text">Add Attachment</p>
953
- </div>
954
- </div>
955
- </div>
956
- );
957
- default:
958
- return null;
959
- }
960
- }
961
-
962
- renderCustomFields() {
963
- const { customFields } = this.state;
964
- if (!customFields || customFields.length === 0) return null;
965
-
966
- return (
967
- <div>
968
- {customFields.map((field, i) => {
969
- return this.renderField(field, i);
970
- })}
971
- </div>
972
- );
973
- }
974
-
975
- renderMain() {
976
- const { customFields } = this.state;
977
-
978
- return (
979
- <div style={{ marginBottom: 15 }}>
980
- <div className="padding-60 paddingVertical-40 bottomDivideBorder">
981
- <Components.Text type="formTitleLarge" className="marginBottom-24">
982
- {this.state.infoId == null ? "New" : "Edit"}{" "}
983
- {values.textSingularName}
984
- </Components.Text>
985
- {/* Resident Information */}
986
- {this.renderSelectUser()}
987
- <Components.GenericInput
988
- id="phone"
989
- type="text"
990
- label="Contact number"
991
- placeholder="04XX XXX XXX"
992
- value={this.state.phone}
993
- // showError={this.state.showWarnings && !this.validateImage()}
994
- onChange={(e) => this.onHandleChange(e)}
995
- alwaysShowLabel
996
- />
997
- <Components.GenericInput
998
- id="room"
999
- type="text"
1000
- label="Address"
1001
- placeholder="Insert address here"
1002
- value={this.state.room}
1003
- onChange={(e) => this.onHandleChange(e)}
1004
- isRequired
1005
- alwaysShowLabel
1006
- isValid={() => {
1007
- return !_.isEmpty(this.state.room);
1008
- }}
1009
- showError={() => {
1010
- return this.state.showWarnings && _.isEmpty(this.state.room);
1011
- }}
1012
- />
1013
- <div style={{ marginBottom: 15 }}>
1014
- <Components.Text type="formLabel">
1015
- {values.textJobType}
1016
- </Components.Text>
1017
- <DropdownButton
1018
- style={{ minWidth: 80 }}
1019
- bsStyle="default"
1020
- title={this.state.type}
1021
- id="typeSelect"
1022
- onSelect={this.onSelectType}
1023
- >
1024
- {this.renderTypeOptions()}
1025
- </DropdownButton>
1026
- </div>
1027
- {!_.isEmpty(customFields) || values.forceCustomFields
1028
- ? this.renderCustomFields()
1029
- : this.renderDefaultFields()}
1030
- </div>
1031
- </div>
1032
- );
1033
- }
1034
-
1035
- renderUserFilterPopup() {
1036
- const { userFilterOpen, userSearch, users } = this.state;
1037
- if (!userFilterOpen) return null;
1038
- return (
1039
- <Components.Popup
1040
- title="Select Requestor"
1041
- maxWidth={600}
1042
- minWidth={400}
1043
- maxHeight={600}
1044
- minHeight={600}
1045
- hasPadding
1046
- onClose={this.onCloseUserSelector}
1047
- buttons={[
1048
- {
1049
- type: "tertiary",
1050
- onClick: this.onCloseUserSelector,
1051
- isActive: true,
1052
- text: "Cancel",
1053
- },
1054
- ]}
1055
- >
1056
- <Components.GenericInput
1057
- id="userSearch"
1058
- type="text"
1059
- label="Search User"
1060
- placeholder="Search name"
1061
- value={userSearch}
1062
- onChange={(e) => this.onHandleChange(e)}
1063
- alwaysShowLabel
1064
- />
1065
- {_.sortBy(users, (u) => u.displayName.toUpperCase())
1066
- .filter((u) => {
1067
- if (_.isEmpty(userSearch)) return true;
1068
- return (
1069
- u.displayName.toUpperCase().indexOf(userSearch.toUpperCase()) > -1
1070
- );
1071
- })
1072
- .map((user) => {
1073
- return (
1074
- <Components.UserListing
1075
- key={user.userId}
1076
- user={user}
1077
- onClick={() => this.onSelectUser(user)}
1078
- />
1079
- );
1080
- })}
1081
- </Components.Popup>
1082
- );
1083
- }
1084
-
1085
- render() {
1086
- const { success } = this.state;
1087
-
1088
- return (
1089
- <Components.OverlayPage>
1090
- <Components.OverlayPageContents noBottomButtons={success}>
1091
- <Components.OverlayPageSection className="pageSectionWrapper--newPopup">
1092
- <div>
1093
- {this.renderSuccess()}
1094
- {!success && this.renderMain()}
1095
- </div>
1096
- {this.renderUserFilterPopup()}
1097
- </Components.OverlayPageSection>
1098
- </Components.OverlayPageContents>
1099
- <Components.OverlayPageBottomButtons>
1100
- {this.renderSubmit()}
1101
- </Components.OverlayPageBottomButtons>
1102
- </Components.OverlayPage>
1103
- );
1104
- }
1105
- }
1106
-
1107
- const styles = {
1108
- userLabelContainer: {
1109
- display: "flex",
1110
- flexDirection: "row",
1111
- alignItems: "center",
1112
- marginBottom: 0,
1113
- justifyContent: "space-between",
1114
- },
1115
- fieldContainer: {
1116
- display: "flex",
1117
- flexDirection: "row",
1118
- alignItems: "center",
1119
- },
1120
- };
1121
-
1122
- const mapStateToProps = (state) => {
1123
- const { auth } = state;
1124
- return {
1125
- auth,
1126
- strings: (state.strings && state.strings.config) || {},
1127
- optionOnlyForResidents: Helper.getSiteSettingFromState(
1128
- state,
1129
- values.optionOnlyForResidents,
1130
- ),
1131
- };
1132
- };
1133
-
1134
- export default connect(mapStateToProps, {
1135
- jobsUpdate,
1136
- jobsLoaded,
1137
- addRecentlyCreated: Actions.addRecentlyCreated,
1138
- })(withRouter(AddJob));