@plusscommunities/pluss-maintenance-web 1.2.4-beta.0 → 1.2.4-beta.2

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} +3176 -2644
  2. package/dist/index.js.map +1 -0
  3. package/package.json +13 -20
  4. package/.babelrc +0 -3
  5. package/dist/index.cjs.js +0 -6234
  6. package/dist/index.umd.js +0 -6227
  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 -75
@@ -1,1108 +0,0 @@
1
- import React, { Component } from 'react';
2
- import { Table } from 'react-bootstrap';
3
- import { connect } from 'react-redux';
4
- import { withRouter } from 'react-router';
5
- import { Link } from 'react-router-dom';
6
- import FontAwesome from 'react-fontawesome';
7
- import moment from 'moment';
8
- import _ from 'lodash';
9
- import { jobsLoaded, removeJob, jobStatusesUpdate } from '../actions';
10
- import { PlussCore } from '../feature.config';
11
- import { maintenanceActions } from '../apis';
12
- import {
13
- STATUS_NOT_ACTIONED,
14
- STATUS_COMPLETED,
15
- STATUS_IMCOMPLETE,
16
- jobPriorityOptions,
17
- getDefaultPriority,
18
- getJobPriority,
19
- } from '../helper';
20
- import { Colours } from '@plusscommunities/pluss-core-web';
21
- import { values } from '../values.config';
22
-
23
- const { Session, Components, Analytics } = PlussCore;
24
-
25
- class JobList extends Component {
26
- constructor(props) {
27
- super(props);
28
- this.state = {
29
- showCompleted: false,
30
- sortColumn: 'createdUnix',
31
- sortDesc: true,
32
- selectedTimeFilter: Analytics.getAnalyticsFilterOptions()[1],
33
- assignees: [],
34
- requesters: [],
35
- };
36
-
37
- this.exportColumns = [
38
- { label: 'Select All', key: '' },
39
- { label: 'System Id', key: 'id' },
40
- { label: `${values.textEntityName} No.`, key: 'jobId' },
41
- { label: values.textJobType, key: 'type' },
42
- { label: 'Status', key: 'status' },
43
- { label: 'Priority', key: 'priority' },
44
- { label: 'Title', key: 'title' },
45
- { label: 'Address', key: 'room' },
46
- { label: 'Description', key: 'description' },
47
- { label: 'Notes', key: 'notes' },
48
- { label: 'User Name', key: 'userName' },
49
- { label: 'User ID', key: 'userID' },
50
- { label: 'Request Time', key: 'createdTime' },
51
- { label: 'Progress Time', key: 'progressTime' },
52
- { label: 'Completed Time', key: 'completedTime' },
53
- { label: 'Time to Progress (in seconds)', key: 'progressDuration' },
54
- { label: 'Time to Complete (in seconds)', key: 'completedDuration' },
55
- ];
56
- }
57
-
58
- UNSAFE_componentWillMount() {
59
- Session.checkLoggedIn(this);
60
- }
61
-
62
- componentDidMount() {
63
- this.props.jobStatusesUpdate(this.props.auth.site);
64
- this.getJobs();
65
- this.getAssignees();
66
- }
67
-
68
- setRequesters = (jobs) => {
69
- const requesters = _.orderBy(
70
- _.uniqBy(
71
- jobs.map((j) => ({
72
- id: j.userID,
73
- displayName: j.userName,
74
- profilePic: j.userProfilePic,
75
- })),
76
- 'id',
77
- ),
78
- 'displayName',
79
- 'asc',
80
- );
81
- this.setState({ requesters });
82
- };
83
-
84
- getJobs = async () => {
85
- const { auth } = this.props;
86
- try {
87
- const res = await maintenanceActions.getJobsRecursive(auth.site);
88
- if (!_.isEmpty(res) && res[0].site === auth.site) {
89
- this.setRequesters(res);
90
- this.props.jobsLoaded(res);
91
- }
92
- } catch (error) {
93
- console.error('getJobs', error);
94
- }
95
- };
96
-
97
- getAssignees = async () => {
98
- try {
99
- const res = await maintenanceActions.getAssignees(this.props.auth.site);
100
- this.setState({ assignees: res.data.Users });
101
- } catch (error) {
102
- console.error('getAssignees', error);
103
- }
104
- };
105
-
106
- sortByCol = (col) => {
107
- const { sortColumn, sortDesc } = this.state;
108
- if (sortColumn === col) {
109
- this.setState({ sortDesc: !sortDesc });
110
- } else {
111
- this.setState({ sortColumn: col, sortDesc: false });
112
- }
113
- };
114
-
115
- onRemoveRequest = async (request) => {
116
- if (window.confirm(values.textAreYouSureYouWantToDelete)) {
117
- this.props.removeJob(request.id);
118
- try {
119
- await maintenanceActions.deleteJob(this.props.auth.site, request.id);
120
- this.getJobs();
121
- } catch (error) {
122
- console.log('onRemoveRequest', error);
123
- alert('Something went wrong with the request. Please try again.');
124
- }
125
- }
126
- };
127
-
128
- openFilter = (filter) => {
129
- this.setState({
130
- filterOpen: filter,
131
- });
132
- };
133
-
134
- closeFilter = () => {
135
- this.setState({
136
- filterOpen: null,
137
- });
138
- };
139
-
140
- selectTypeFilter = (filter) => {
141
- this.setState({
142
- selectedTypeFilter: filter,
143
- });
144
- this.closeFilter();
145
- };
146
-
147
- selectPriorityFilter = (filter) => {
148
- this.setState({
149
- selectedPriorityFilter: filter,
150
- });
151
- this.closeFilter();
152
- };
153
-
154
- selectStatusFilter = (filter) => {
155
- this.setState({
156
- selectedStatusFilter: filter,
157
- });
158
- this.closeFilter();
159
- };
160
-
161
- timeFilterChanged = (selectedTimeFilter) => {
162
- this.setState({ selectedTimeFilter });
163
- };
164
-
165
- timeFilterDateRangeChanged = (startDate, endDate) => {
166
- this.setState({ timeFilterStart: startDate, timeFilterEnd: endDate });
167
- };
168
-
169
- isValidTimeFilter = () => {
170
- if (!this.state.selectedTimeFilter) {
171
- return false;
172
- }
173
- if (this.state.selectedTimeFilter.dayCount > 0) {
174
- return true;
175
- }
176
- return moment(this.state.timeFilterStart).startOf('d').valueOf() < moment(this.state.timeFilterEnd).endOf('d').valueOf();
177
- };
178
-
179
- saveTimeFilter = () => {
180
- if (!this.isValidTimeFilter()) {
181
- return;
182
- }
183
- let startTime = 0;
184
- let endTime = moment().endOf('d').valueOf();
185
- let text = '';
186
- if (this.state.selectedTimeFilter.dayCount > 0) {
187
- startTime = moment().add(-this.state.selectedTimeFilter.dayCount, 'd').startOf('d').valueOf();
188
- text = this.state.selectedTimeFilter.text;
189
- } else {
190
- const startDate = moment(this.state.timeFilterStart).startOf('d');
191
- const endDate = moment(this.state.timeFilterEnd).endOf('d');
192
- startTime = startDate.valueOf();
193
- endTime = endDate.valueOf();
194
- text = `${startDate.format('DD/MM/YYYY')} to ${endDate.format('DD/MM/YYYY')}`;
195
- }
196
- this.setState({
197
- selectedTimeFilterStart: startTime,
198
- selectedTimeFilterEnd: endTime,
199
- selectedTimeFilterText: text,
200
- });
201
- this.closeFilter();
202
- };
203
-
204
- removeTimeFilter = () => {
205
- this.setState({
206
- selectedTimeFilterStart: null,
207
- selectedTimeFilterEnd: null,
208
- selectedTimeFilterText: null,
209
- });
210
- };
211
-
212
- onHandleChange = (event) => {
213
- var stateChange = {};
214
- stateChange[event.target.getAttribute('id')] = event.target.value;
215
- this.setState(stateChange);
216
- };
217
-
218
- onSelectAssignee = (user) => {
219
- this.setState({
220
- selectedAssignee: user,
221
- });
222
- };
223
-
224
- removeUserFilter = () => {
225
- this.setState({
226
- selectedUserFilter: null,
227
- selectedUserFilterText: null,
228
- });
229
- };
230
-
231
- saveUserFilter = () => {
232
- if (!this.state.selectedAssignee) {
233
- this.removeUserFilter();
234
- } else {
235
- this.setState({
236
- selectedUserFilter: this.state.selectedAssignee.id,
237
- selectedUserFilterText: this.state.selectedAssignee.displayName,
238
- selectedAssignee: null,
239
- });
240
- }
241
- this.closeFilter();
242
- };
243
-
244
- onSelectRequester = (user) => {
245
- this.setState({
246
- selectedRequester: user,
247
- });
248
- };
249
-
250
- removeRequesterFilter = () => {
251
- this.setState({
252
- selectedRequesterFilter: null,
253
- selectedRequesterFilterText: null,
254
- });
255
- };
256
-
257
- saveRequesterFilter = () => {
258
- if (!this.state.selectedRequester) {
259
- this.removeRequesterFilter();
260
- } else {
261
- this.setState({
262
- selectedRequesterFilter: this.state.selectedRequester.id,
263
- selectedRequesterFilterText: this.state.selectedRequester.displayName,
264
- selectedRequester: null,
265
- });
266
- }
267
- this.closeFilter();
268
- };
269
-
270
- onHandleSearchChange = (event) => {
271
- const thisSearchTime = moment().valueOf();
272
- this.setState({ search: event.target.value, lastSearch: thisSearchTime });
273
-
274
- setTimeout(() => {
275
- // delayed setter to avoid filtering on every keypress
276
- if (this.state.lastSearch === thisSearchTime) {
277
- this.setState({
278
- searchTerm: this.state.search,
279
- });
280
- }
281
- }, 500);
282
- };
283
-
284
- getSource = () => {
285
- let source = this.props.source;
286
-
287
- // filter by time
288
- if (this.state.selectedTimeFilterStart && this.state.selectedTimeFilterEnd) {
289
- source = _.filter(source, (r) => {
290
- return r.createdUnix >= this.state.selectedTimeFilterStart && r.createdUnix <= this.state.selectedTimeFilterEnd;
291
- });
292
- }
293
-
294
- // filter by type
295
- if (this.state.selectedTypeFilter) {
296
- source = _.filter(source, (r) => {
297
- return r.type === this.state.selectedTypeFilter;
298
- });
299
- }
300
-
301
- // filter by priority
302
- if (this.state.selectedPriorityFilter) {
303
- const defaultPriority = getDefaultPriority().name;
304
- source = _.filter(source, (r) => {
305
- return (
306
- r.priority === this.state.selectedPriorityFilter || (this.state.selectedPriorityFilter === defaultPriority && _.isNil(r.priority))
307
- );
308
- });
309
- }
310
-
311
- // filter by status
312
- if (this.state.selectedStatusFilter) {
313
- const { statusTypes } = this.props;
314
- const defaultStatus = statusTypes.find((s) => s.category === STATUS_NOT_ACTIONED);
315
- source = _.filter(source, (r) => {
316
- const status = statusTypes.find((s) => s.text === r.status) || defaultStatus;
317
- if (this.state.selectedStatusFilter === STATUS_IMCOMPLETE) {
318
- return status.category !== STATUS_COMPLETED;
319
- }
320
- return status.text === this.state.selectedStatusFilter;
321
- });
322
- }
323
-
324
- if (this.state.selectedUserFilter) {
325
- source = _.filter(source, (r) => {
326
- return r.AssigneeId === this.state.selectedUserFilter;
327
- });
328
- }
329
-
330
- if (this.state.selectedRequesterFilter) {
331
- source = _.filter(source, (r) => {
332
- return r.userID === this.state.selectedRequesterFilter;
333
- });
334
- }
335
-
336
- if (!_.isEmpty(this.state.searchTerm)) {
337
- source = _.filter(source, (r) => {
338
- if (r.jobId && r.jobId === this.state.searchTerm) {
339
- return true;
340
- }
341
- if (r.room && r.room.toLowerCase().indexOf(this.state.searchTerm.toLowerCase()) > -1) {
342
- return true;
343
- }
344
- if (r.title && r.title.toLowerCase().indexOf(this.state.searchTerm.toLowerCase()) > -1) {
345
- return true;
346
- }
347
- return false;
348
- });
349
- }
350
-
351
- source = _.sortBy(source, (event) => {
352
- if (this.state.sortColumn === 'assigned') {
353
- return event.Assignee ? event.Assignee.displayName : 'Unassigned';
354
- }
355
- if (this.state.sortColumn !== 'createdUnix') return event[this.state.sortColumn];
356
- return event.createdUnix;
357
- });
358
- if (this.state.sortDesc) source.reverse();
359
- return source;
360
- };
361
-
362
- getCustomFieldValue = (field) => {
363
- switch (field.type) {
364
- case 'date':
365
- return field.answer ? moment(field.answer, 'YYYY-MM-DD').format('DD-MMM-YYYY') : '';
366
- case 'time':
367
- return field.answer ? moment(field.answer, 'HH:mm').format('h:mm a') : '';
368
- case 'yn':
369
- return field.answer ? 'Yes' : 'No';
370
- case 'checkbox':
371
- return field.answer && Array.isArray(field.answer) ? field.answer.join(', ') : '';
372
- default:
373
- return field.answer;
374
- }
375
- };
376
-
377
- getCustomFields = (job, customColumns) => {
378
- const { customFields, type } = job;
379
- const customValues = {};
380
- if (customFields && Array.isArray(customFields)) {
381
- customFields.forEach((field) => {
382
- // Exclude un-exportable fields
383
- if (['image', 'document', 'staticTitle', 'staticText'].includes(field.type)) return;
384
-
385
- const fieldKey = `${_.camelCase(type)}.${_.camelCase(field.label)}`;
386
- // Build custom columns
387
- const exists = customColumns.find((c) => c.key === fieldKey);
388
- if (!exists) {
389
- customColumns.push({ label: field.label, key: fieldKey });
390
- }
391
- // Build custom field source
392
- customValues[fieldKey] = this.getCustomFieldValue(field);
393
- });
394
- }
395
- return customValues;
396
- };
397
-
398
- getExportSource = () => {
399
- const customColumns = [];
400
- const defaultPriority = getDefaultPriority().name;
401
- const source = this.getSource().map((r) => {
402
- const history = r.history || [];
403
- const progressEntry = _.find(history, (e) => {
404
- return e.status !== 'Unassigned';
405
- });
406
- const completedEntry = _.find(history, (e) => {
407
- return e.status === 'Completed';
408
- });
409
- let progressTime = null;
410
- let completedTime = null;
411
- let progressDuration = null;
412
- let completedDuration = null;
413
- if (progressEntry) {
414
- progressTime = moment.utc(progressEntry.timestamp).format();
415
- progressDuration = moment.utc(progressEntry.timestamp).unix() - moment.utc(r.createdTime).unix();
416
- }
417
- if (completedEntry) {
418
- completedTime = moment.utc(completedEntry.timestamp).format();
419
- completedDuration = moment.utc(completedEntry.timestamp).unix() - moment.utc(r.createdTime).unix();
420
- }
421
- let notes = '';
422
- (r.Notes || []).forEach((note, index) => {
423
- if (index > 0) {
424
- notes += '\n\n';
425
- }
426
- if (note.User && !_.isEmpty(note.User.displayName)) {
427
- notes += `${note.User.displayName}:\n`;
428
- }
429
- if (note.Timestamp) {
430
- notes += `${moment(note.Timestamp).format('DD-MM-YYYY h:mma')}\n`;
431
- }
432
- notes += note.Note;
433
- });
434
- const customFieldValues = this.getCustomFields(r, customColumns);
435
- const priority = r.priority || defaultPriority;
436
- return { ...r, ...customFieldValues, notes, progressTime, completedTime, progressDuration, completedDuration, priority };
437
- });
438
-
439
- // Compose revised columns list with custom fields
440
- const index = this.exportColumns.findIndex((c) => c.key === 'notes');
441
- const columns = [...this.exportColumns.slice(0, index), ...customColumns, ...this.exportColumns.slice(index)];
442
- return { columns, source };
443
- };
444
-
445
- isExportReady = () => {
446
- return !_.isEmpty(this.getSource());
447
- };
448
-
449
- onOpenExportCsv = () => {
450
- if (!this.isExportReady()) return;
451
- this.setState({ exportCsvOpen: true });
452
- };
453
-
454
- onCloseExportCsv = () => {
455
- this.setState({ exportCsvOpen: false });
456
- };
457
-
458
- renderFilterPopup() {
459
- if (!this.state.filterOpen) {
460
- return null;
461
- }
462
- if (this.state.filterOpen === 'type') {
463
- return (
464
- <Components.Popup title={`Select ${values.textJobType}`} maxWidth={600} minWidth={400} hasPadding onClose={this.closeFilter}>
465
- {_.sortBy(_.uniq(this.props.source.map((r) => r.type)), (t) => t.toLowerCase()).map((type) => {
466
- return (
467
- <Components.Tag
468
- key={type}
469
- onClick={() => {
470
- this.selectTypeFilter(type);
471
- }}
472
- text={type}
473
- className="marginRight-10"
474
- />
475
- );
476
- })}
477
- </Components.Popup>
478
- );
479
- }
480
- if (this.state.filterOpen === 'priority') {
481
- return (
482
- <Components.Popup title="Select Priority" maxWidth={600} minWidth={400} hasPadding onClose={this.closeFilter}>
483
- {jobPriorityOptions.map((p) => {
484
- return (
485
- <Components.Tag
486
- key={p.name}
487
- onClick={() => {
488
- this.selectPriorityFilter(p.name);
489
- }}
490
- text={p.name}
491
- className="marginRight-10"
492
- />
493
- );
494
- })}
495
- </Components.Popup>
496
- );
497
- }
498
- if (this.state.filterOpen === 'status') {
499
- const { statusTypes } = this.props;
500
- return (
501
- <Components.Popup title="Select Status" maxWidth={600} minWidth={400} hasPadding onClose={this.closeFilter}>
502
- {[STATUS_IMCOMPLETE, ...statusTypes.map((s) => s.text)].map((status) => {
503
- return (
504
- <Components.Tag
505
- key={status}
506
- onClick={() => {
507
- this.selectStatusFilter(status);
508
- }}
509
- text={status}
510
- className="marginRight-10 marginBottom-10"
511
- />
512
- );
513
- })}
514
- </Components.Popup>
515
- );
516
- }
517
- if (this.state.filterOpen === 'time') {
518
- return (
519
- <Components.Popup
520
- title="Select Time"
521
- maxWidth={600}
522
- minWidth={400}
523
- hasPadding
524
- onClose={this.closeFilter}
525
- buttons={[
526
- {
527
- type: 'primaryAction',
528
- onClick: this.saveTimeFilter,
529
- text: 'Select',
530
- isActive: this.isValidTimeFilter(),
531
- },
532
- ]}
533
- >
534
- <div style={{ minHeight: 150 }}>
535
- <Components.AnalyticsFilter
536
- defaultFilter={this.state.selectedTimeFilter}
537
- filterChanged={this.timeFilterChanged}
538
- filterDateRangeChanged={this.timeFilterDateRangeChanged}
539
- />
540
- </div>
541
- </Components.Popup>
542
- );
543
- }
544
- if (this.state.filterOpen === 'user') {
545
- let userContent = null;
546
- if (this.state.selectedAssignee) {
547
- userContent = (
548
- <div>
549
- <Components.UserListing
550
- key={this.state.selectedAssignee.id}
551
- user={this.state.selectedAssignee}
552
- rightContent={
553
- <Components.SVGIcon
554
- className="removeIcon"
555
- icon="close"
556
- onClick={() => {
557
- this.onSelectAssignee();
558
- }}
559
- colour={Colours.COLOUR_DUSK}
560
- />
561
- }
562
- />
563
- </div>
564
- );
565
- } else {
566
- userContent = (
567
- <div>
568
- <Components.GenericInput
569
- id="userSearch"
570
- type="text"
571
- // label="Search"
572
- placeholder="Search name"
573
- value={this.state.userSearch}
574
- onChange={(e) => this.onHandleChange(e)}
575
- alwaysShowLabel
576
- />
577
- {_.sortBy(this.state.assignees, (u) => u.displayName.toUpperCase())
578
- .filter((u) => {
579
- if (_.isEmpty(this.state.userSearch)) return true;
580
- return u.displayName.toUpperCase().indexOf(this.state.userSearch.toUpperCase()) > -1;
581
- })
582
- .map((user) => {
583
- return (
584
- <Components.UserListing
585
- key={user.id}
586
- user={user}
587
- onClick={() => {
588
- this.onSelectAssignee(user);
589
- }}
590
- />
591
- );
592
- })}
593
- </div>
594
- );
595
- }
596
- return (
597
- <Components.Popup
598
- title="Select User"
599
- maxWidth={600}
600
- minWidth={400}
601
- hasPadding
602
- onClose={this.closeFilter}
603
- buttons={[
604
- {
605
- type: 'primaryAction',
606
- onClick: this.saveUserFilter,
607
- text: 'Select',
608
- isActive: true,
609
- },
610
- ]}
611
- >
612
- {userContent}
613
- </Components.Popup>
614
- );
615
- }
616
- if (this.state.filterOpen === 'requester') {
617
- let userContent = null;
618
- if (this.state.selectedRequester) {
619
- userContent = (
620
- <div>
621
- <Components.UserListing
622
- key={this.state.selectedRequester.id}
623
- user={this.state.selectedRequester}
624
- rightContent={
625
- <Components.SVGIcon
626
- className="removeIcon"
627
- icon="close"
628
- onClick={() => {
629
- this.onSelectRequester();
630
- }}
631
- colour={Colours.COLOUR_DUSK}
632
- />
633
- }
634
- />
635
- </div>
636
- );
637
- } else {
638
- userContent = (
639
- <div>
640
- <Components.GenericInput
641
- id="requesterSearch"
642
- type="text"
643
- // label="Search"
644
- placeholder="Search name"
645
- value={this.state.requesterSearch}
646
- onChange={(e) => this.onHandleChange(e)}
647
- alwaysShowLabel
648
- />
649
- {_.sortBy(this.state.requesters, (u) => u.displayName.toUpperCase())
650
- .filter((u) => {
651
- if (_.isEmpty(this.state.requesterSearch)) return true;
652
- return u.displayName.toUpperCase().indexOf(this.state.requesterSearch.toUpperCase()) > -1;
653
- })
654
- .map((user) => {
655
- return (
656
- <Components.UserListing
657
- key={user.id}
658
- user={user}
659
- onClick={() => {
660
- this.onSelectRequester(user);
661
- }}
662
- />
663
- );
664
- })}
665
- </div>
666
- );
667
- }
668
- return (
669
- <Components.Popup
670
- title="Select User"
671
- maxWidth={600}
672
- minWidth={400}
673
- hasPadding
674
- onClose={this.closeFilter}
675
- buttons={[
676
- {
677
- type: 'primaryAction',
678
- onClick: this.saveRequesterFilter,
679
- text: 'Select',
680
- isActive: true,
681
- },
682
- ]}
683
- >
684
- {userContent}
685
- </Components.Popup>
686
- );
687
- }
688
- return null;
689
- }
690
-
691
- renderRequests() {
692
- const { statusTypes } = this.props;
693
- const defaultStatus = statusTypes.find((s) => s.category === STATUS_NOT_ACTIONED);
694
-
695
- return this.getSource().map((ev, index) => {
696
- if (!ev) {
697
- return null;
698
- }
699
- const status = (ev.status && statusTypes.find((s) => s.text === ev.status)) || defaultStatus;
700
- const priority = getJobPriority(ev.priority);
701
-
702
- return (
703
- <tr key={index}>
704
- <td>{ev.jobId}</td>
705
- <td className="table-TitleColumn">
706
- <Link to={`${values.routeRequestDetails}/${ev.id}`}>
707
- <span>{ev.title}</span>
708
- </Link>
709
- </td>
710
- <td>{ev.type}</td>
711
- <td>{moment.utc(ev.createdTime).local().format('D MMM YY')}</td>
712
- <td>{ev.room}</td>
713
- <td>
714
- {ev.userName ? (
715
- <Components.UserListing
716
- user={{ id: ev.userID, displayName: ev.userName, profilePic: ev.userProfilePic }}
717
- textClass="fontSize-13"
718
- />
719
- ) : (
720
- 'Unknown'
721
- )}
722
- </td>
723
- <td>{ev.Assignee ? <Components.UserListing user={ev.Assignee} textClass="fontSize-13" /> : 'Unassigned'}</td>
724
- {/* <td>
725
- <div style={{ textAlign: 'center' }}>{ev.commentCount}</div>
726
- </td> */}
727
- <td>
728
- <div
729
- style={{
730
- textAlign: 'center',
731
- borderRadius: 4,
732
- width: 100,
733
- paddingTop: 2,
734
- paddingBottom: 2,
735
- color: '#fff',
736
- backgroundColor: status ? status.color : undefined,
737
- }}
738
- >
739
- {status ? status.text : ''}
740
- </div>
741
- </td>
742
- <td>
743
- <div
744
- style={{
745
- textAlign: 'center',
746
- borderRadius: 4,
747
- width: 100,
748
- paddingTop: 2,
749
- paddingBottom: 2,
750
- color: '#fff',
751
- backgroundColor: priority.color,
752
- }}
753
- >
754
- {priority.name}
755
- </div>
756
- </td>
757
- <td className="table-options">
758
- <div style={{ display: 'flex', alignItems: 'center' }}>
759
- <Link to={`${values.routeRequestDetails}/${ev.id}`}>
760
- <FontAwesome style={{ fontSize: 20, padding: 5, cursor: 'pointer' }} name="pencil" />
761
- </Link>
762
- {Session.validateAccess(this.props.auth.site, values.permissionMaintenanceTracking, this.props.auth) && (
763
- <a onClick={() => this.onRemoveRequest(ev)}>
764
- <FontAwesome style={{ fontSize: 20, padding: 5, marginLeft: 8, cursor: 'pointer' }} name="minus-circle" />
765
- </a>
766
- )}
767
- </div>
768
- </td>
769
- </tr>
770
- );
771
- });
772
- }
773
-
774
- renderSort(col) {
775
- const { sortColumn, sortDesc } = this.state;
776
- if (col !== sortColumn) return null;
777
- return <FontAwesome style={{ marginLeft: 5 }} name={sortDesc ? 'chevron-up' : 'chevron-down'} />;
778
- }
779
-
780
- sortIsActive(col) {
781
- if (col !== this.state.sortColumn) return '';
782
- return ' table--columnActive';
783
- }
784
-
785
- renderEmpty() {
786
- const title = this.props.strings[`${values.featureKey}_textTitleRequests`] || values.textTitleRequests;
787
-
788
- return (
789
- <div style={{ display: 'flex', flexDirection: 'column', flex: 1, justifyContent: 'center', alignItems: 'center', marginTop: 32 }}>
790
- <div className="emptyState" />
791
- <div className="marginTop-32" style={{ maxWidth: 500, textAlign: 'center' }}>
792
- <span className="fontRegular fontSize-13">
793
- <span className="fontHeavy text-brandingColour">{title} </span>
794
- {values.textEmptyDescription}
795
- </span>
796
- </div>
797
- <div className="marginTop-8 fontRegular fontSize-13" style={{ maxWidth: 500, textAlign: 'center' }}>
798
- {values.textEmptyExample}
799
- </div>
800
- </div>
801
- );
802
- }
803
-
804
- renderContent() {
805
- if (_.isEmpty(this.props.source)) return this.renderEmpty();
806
-
807
- return (
808
- <Table className="plussTable" striped bordered condensed hover style={{ minWidth: '100%' }}>
809
- <thead>
810
- <tr>
811
- <th
812
- className={`${this.sortIsActive('jobId')}`}
813
- style={{ cursor: 'pointer', width: 70 }}
814
- onClick={() => {
815
- this.sortByCol('jobId');
816
- }}
817
- >
818
- {values.textEntityName} No.{this.renderSort('jobId')}
819
- </th>
820
- <th
821
- className={`${this.sortIsActive('title')}`}
822
- style={{ cursor: 'pointer' }}
823
- onClick={() => {
824
- this.sortByCol('title');
825
- }}
826
- >
827
- Title{this.renderSort('title')}
828
- </th>
829
- <th
830
- className={`${this.sortIsActive('type')}`}
831
- style={{ cursor: 'pointer', width: 100 }}
832
- onClick={() => {
833
- this.sortByCol('type');
834
- }}
835
- >
836
- {values.textJobType}
837
- {this.renderSort('type')}
838
- </th>
839
- <th
840
- className={`${this.sortIsActive('createdUnix')}`}
841
- style={{ cursor: 'pointer', width: 80 }}
842
- onClick={() => {
843
- this.sortByCol('createdUnix');
844
- }}
845
- >
846
- Date{this.renderSort('createdUnix')}
847
- </th>
848
- <th
849
- className={`${this.sortIsActive('room')}`}
850
- style={{ cursor: 'pointer', width: 100 }}
851
- onClick={() => {
852
- this.sortByCol('room');
853
- }}
854
- >
855
- Address{this.renderSort('room')}
856
- </th>
857
- <th
858
- className={`${this.sortIsActive('userName')}`}
859
- style={{ cursor: 'pointer', width: 150 }}
860
- onClick={() => {
861
- this.sortByCol('userName');
862
- }}
863
- >
864
- Submitted By{this.renderSort('userName')}
865
- </th>
866
- <th
867
- className={`${this.sortIsActive('assigned')}`}
868
- style={{ cursor: 'pointer', width: 150 }}
869
- onClick={() => {
870
- this.sortByCol('assigned');
871
- }}
872
- >
873
- Assigned To{this.renderSort('assigned')}
874
- </th>
875
- {/* <th style={{ width: 30 }}>Comments</th> */}
876
- <th style={{ width: 120 }}>Status</th>
877
- <th style={{ width: 120 }}>Priority</th>
878
- <th style={{ width: 50 }} />
879
- </tr>
880
- </thead>
881
- <tbody>{this.renderRequests()}</tbody>
882
- </Table>
883
- );
884
- }
885
-
886
- renderFilters() {
887
- let typeFilter = (
888
- <Components.Tag
889
- className="marginRight-10"
890
- onClick={() => {
891
- this.openFilter('type');
892
- }}
893
- text={values.textJobType}
894
- />
895
- );
896
- let statusFilter = (
897
- <Components.Tag
898
- className="marginRight-10"
899
- onClick={() => {
900
- this.openFilter('status');
901
- }}
902
- text="Status"
903
- />
904
- );
905
- let priorityFilter = (
906
- <Components.Tag
907
- className="marginRight-10"
908
- onClick={() => {
909
- this.openFilter('priority');
910
- }}
911
- text="Priority"
912
- />
913
- );
914
- let timeFilter = (
915
- <Components.Tag
916
- className="marginRight-10"
917
- onClick={() => {
918
- this.openFilter('time');
919
- }}
920
- text="Time"
921
- />
922
- );
923
- let userFilter = (
924
- <Components.Tag
925
- className="marginRight-10"
926
- onClick={() => {
927
- this.openFilter('user');
928
- }}
929
- text="Assigned To"
930
- />
931
- );
932
- let requesterFilter = (
933
- <Components.Tag
934
- className="marginRight-10"
935
- onClick={() => {
936
- this.openFilter('requester');
937
- }}
938
- text="Submitted By"
939
- />
940
- );
941
-
942
- if (this.state.selectedTypeFilter) {
943
- typeFilter = (
944
- <Components.Tag
945
- className="marginRight-10"
946
- onClick={() => {
947
- this.openFilter('type');
948
- }}
949
- rightIcon="close"
950
- rightClick={(e) => {
951
- e.stopPropagation();
952
- this.selectTypeFilter();
953
- }}
954
- text={this.state.selectedTypeFilter}
955
- />
956
- );
957
- }
958
-
959
- if (this.state.selectedPriorityFilter) {
960
- priorityFilter = (
961
- <Components.Tag
962
- className="marginRight-10"
963
- onClick={() => {
964
- this.openFilter('priority');
965
- }}
966
- rightIcon="close"
967
- rightClick={(e) => {
968
- e.stopPropagation();
969
- this.selectPriorityFilter();
970
- }}
971
- text={this.state.selectedPriorityFilter}
972
- />
973
- );
974
- }
975
-
976
- if (this.state.selectedStatusFilter) {
977
- statusFilter = (
978
- <Components.Tag
979
- className="marginRight-10"
980
- onClick={() => {
981
- this.openFilter('status');
982
- }}
983
- rightIcon="close"
984
- rightClick={(e) => {
985
- e.stopPropagation();
986
- this.selectStatusFilter();
987
- }}
988
- text={this.state.selectedStatusFilter}
989
- />
990
- );
991
- }
992
- if (this.state.selectedTimeFilterText) {
993
- timeFilter = (
994
- <Components.Tag
995
- className="marginRight-10"
996
- onClick={() => {
997
- this.openFilter('time');
998
- }}
999
- rightIcon="close"
1000
- rightClick={(e) => {
1001
- e.stopPropagation();
1002
- this.removeTimeFilter();
1003
- }}
1004
- text={this.state.selectedTimeFilterText}
1005
- />
1006
- );
1007
- }
1008
- if (this.state.selectedUserFilter) {
1009
- userFilter = (
1010
- <Components.Tag
1011
- className="marginRight-10"
1012
- onClick={() => {
1013
- this.openFilter('user');
1014
- }}
1015
- rightIcon="close"
1016
- rightClick={(e) => {
1017
- e.stopPropagation();
1018
- this.removeUserFilter();
1019
- }}
1020
- text={this.state.selectedUserFilterText}
1021
- />
1022
- );
1023
- }
1024
- if (this.state.selectedRequesterFilter) {
1025
- requesterFilter = (
1026
- <Components.Tag
1027
- className="marginRight-10"
1028
- onClick={() => {
1029
- this.openFilter('requester');
1030
- }}
1031
- rightIcon="close"
1032
- rightClick={(e) => {
1033
- e.stopPropagation();
1034
- this.removeRequesterFilter();
1035
- }}
1036
- text={this.state.selectedRequesterFilterText}
1037
- />
1038
- );
1039
- }
1040
- return (
1041
- <div>
1042
- <div className="marginTop-20 flex flex-between flex-center">
1043
- <div className="flex flex-center">
1044
- <Components.Text type="h5" className="marginRight-20">
1045
- Filter by
1046
- </Components.Text>
1047
- {typeFilter}
1048
- {statusFilter}
1049
- {priorityFilter}
1050
- {timeFilter}
1051
- {userFilter}
1052
- {requesterFilter}
1053
- </div>
1054
- <Components.Button
1055
- inline
1056
- buttonType="primaryAction"
1057
- leftIcon="file-code-o"
1058
- onClick={this.onOpenExportCsv}
1059
- isActive={!_.isEmpty(this.getSource())}
1060
- >
1061
- Export CSV
1062
- </Components.Button>
1063
- </div>
1064
- <Components.GenericInput
1065
- id="search"
1066
- type="text"
1067
- placeholder={`Search by ${values.textEntityName} ID, Address or Title`}
1068
- value={this.state.search}
1069
- onChange={(e) => this.onHandleSearchChange(e)}
1070
- className="genericInputContainer-rounded marginTop-10"
1071
- style={{ maxWidth: 500 }}
1072
- />
1073
- </div>
1074
- );
1075
- }
1076
-
1077
- renderCSVPopup() {
1078
- if (!this.state.exportCsvOpen) {
1079
- return null;
1080
- }
1081
-
1082
- const { columns, source } = this.getExportSource();
1083
- return <Components.ExportCsvPopup onClose={this.onCloseExportCsv} columns={columns} source={source} filename="requests.csv" />;
1084
- }
1085
-
1086
- render() {
1087
- return (
1088
- <div style={{ minWidth: '100%' }}>
1089
- {this.renderFilterPopup()}
1090
- {this.renderCSVPopup()}
1091
- {this.renderFilters()}
1092
- {this.renderContent()}
1093
- </div>
1094
- );
1095
- }
1096
- }
1097
-
1098
- const mapStateToProps = (state) => {
1099
- const { auth } = state;
1100
- return {
1101
- jobs: state[values.reducerKey].jobs,
1102
- auth,
1103
- strings: (state.strings && state.strings.config) || {},
1104
- statusTypes: state[values.reducerKey].jobstatuses,
1105
- };
1106
- };
1107
-
1108
- export default connect(mapStateToProps, { jobsLoaded, removeJob, jobStatusesUpdate })(withRouter(JobList));