@plusscommunities/pluss-maintenance-web-feedback 1.1.36 → 1.1.37-beta.1

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.
package/dist/index.cjs.js CHANGED
@@ -6,12 +6,12 @@ var _defineProperty = require('@babel/runtime/helpers/defineProperty');
6
6
  var React = require('react');
7
7
  var reactRedux = require('react-redux');
8
8
  var reactRouter = require('react-router');
9
- var _ = require('lodash');
10
- var moment = require('moment');
11
- var FontAwesome = require('react-fontawesome');
12
9
  var PlussCore = require('@plusscommunities/pluss-core-web');
13
10
  var reactBootstrap = require('react-bootstrap');
14
11
  var reactRouterDom = require('react-router-dom');
12
+ var FontAwesome = require('react-fontawesome');
13
+ var moment = require('moment');
14
+ var _ = require('lodash');
15
15
  var Textarea = require('react-textarea-autosize');
16
16
  var freeSolidSvgIcons = require('@fortawesome/free-solid-svg-icons');
17
17
 
@@ -37,10 +37,10 @@ function _interopNamespace(e) {
37
37
 
38
38
  var _defineProperty__default = /*#__PURE__*/_interopDefaultLegacy(_defineProperty);
39
39
  var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
40
- var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
41
- var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
42
- var FontAwesome__default = /*#__PURE__*/_interopDefaultLegacy(FontAwesome);
43
40
  var PlussCore__namespace = /*#__PURE__*/_interopNamespace(PlussCore);
41
+ var FontAwesome__default = /*#__PURE__*/_interopDefaultLegacy(FontAwesome);
42
+ var moment__default = /*#__PURE__*/_interopDefaultLegacy(moment);
43
+ var ___default = /*#__PURE__*/_interopDefaultLegacy(_);
44
44
  var Textarea__default = /*#__PURE__*/_interopDefaultLegacy(Textarea);
45
45
 
46
46
  const values = {
@@ -249,12 +249,12 @@ const JOBS_HIDE_SEEN = values.actionJobsHideSeen;
249
249
  function ownKeys$7(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
250
250
  function _objectSpread$7(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$7(Object(t), !0).forEach(function (r) { _defineProperty__default["default"](e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$7(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
251
251
  const {
252
- Helper: Helper$5,
252
+ Helper: Helper$4,
253
253
  Session: Session$9
254
254
  } = PlussCore__namespace;
255
- const maintenanceActions = {
255
+ const maintenanceActions$1 = {
256
256
  getJobType: (site, typeId) => {
257
- let url = Helper$5.getUrl(values.serviceKey, 'getjobtype');
257
+ let url = Helper$4.getUrl(values.serviceKey, 'getjobtype');
258
258
  return Session$9.authedFunction({
259
259
  method: 'POST',
260
260
  url,
@@ -265,7 +265,7 @@ const maintenanceActions = {
265
265
  });
266
266
  },
267
267
  getJobTypes: (site, id) => {
268
- let url = Helper$5.getUrl(values.serviceKey, 'getjobtypes');
268
+ let url = Helper$4.getUrl(values.serviceKey, 'getjobtypes');
269
269
  return Session$9.authedFunction({
270
270
  method: 'POST',
271
271
  url,
@@ -275,7 +275,7 @@ const maintenanceActions = {
275
275
  });
276
276
  },
277
277
  getJob: (site, id) => {
278
- let url = Helper$5.getUrl(values.serviceKey, 'getJob');
278
+ let url = Helper$4.getUrl(values.serviceKey, 'getJob');
279
279
  return Session$9.authedFunction({
280
280
  method: 'POST',
281
281
  url,
@@ -286,7 +286,7 @@ const maintenanceActions = {
286
286
  });
287
287
  },
288
288
  getJobByJobId: (site, jobId) => {
289
- let url = Helper$5.getUrl(values.serviceKey, 'getJob');
289
+ let url = Helper$4.getUrl(values.serviceKey, 'getJob');
290
290
  return Session$9.authedFunction({
291
291
  method: 'POST',
292
292
  url,
@@ -301,7 +301,7 @@ const maintenanceActions = {
301
301
  let type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
302
302
  return Session$9.authedFunction({
303
303
  method: 'POST',
304
- url: Helper$5.getUrl(values.serviceKey, 'getJobs'),
304
+ url: Helper$4.getUrl(values.serviceKey, 'getJobs'),
305
305
  data: {
306
306
  site,
307
307
  status,
@@ -309,7 +309,7 @@ const maintenanceActions = {
309
309
  }
310
310
  });
311
311
  },
312
- getJobs2: (site, status, type, lastKey) => {
312
+ getJobs2: (site, status, type, priority, assignee, startTime, endTime, search, lastKey) => {
313
313
  const query = {
314
314
  site
315
315
  };
@@ -319,37 +319,57 @@ const maintenanceActions = {
319
319
  if (type) {
320
320
  query.type = type;
321
321
  }
322
+ if (priority) {
323
+ query.priority = priority;
324
+ }
325
+ if (assignee) {
326
+ query.assignee = assignee;
327
+ }
328
+ if (startTime) {
329
+ query.startTime = startTime;
330
+ }
331
+ if (endTime) {
332
+ query.endTime = endTime;
333
+ }
334
+ if (search) {
335
+ query.search = search;
336
+ }
322
337
  if (lastKey) {
323
338
  query.lastKey = JSON.stringify(lastKey);
324
339
  }
325
340
  return Session$9.authedFunction({
326
341
  method: 'GET',
327
- url: Helper$5.getUrl(values.serviceKey, 'get/requests', query)
342
+ url: Helper$4.getUrl(values.serviceKey, 'get/requests', query)
328
343
  });
329
344
  },
345
+ /**
346
+ * @deprecated Use getJobs2 with pagination instead.
347
+ * This method recursively fetches ALL pages which can be slow for large datasets.
348
+ * Only use for CSV export where all data is needed.
349
+ */
330
350
  getJobsRecursive: function (site, status, type, lastKey) {
331
351
  let jobs = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : [];
332
- return new Promise(resolve => {
333
- maintenanceActions.getJobs2(site, status, type, lastKey).then(jobRes => {
352
+ return new Promise((resolve, reject) => {
353
+ maintenanceActions$1.getJobs2(site, status, type, undefined, undefined, undefined, undefined, undefined, lastKey).then(jobRes => {
334
354
  const newJobs = [...jobs, ...jobRes.data.Items];
335
355
  if (!jobRes.data.LastKey) {
336
356
  return resolve(newJobs);
337
357
  }
338
- return resolve(maintenanceActions.getJobsRecursive(site, status, type, jobRes.data.LastKey, newJobs));
339
- });
358
+ maintenanceActions$1.getJobsRecursive(site, status, type, jobRes.data.LastKey, newJobs).then(resolve).catch(reject);
359
+ }).catch(reject);
340
360
  });
341
361
  },
342
362
  createJob: job => {
343
363
  return Session$9.authedFunction({
344
364
  method: 'POST',
345
- url: Helper$5.getUrl(values.serviceKey, 'sendMaintenance'),
365
+ url: Helper$4.getUrl(values.serviceKey, 'sendMaintenance'),
346
366
  data: _objectSpread$7({}, job)
347
367
  });
348
368
  },
349
369
  editJob: (job, site) => {
350
370
  return Session$9.authedFunction({
351
371
  method: 'POST',
352
- url: Helper$5.getUrl(values.serviceKey, 'editJob'),
372
+ url: Helper$4.getUrl(values.serviceKey, 'editJob'),
353
373
  data: {
354
374
  job,
355
375
  site
@@ -359,7 +379,7 @@ const maintenanceActions = {
359
379
  deleteJob: (site, id) => {
360
380
  return Session$9.authedFunction({
361
381
  method: 'POST',
362
- url: Helper$5.getUrl(values.serviceKey, 'requests/remove'),
382
+ url: Helper$4.getUrl(values.serviceKey, 'requests/remove'),
363
383
  data: {
364
384
  site,
365
385
  id
@@ -369,7 +389,7 @@ const maintenanceActions = {
369
389
  editJobStatus: (id, status) => {
370
390
  return Session$9.authedFunction({
371
391
  method: 'POST',
372
- url: Helper$5.getUrl(values.serviceKey, 'editJobStatus'),
392
+ url: Helper$4.getUrl(values.serviceKey, 'editJobStatus'),
373
393
  data: {
374
394
  id,
375
395
  status
@@ -379,7 +399,7 @@ const maintenanceActions = {
379
399
  editJobPriority: (id, priority) => {
380
400
  return Session$9.authedFunction({
381
401
  method: 'POST',
382
- url: Helper$5.getUrl(values.serviceKey, 'update/priority'),
402
+ url: Helper$4.getUrl(values.serviceKey, 'update/priority'),
383
403
  data: {
384
404
  id,
385
405
  priority
@@ -389,7 +409,7 @@ const maintenanceActions = {
389
409
  assignJob: (jobId, userId) => {
390
410
  return Session$9.authedFunction({
391
411
  method: 'POST',
392
- url: Helper$5.getUrl(values.serviceKey, 'update/assign'),
412
+ url: Helper$4.getUrl(values.serviceKey, 'update/assign'),
393
413
  data: {
394
414
  id: jobId,
395
415
  userId
@@ -399,7 +419,7 @@ const maintenanceActions = {
399
419
  getAssignees: site => {
400
420
  return Session$9.authedFunction({
401
421
  method: 'GET',
402
- url: Helper$5.getUrl(values.serviceKey, 'get/assignees', {
422
+ url: Helper$4.getUrl(values.serviceKey, 'get/assignees', {
403
423
  site
404
424
  })
405
425
  });
@@ -407,7 +427,7 @@ const maintenanceActions = {
407
427
  addNote: (jobId, note, attachments, images) => {
408
428
  return Session$9.authedFunction({
409
429
  method: 'POST',
410
- url: Helper$5.getUrl(values.serviceKey, 'requests/note'),
430
+ url: Helper$4.getUrl(values.serviceKey, 'requests/note'),
411
431
  data: {
412
432
  id: jobId,
413
433
  note,
@@ -420,7 +440,7 @@ const maintenanceActions = {
420
440
  editNote: (jobId, noteId, note, attachments, images) => {
421
441
  return Session$9.authedFunction({
422
442
  method: 'POST',
423
- url: Helper$5.getUrl(values.serviceKey, 'requests/note'),
443
+ url: Helper$4.getUrl(values.serviceKey, 'requests/note'),
424
444
  data: {
425
445
  id: jobId,
426
446
  note,
@@ -434,7 +454,7 @@ const maintenanceActions = {
434
454
  deleteNote: (jobId, noteId) => {
435
455
  return Session$9.authedFunction({
436
456
  method: 'POST',
437
- url: Helper$5.getUrl(values.serviceKey, 'requests/note'),
457
+ url: Helper$4.getUrl(values.serviceKey, 'requests/note'),
438
458
  data: {
439
459
  id: jobId,
440
460
  noteId,
@@ -454,7 +474,7 @@ const maintenanceActions = {
454
474
  };
455
475
  return Session$9.authedFunction({
456
476
  method: 'POST',
457
- url: Helper$5.getUrl(values.serviceKey, 'createJobType'),
477
+ url: Helper$4.getUrl(values.serviceKey, 'createJobType'),
458
478
  data
459
479
  });
460
480
  },
@@ -472,14 +492,14 @@ const maintenanceActions = {
472
492
  if (hasCustomFields && customFields) data.customFields = customFields;
473
493
  return Session$9.authedFunction({
474
494
  method: 'POST',
475
- url: Helper$5.getUrl(values.serviceKey, 'editJobType'),
495
+ url: Helper$4.getUrl(values.serviceKey, 'editJobType'),
476
496
  data
477
497
  });
478
498
  },
479
499
  deleteJobType: (site, id) => {
480
500
  return Session$9.authedFunction({
481
501
  method: 'POST',
482
- url: Helper$5.getUrl(values.serviceKey, 'deleteJobType'),
502
+ url: Helper$4.getUrl(values.serviceKey, 'deleteJobType'),
483
503
  data: {
484
504
  site,
485
505
  id
@@ -489,7 +509,7 @@ const maintenanceActions = {
489
509
  getExternalSync: jobId => {
490
510
  return Session$9.authedFunction({
491
511
  method: 'GET',
492
- url: Helper$5.getUrl(values.serviceKey, 'get/externalsync', {
512
+ url: Helper$4.getUrl(values.serviceKey, 'get/externalsync', {
493
513
  id: jobId
494
514
  })
495
515
  });
@@ -497,7 +517,7 @@ const maintenanceActions = {
497
517
  retrySync: jobId => {
498
518
  return Session$9.authedFunction({
499
519
  method: 'POST',
500
- url: Helper$5.getUrl(values.serviceKey, 'update/retrysync'),
520
+ url: Helper$4.getUrl(values.serviceKey, 'update/retrysync'),
501
521
  data: {
502
522
  id: jobId
503
523
  }
@@ -506,7 +526,7 @@ const maintenanceActions = {
506
526
  };
507
527
 
508
528
  const {
509
- Helper: Helper$4,
529
+ Helper: Helper$3,
510
530
  Session: Session$8
511
531
  } = PlussCore__namespace;
512
532
  const reactionActions = {
@@ -524,7 +544,7 @@ const reactionActions = {
524
544
  }
525
545
  return Session$8.authedFunction({
526
546
  method: 'POST',
527
- url: Helper$4.getUrl('reactions', 'comments/add'),
547
+ url: Helper$3.getUrl('reactions', 'comments/add'),
528
548
  data
529
549
  });
530
550
  },
@@ -547,7 +567,7 @@ const reactionActions = {
547
567
  }
548
568
  return Session$8.authedFunction({
549
569
  method: 'GET',
550
- url: Helper$4.getUrl('reactions', 'comments/get', query)
570
+ url: Helper$3.getUrl('reactions', 'comments/get', query)
551
571
  });
552
572
  }
553
573
  };
@@ -580,30 +600,6 @@ var jobStatusOptions = [
580
600
  }
581
601
  ];
582
602
 
583
- const {
584
- Helper: Helper$3
585
- } = PlussCore__namespace;
586
- const jobsUpdate = (site, isdashboard) => {
587
- return dispatch => {
588
- if (isdashboard) dispatch({
589
- type: JOBS_LOADING
590
- });
591
- maintenanceActions.getJobsRecursive(site).then(res => {
592
- const currentSite = Helper$3.readStorageWithCookie('site');
593
- if (!___default["default"].isEmpty(res) && res[0].site === currentSite) {
594
- dispatch({
595
- type: JOBS_LOADED,
596
- payload: res
597
- });
598
- } else {
599
- dispatch({
600
- type: JOBS_LOADED,
601
- payload: []
602
- });
603
- }
604
- });
605
- };
606
- };
607
603
  const jobsLoaded = events => {
608
604
  return {
609
605
  type: JOBS_LOADED,
@@ -750,41 +746,241 @@ const {
750
746
  } = PlussCore__namespace;
751
747
  class JobList extends React.Component {
752
748
  constructor(props) {
749
+ var _this;
753
750
  super(props);
754
- _defineProperty__default["default"](this, "setRequesters", jobs => {
755
- const requesters = ___default["default"].orderBy(___default["default"].uniqBy(jobs.map(j => ({
756
- id: j.userID,
757
- displayName: j.userName,
758
- profilePic: j.userProfilePic
759
- })), 'id'), 'displayName', 'asc');
760
- this.setState({
761
- requesters
762
- });
751
+ _this = this;
752
+ _defineProperty__default["default"](this, "getAssignees", async () => {
753
+ try {
754
+ const res = await maintenanceActions$1.getAssignees(this.props.auth.site);
755
+ this.setState({
756
+ assignees: res.data.Users
757
+ });
758
+ } catch (error) {
759
+ console.error('getAssignees', error);
760
+ }
763
761
  });
764
- _defineProperty__default["default"](this, "getJobs", async () => {
762
+ /**
763
+ * Build server-side filter params from current filter state.
764
+ * Translates UI filter selections into API query parameters.
765
+ */
766
+ _defineProperty__default["default"](this, "buildFilterParams", () => {
765
767
  const {
766
- auth
768
+ selectedStatusFilter,
769
+ selectedTypeFilter,
770
+ selectedPriorityFilter,
771
+ selectedUserFilter,
772
+ selectedTimeFilterStart,
773
+ selectedTimeFilterEnd,
774
+ searchTerm
775
+ } = this.state;
776
+ const {
777
+ statusTypes
767
778
  } = this.props;
768
- try {
769
- const res = await maintenanceActions.getJobsRecursive(auth.site);
770
- if (!___default["default"].isEmpty(res) && res[0].site === auth.site) {
771
- this.setRequesters(res);
772
- this.props.jobsLoaded(res);
779
+ const params = {};
780
+
781
+ // Status filter: translate "All Incomplete" into the actual incomplete statuses
782
+ if (selectedStatusFilter) {
783
+ if (selectedStatusFilter === STATUS_IMCOMPLETE) {
784
+ // Exclude completed statuses - pass all non-completed status texts
785
+ const incompleteStatuses = statusTypes.filter(s => s.category !== STATUS_COMPLETED).map(s => s.text);
786
+ if (incompleteStatuses.length > 0) {
787
+ params.status = incompleteStatuses.join(',');
788
+ }
789
+ } else {
790
+ params.status = selectedStatusFilter;
773
791
  }
774
- } catch (error) {
775
- console.error('getJobs', error);
776
792
  }
793
+ if (selectedTypeFilter) {
794
+ params.type = selectedTypeFilter;
795
+ }
796
+ if (selectedPriorityFilter) {
797
+ params.priority = selectedPriorityFilter;
798
+ }
799
+ if (selectedUserFilter) {
800
+ params.assignee = selectedUserFilter;
801
+ }
802
+ if (selectedTimeFilterStart) {
803
+ params.startTime = selectedTimeFilterStart;
804
+ }
805
+ if (selectedTimeFilterEnd) {
806
+ params.endTime = selectedTimeFilterEnd;
807
+ }
808
+ if (searchTerm) {
809
+ params.search = searchTerm;
810
+ }
811
+ return params;
812
+ });
813
+ /**
814
+ * Minimum number of items to auto-fill before stopping background fetch.
815
+ * Because DynamoDB pages are unfiltered and the backend filters after
816
+ * query, a single page may yield very few matching results. We keep
817
+ * fetching in the background until we have this many items to display.
818
+ */
819
+ _defineProperty__default["default"](this, "MIN_PAGE_SIZE", 20);
820
+ /**
821
+ * Monotonically increasing ID used to ignore stale fetch results.
822
+ * When a filter changes, fetchJobs() increments this counter. Any
823
+ * in-flight fetch or autoFillPages loop checks the counter before
824
+ * applying results — if it doesn't match, the results are discarded.
825
+ *
826
+ * Alternative: AbortController would actually cancel the HTTP request
827
+ * (axios supports it via the `signal` config option). That would save
828
+ * bandwidth and server load but requires threading `signal` through
829
+ * authedFunction → getJobs2 → fetchPage (3 layers). The fetchId
830
+ * approach is simpler and sufficient — stale requests still complete
831
+ * in the background but their results are ignored.
832
+ */
833
+ _defineProperty__default["default"](this, "_fetchId", 0);
834
+ /**
835
+ * Fetch a single page from the server using the given lastKey.
836
+ */
837
+ _defineProperty__default["default"](this, "fetchPage", async function () {
838
+ let lastKey = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
839
+ const {
840
+ auth
841
+ } = _this.props;
842
+ const filters = _this.buildFilterParams();
843
+ return maintenanceActions$1.getJobs2(auth.site, filters.status, filters.type, filters.priority, filters.assignee, filters.startTime, filters.endTime, filters.search, lastKey);
844
+ });
845
+ /**
846
+ * Fetch the first page and render immediately.
847
+ * If fewer than MIN_PAGE_SIZE items returned and there's more data,
848
+ * kicks off a background loop that keeps fetching and appending
849
+ * so results appear progressively.
850
+ */
851
+ _defineProperty__default["default"](this, "fetchJobs", async () => {
852
+ const fetchId = ++this._fetchId;
853
+ this.setState({
854
+ loading: true,
855
+ loadingMore: false
856
+ }, async () => {
857
+ try {
858
+ const res = await this.fetchPage(null);
859
+ if (this._fetchId !== fetchId) return; // stale fetch
860
+
861
+ const items = res.data.Items || [];
862
+ const lastKey = res.data.LastKey || null;
863
+ this.setState({
864
+ jobs: items,
865
+ lastKey,
866
+ hasMore: !!lastKey,
867
+ loading: false
868
+ });
869
+ this.props.jobsLoaded(items);
870
+ this.setRequesters(items);
871
+
872
+ // Auto-fill in background if first page was sparse
873
+ if (lastKey && items.length < this.MIN_PAGE_SIZE) {
874
+ this.autoFillPages(items, lastKey, fetchId);
875
+ }
876
+ } catch (error) {
877
+ if (this._fetchId !== fetchId) return; // stale fetch
878
+ console.error('fetchJobs', error);
879
+ this.setState({
880
+ loading: false
881
+ });
882
+ }
883
+ });
777
884
  });
778
- _defineProperty__default["default"](this, "getAssignees", async () => {
885
+ /**
886
+ * Background loop: keep fetching pages and appending results
887
+ * until we have MIN_PAGE_SIZE items or run out of data.
888
+ * Each page is rendered as it arrives so the user sees
889
+ * results appearing progressively.
890
+ */
891
+ _defineProperty__default["default"](this, "autoFillPages", async (existingJobs, startKey, fetchId) => {
892
+ this.setState({
893
+ loadingMore: true
894
+ });
895
+ let currentJobs = existingJobs;
896
+ let currentLastKey = startKey;
779
897
  try {
780
- const res = await maintenanceActions.getAssignees(this.props.auth.site);
781
- this.setState({
782
- assignees: res.data.Users
783
- });
898
+ while (currentLastKey && currentJobs.length < this.MIN_PAGE_SIZE) {
899
+ const res = await this.fetchPage(currentLastKey);
900
+ if (this._fetchId !== fetchId) return; // stale fetch
901
+
902
+ const items = res.data.Items || [];
903
+ currentLastKey = res.data.LastKey || null;
904
+ currentJobs = [...currentJobs, ...items];
905
+
906
+ // Append to UI immediately after each page arrives
907
+ this.setState({
908
+ jobs: currentJobs,
909
+ lastKey: currentLastKey,
910
+ hasMore: !!currentLastKey
911
+ });
912
+ if (items.length > 0) {
913
+ this.props.jobsLoaded(items);
914
+ this.setRequesters(currentJobs);
915
+ }
916
+ }
784
917
  } catch (error) {
785
- console.error('getAssignees', error);
918
+ if (this._fetchId !== fetchId) return; // stale fetch
919
+ console.error('autoFillPages', error);
920
+ } finally {
921
+ if (this._fetchId === fetchId) {
922
+ this.setState({
923
+ loadingMore: false
924
+ });
925
+ }
786
926
  }
787
927
  });
928
+ /**
929
+ * Load the next batch of jobs when the user clicks "Load More".
930
+ * Fetches one page and also auto-fills in background if sparse.
931
+ */
932
+ _defineProperty__default["default"](this, "loadMore", async () => {
933
+ const fetchId = ++this._fetchId;
934
+ const {
935
+ lastKey,
936
+ jobs
937
+ } = this.state;
938
+ this.setState({
939
+ loadingMore: true
940
+ }, async () => {
941
+ try {
942
+ const res = await this.fetchPage(lastKey);
943
+ if (this._fetchId !== fetchId) return; // stale fetch
944
+
945
+ const items = res.data.Items || [];
946
+ const newLastKey = res.data.LastKey || null;
947
+ const updatedJobs = [...jobs, ...items];
948
+ this.setState({
949
+ jobs: updatedJobs,
950
+ lastKey: newLastKey,
951
+ hasMore: !!newLastKey
952
+ });
953
+ if (items.length > 0) {
954
+ this.props.jobsLoaded(items);
955
+ }
956
+
957
+ // Auto-fill in background if this page was sparse
958
+ if (newLastKey && items.length < this.MIN_PAGE_SIZE) {
959
+ this.autoFillPages(updatedJobs, newLastKey, fetchId);
960
+ } else {
961
+ this.setState({
962
+ loadingMore: false
963
+ });
964
+ }
965
+ } catch (error) {
966
+ if (this._fetchId !== fetchId) return; // stale fetch
967
+ console.error('loadMore', error);
968
+ this.setState({
969
+ loadingMore: false
970
+ });
971
+ }
972
+ });
973
+ });
974
+ _defineProperty__default["default"](this, "setRequesters", jobs => {
975
+ const requesters = ___default["default"].orderBy(___default["default"].uniqBy(jobs.map(j => ({
976
+ id: j.userID,
977
+ displayName: j.userName,
978
+ profilePic: j.userProfilePic
979
+ })), 'id'), 'displayName', 'asc');
980
+ this.setState({
981
+ requesters
982
+ });
983
+ });
788
984
  _defineProperty__default["default"](this, "sortByCol", col => {
789
985
  const {
790
986
  sortColumn,
@@ -804,9 +1000,12 @@ class JobList extends React.Component {
804
1000
  _defineProperty__default["default"](this, "onRemoveRequest", async request => {
805
1001
  if (window.confirm(values.textAreYouSureYouWantToDelete)) {
806
1002
  this.props.removeJob(request.id);
1003
+ // Remove from local state immediately
1004
+ this.setState(prevState => ({
1005
+ jobs: prevState.jobs.filter(j => j.id !== request.id)
1006
+ }));
807
1007
  try {
808
- await maintenanceActions.deleteJob(this.props.auth.site, request.id);
809
- this.getJobs();
1008
+ await maintenanceActions$1.deleteJob(this.props.auth.site, request.id);
810
1009
  } catch (error) {
811
1010
  console.log('onRemoveRequest', error);
812
1011
  alert('Something went wrong with the request. Please try again.');
@@ -825,20 +1024,20 @@ class JobList extends React.Component {
825
1024
  });
826
1025
  _defineProperty__default["default"](this, "selectTypeFilter", filter => {
827
1026
  this.setState({
828
- selectedTypeFilter: filter
829
- });
1027
+ selectedTypeFilter: filter || null
1028
+ }, () => this.fetchJobs());
830
1029
  this.closeFilter();
831
1030
  });
832
1031
  _defineProperty__default["default"](this, "selectPriorityFilter", filter => {
833
1032
  this.setState({
834
- selectedPriorityFilter: filter
835
- });
1033
+ selectedPriorityFilter: filter || null
1034
+ }, () => this.fetchJobs());
836
1035
  this.closeFilter();
837
1036
  });
838
1037
  _defineProperty__default["default"](this, "selectStatusFilter", filter => {
839
1038
  this.setState({
840
- selectedStatusFilter: filter
841
- });
1039
+ selectedStatusFilter: filter || null
1040
+ }, () => this.fetchJobs());
842
1041
  this.closeFilter();
843
1042
  });
844
1043
  _defineProperty__default["default"](this, "timeFilterChanged", selectedTimeFilter => {
@@ -882,7 +1081,7 @@ class JobList extends React.Component {
882
1081
  selectedTimeFilterStart: startTime,
883
1082
  selectedTimeFilterEnd: endTime,
884
1083
  selectedTimeFilterText: text
885
- });
1084
+ }, () => this.fetchJobs());
886
1085
  this.closeFilter();
887
1086
  });
888
1087
  _defineProperty__default["default"](this, "removeTimeFilter", () => {
@@ -890,7 +1089,7 @@ class JobList extends React.Component {
890
1089
  selectedTimeFilterStart: null,
891
1090
  selectedTimeFilterEnd: null,
892
1091
  selectedTimeFilterText: null
893
- });
1092
+ }, () => this.fetchJobs());
894
1093
  });
895
1094
  _defineProperty__default["default"](this, "onHandleChange", event => {
896
1095
  var stateChange = {};
@@ -906,7 +1105,7 @@ class JobList extends React.Component {
906
1105
  this.setState({
907
1106
  selectedUserFilter: null,
908
1107
  selectedUserFilterText: null
909
- });
1108
+ }, () => this.fetchJobs());
910
1109
  });
911
1110
  _defineProperty__default["default"](this, "saveUserFilter", () => {
912
1111
  if (!this.state.selectedAssignee) {
@@ -916,7 +1115,7 @@ class JobList extends React.Component {
916
1115
  selectedUserFilter: this.state.selectedAssignee.id,
917
1116
  selectedUserFilterText: this.state.selectedAssignee.displayName,
918
1117
  selectedAssignee: null
919
- });
1118
+ }, () => this.fetchJobs());
920
1119
  }
921
1120
  this.closeFilter();
922
1121
  });
@@ -929,7 +1128,7 @@ class JobList extends React.Component {
929
1128
  this.setState({
930
1129
  selectedRequesterFilter: null,
931
1130
  selectedRequesterFilterText: null
932
- });
1131
+ }, () => this.fetchJobs());
933
1132
  });
934
1133
  _defineProperty__default["default"](this, "saveRequesterFilter", () => {
935
1134
  if (!this.state.selectedRequester) {
@@ -939,7 +1138,7 @@ class JobList extends React.Component {
939
1138
  selectedRequesterFilter: this.state.selectedRequester.id,
940
1139
  selectedRequesterFilterText: this.state.selectedRequester.displayName,
941
1140
  selectedRequester: null
942
- });
1141
+ }, () => this.fetchJobs());
943
1142
  }
944
1143
  this.closeFilter();
945
1144
  });
@@ -950,85 +1149,47 @@ class JobList extends React.Component {
950
1149
  lastSearch: thisSearchTime
951
1150
  });
952
1151
  setTimeout(() => {
953
- // delayed setter to avoid filtering on every keypress
1152
+ // delayed setter to avoid searching on every keypress
954
1153
  if (this.state.lastSearch === thisSearchTime) {
955
1154
  this.setState({
956
1155
  searchTerm: this.state.search
957
- });
1156
+ }, () => this.fetchJobs());
958
1157
  }
959
1158
  }, 500);
960
1159
  });
1160
+ /**
1161
+ * Get the source array for rendering.
1162
+ * With server-side filtering, this is simply the local jobs array
1163
+ * sorted by the user's selected sort column.
1164
+ * The requester filter still applies client-side since the backend
1165
+ * doesn't have a requester/userID filter param.
1166
+ */
961
1167
  _defineProperty__default["default"](this, "getSource", () => {
962
- let source = this.props.source;
1168
+ let source = this.state.jobs;
963
1169
 
964
- // filter by time
965
- if (this.state.selectedTimeFilterStart && this.state.selectedTimeFilterEnd) {
966
- source = ___default["default"].filter(source, r => {
967
- return r.createdUnix >= this.state.selectedTimeFilterStart && r.createdUnix <= this.state.selectedTimeFilterEnd;
968
- });
969
- }
1170
+ // Filter out deleted items
1171
+ source = ___default["default"].filter(source, ev => ev != null && !ev.Deleted);
970
1172
 
971
- // filter by type
972
- if (this.state.selectedTypeFilter) {
973
- source = ___default["default"].filter(source, r => {
974
- return r.type === this.state.selectedTypeFilter;
975
- });
976
- }
977
-
978
- // filter by priority
979
- if (this.state.selectedPriorityFilter) {
980
- const defaultPriority = getDefaultPriority().name;
981
- source = ___default["default"].filter(source, r => {
982
- return r.priority === this.state.selectedPriorityFilter || this.state.selectedPriorityFilter === defaultPriority && ___default["default"].isNil(r.priority);
983
- });
984
- }
985
-
986
- // filter by status
987
- if (this.state.selectedStatusFilter) {
988
- const {
989
- statusTypes
990
- } = this.props;
991
- const defaultStatus = statusTypes.find(s => s.category === STATUS_NOT_ACTIONED);
992
- source = ___default["default"].filter(source, r => {
993
- const status = statusTypes.find(s => s.text === r.status) || defaultStatus;
994
- if (this.state.selectedStatusFilter === STATUS_IMCOMPLETE) {
995
- return status.category !== STATUS_COMPLETED;
996
- }
997
- return status.text === this.state.selectedStatusFilter;
998
- });
999
- }
1000
- if (this.state.selectedUserFilter) {
1001
- source = ___default["default"].filter(source, r => {
1002
- return r.AssigneeId === this.state.selectedUserFilter;
1003
- });
1004
- }
1173
+ // Requester filter still client-side (no backend param for userID filtering)
1005
1174
  if (this.state.selectedRequesterFilter) {
1006
1175
  source = ___default["default"].filter(source, r => {
1007
1176
  return r.userID === this.state.selectedRequesterFilter;
1008
1177
  });
1009
1178
  }
1010
- if (!___default["default"].isEmpty(this.state.searchTerm)) {
1011
- source = ___default["default"].filter(source, r => {
1012
- if (r.jobId && r.jobId === this.state.searchTerm) {
1013
- return true;
1014
- }
1015
- if (r.room && r.room.toLowerCase().indexOf(this.state.searchTerm.toLowerCase()) > -1) {
1016
- return true;
1017
- }
1018
- if (r.title && r.title.toLowerCase().indexOf(this.state.searchTerm.toLowerCase()) > -1) {
1019
- return true;
1179
+
1180
+ // Skip sorting while auto-fill is appending results to avoid jumpiness.
1181
+ // Sort is re-applied when the user clicks a column header or when
1182
+ // auto-fill completes.
1183
+ if (!this.state.loadingMore) {
1184
+ source = ___default["default"].sortBy(source, event => {
1185
+ if (this.state.sortColumn === 'assigned') {
1186
+ return event.Assignee ? event.Assignee.displayName : 'Unassigned';
1020
1187
  }
1021
- return false;
1188
+ if (this.state.sortColumn !== 'createdUnix') return event[this.state.sortColumn];
1189
+ return event.createdUnix;
1022
1190
  });
1191
+ if (this.state.sortDesc) source.reverse();
1023
1192
  }
1024
- source = ___default["default"].sortBy(source, event => {
1025
- if (this.state.sortColumn === 'assigned') {
1026
- return event.Assignee ? event.Assignee.displayName : 'Unassigned';
1027
- }
1028
- if (this.state.sortColumn !== 'createdUnix') return event[this.state.sortColumn];
1029
- return event.createdUnix;
1030
- });
1031
- if (this.state.sortDesc) source.reverse();
1032
1193
  return source;
1033
1194
  });
1034
1195
  _defineProperty__default["default"](this, "getCustomFieldValue", field => {
@@ -1140,13 +1301,60 @@ class JobList extends React.Component {
1140
1301
  exportCsvOpen: false
1141
1302
  });
1142
1303
  });
1304
+ _defineProperty__default["default"](this, "hasActiveFilters", () => {
1305
+ const {
1306
+ selectedTypeFilter,
1307
+ selectedPriorityFilter,
1308
+ selectedStatusFilter,
1309
+ selectedTimeFilterStart,
1310
+ selectedUserFilter,
1311
+ selectedRequesterFilter,
1312
+ searchTerm
1313
+ } = this.state;
1314
+ return !!(selectedTypeFilter || selectedPriorityFilter || selectedStatusFilter || selectedTimeFilterStart || selectedUserFilter || selectedRequesterFilter || searchTerm);
1315
+ });
1316
+ _defineProperty__default["default"](this, "clearAllFilters", () => {
1317
+ this.setState({
1318
+ selectedTypeFilter: null,
1319
+ selectedPriorityFilter: null,
1320
+ selectedStatusFilter: null,
1321
+ selectedTimeFilterStart: null,
1322
+ selectedTimeFilterEnd: null,
1323
+ selectedTimeFilterText: null,
1324
+ selectedUserFilter: null,
1325
+ selectedUserFilterText: null,
1326
+ selectedRequesterFilter: null,
1327
+ selectedRequesterFilterText: null,
1328
+ search: '',
1329
+ searchTerm: ''
1330
+ }, () => this.fetchJobs());
1331
+ });
1143
1332
  this.state = {
1144
- showCompleted: false,
1145
1333
  sortColumn: 'createdUnix',
1146
1334
  sortDesc: true,
1147
1335
  selectedTimeFilter: Analytics$2.getAnalyticsFilterOptions()[1],
1148
1336
  assignees: [],
1149
- requesters: []
1337
+ requesters: [],
1338
+ // Server-side pagination state
1339
+ jobs: [],
1340
+ lastKey: null,
1341
+ hasMore: false,
1342
+ loading: false,
1343
+ loadingMore: false,
1344
+ // Filters (applied to server-side queries)
1345
+ selectedTypeFilter: null,
1346
+ selectedPriorityFilter: null,
1347
+ selectedStatusFilter: null,
1348
+ selectedTimeFilterStart: null,
1349
+ selectedTimeFilterEnd: null,
1350
+ selectedTimeFilterText: null,
1351
+ selectedUserFilter: null,
1352
+ selectedUserFilterText: null,
1353
+ selectedRequesterFilter: null,
1354
+ selectedRequesterFilterText: null,
1355
+ search: '',
1356
+ searchTerm: '',
1357
+ lastSearch: null
1150
1358
  };
1151
1359
  this.exportColumns = [{
1152
1360
  label: 'Select All',
@@ -1206,7 +1414,7 @@ class JobList extends React.Component {
1206
1414
  }
1207
1415
  componentDidMount() {
1208
1416
  this.props.jobStatusesUpdate(this.props.auth.site);
1209
- this.getJobs();
1417
+ this.fetchJobs();
1210
1418
  this.getAssignees();
1211
1419
  }
1212
1420
  renderFilterPopup() {
@@ -1220,7 +1428,7 @@ class JobList extends React.Component {
1220
1428
  minWidth: 400,
1221
1429
  hasPadding: true,
1222
1430
  onClose: this.closeFilter
1223
- }, ___default["default"].sortBy(___default["default"].uniq(this.props.source.map(r => r.type)), t => t.toLowerCase()).map(type => {
1431
+ }, ___default["default"].sortBy(___default["default"].uniq(this.state.jobs.map(r => r.type)), t => t.toLowerCase()).map(type => {
1224
1432
  return /*#__PURE__*/React__default["default"].createElement(Components$7.Tag, {
1225
1433
  key: type,
1226
1434
  onClick: () => {
@@ -1411,7 +1619,7 @@ class JobList extends React.Component {
1411
1619
  const status = ev.status && statusTypes.find(s => s.text === ev.status) || defaultStatus;
1412
1620
  const priority = getJobPriority(ev.priority);
1413
1621
  return /*#__PURE__*/React__default["default"].createElement("tr", {
1414
- key: index
1622
+ key: ev.id || index
1415
1623
  }, /*#__PURE__*/React__default["default"].createElement("td", null, ev.jobId), /*#__PURE__*/React__default["default"].createElement("td", {
1416
1624
  className: "table-TitleColumn"
1417
1625
  }, /*#__PURE__*/React__default["default"].createElement(reactRouterDom.Link, {
@@ -1492,8 +1700,28 @@ class JobList extends React.Component {
1492
1700
  if (col !== this.state.sortColumn) return '';
1493
1701
  return ' table--columnActive';
1494
1702
  }
1703
+ renderLoading() {
1704
+ return /*#__PURE__*/React__default["default"].createElement("div", {
1705
+ className: "flex flex-center-row",
1706
+ style: {
1707
+ padding: 40
1708
+ }
1709
+ }, /*#__PURE__*/React__default["default"].createElement(FontAwesome__default["default"], {
1710
+ style: {
1711
+ fontSize: 32,
1712
+ color: PlussCore.Colours.COLOUR_DUSK_LIGHT
1713
+ },
1714
+ name: "spinner fa-pulse fa-fw"
1715
+ }), /*#__PURE__*/React__default["default"].createElement("span", {
1716
+ className: "marginLeft-16 fontRegular fontSize-14",
1717
+ style: {
1718
+ color: PlussCore.Colours.TEXT_MID
1719
+ }
1720
+ }, "Loading ", values.textTitleRequests, "\u2026"));
1721
+ }
1495
1722
  renderEmpty() {
1496
1723
  const title = this.props.strings["".concat(values.featureKey, "_textTitleRequests")] || values.textTitleRequests;
1724
+ const hasFilters = this.hasActiveFilters();
1497
1725
  return /*#__PURE__*/React__default["default"].createElement("div", {
1498
1726
  style: {
1499
1727
  display: 'flex',
@@ -1505,7 +1733,22 @@ class JobList extends React.Component {
1505
1733
  }
1506
1734
  }, /*#__PURE__*/React__default["default"].createElement("div", {
1507
1735
  className: "emptyState"
1508
- }), /*#__PURE__*/React__default["default"].createElement("div", {
1736
+ }), hasFilters ? /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("div", {
1737
+ className: "marginTop-32",
1738
+ style: {
1739
+ maxWidth: 500,
1740
+ textAlign: 'center'
1741
+ }
1742
+ }, /*#__PURE__*/React__default["default"].createElement("span", {
1743
+ className: "fontRegular fontSize-13"
1744
+ }, "No ", title.toLowerCase(), " match your current filters.")), /*#__PURE__*/React__default["default"].createElement("div", {
1745
+ className: "marginTop-16"
1746
+ }, /*#__PURE__*/React__default["default"].createElement(Components$7.Button, {
1747
+ inline: true,
1748
+ buttonType: "tertiary",
1749
+ onClick: this.clearAllFilters,
1750
+ isActive: true
1751
+ }, "Clear All Filters"))) : /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("div", {
1509
1752
  className: "marginTop-32",
1510
1753
  style: {
1511
1754
  maxWidth: 500,
@@ -1521,11 +1764,58 @@ class JobList extends React.Component {
1521
1764
  maxWidth: 500,
1522
1765
  textAlign: 'center'
1523
1766
  }
1524
- }, values.textEmptyExample));
1767
+ }, values.textEmptyExample)));
1768
+ }
1769
+ renderLoadMore() {
1770
+ const {
1771
+ hasMore,
1772
+ loadingMore
1773
+ } = this.state;
1774
+ if (!hasMore && !loadingMore) return null;
1775
+
1776
+ // During background auto-fill, show a subtle spinner without a button
1777
+ if (loadingMore) {
1778
+ return /*#__PURE__*/React__default["default"].createElement("div", {
1779
+ className: "flex flex-center-row",
1780
+ style: {
1781
+ padding: '16px 0'
1782
+ }
1783
+ }, /*#__PURE__*/React__default["default"].createElement(FontAwesome__default["default"], {
1784
+ name: "spinner fa-pulse fa-fw",
1785
+ style: {
1786
+ fontSize: 16,
1787
+ color: PlussCore.Colours.TEXT_MID,
1788
+ marginRight: 8
1789
+ }
1790
+ }), /*#__PURE__*/React__default["default"].createElement("span", {
1791
+ className: "fontRegular fontSize-13",
1792
+ style: {
1793
+ color: PlussCore.Colours.TEXT_MID
1794
+ }
1795
+ }, "Loading more results\u2026"));
1796
+ }
1797
+ return /*#__PURE__*/React__default["default"].createElement("div", {
1798
+ className: "flex flex-center-row",
1799
+ style: {
1800
+ padding: '16px 0'
1801
+ }
1802
+ }, /*#__PURE__*/React__default["default"].createElement(Components$7.Button, {
1803
+ inline: true,
1804
+ buttonType: "tertiary",
1805
+ onClick: this.loadMore,
1806
+ isActive: true
1807
+ }, "Load More"));
1525
1808
  }
1526
1809
  renderContent() {
1527
- if (___default["default"].isEmpty(this.props.source)) return this.renderEmpty();
1528
- return /*#__PURE__*/React__default["default"].createElement(reactBootstrap.Table, {
1810
+ const {
1811
+ loading,
1812
+ loadingMore,
1813
+ jobs
1814
+ } = this.state;
1815
+ if (loading) return this.renderLoading();
1816
+ if (___default["default"].isEmpty(jobs) && !loadingMore) return this.renderEmpty();
1817
+ if (___default["default"].isEmpty(jobs)) return this.renderLoading();
1818
+ return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement(reactBootstrap.Table, {
1529
1819
  className: "plussTable",
1530
1820
  striped: true,
1531
1821
  bordered: true,
@@ -1608,7 +1898,7 @@ class JobList extends React.Component {
1608
1898
  style: {
1609
1899
  width: 50
1610
1900
  }
1611
- }))), /*#__PURE__*/React__default["default"].createElement("tbody", null, this.renderRequests()));
1901
+ }))), /*#__PURE__*/React__default["default"].createElement("tbody", null, this.renderRequests())), this.renderLoadMore());
1612
1902
  }
1613
1903
  renderFilters() {
1614
1904
  let typeFilter = /*#__PURE__*/React__default["default"].createElement(Components$7.Tag, {
@@ -1657,13 +1947,9 @@ class JobList extends React.Component {
1657
1947
  typeFilter = /*#__PURE__*/React__default["default"].createElement(Components$7.Tag, {
1658
1948
  className: "marginRight-10",
1659
1949
  onClick: () => {
1660
- this.openFilter('type');
1661
- },
1662
- rightIcon: "close",
1663
- rightClick: e => {
1664
- e.stopPropagation();
1665
1950
  this.selectTypeFilter();
1666
1951
  },
1952
+ rightIcon: "close",
1667
1953
  text: this.state.selectedTypeFilter
1668
1954
  });
1669
1955
  }
@@ -1671,13 +1957,9 @@ class JobList extends React.Component {
1671
1957
  priorityFilter = /*#__PURE__*/React__default["default"].createElement(Components$7.Tag, {
1672
1958
  className: "marginRight-10",
1673
1959
  onClick: () => {
1674
- this.openFilter('priority');
1675
- },
1676
- rightIcon: "close",
1677
- rightClick: e => {
1678
- e.stopPropagation();
1679
1960
  this.selectPriorityFilter();
1680
1961
  },
1962
+ rightIcon: "close",
1681
1963
  text: this.state.selectedPriorityFilter
1682
1964
  });
1683
1965
  }
@@ -1685,13 +1967,9 @@ class JobList extends React.Component {
1685
1967
  statusFilter = /*#__PURE__*/React__default["default"].createElement(Components$7.Tag, {
1686
1968
  className: "marginRight-10",
1687
1969
  onClick: () => {
1688
- this.openFilter('status');
1689
- },
1690
- rightIcon: "close",
1691
- rightClick: e => {
1692
- e.stopPropagation();
1693
1970
  this.selectStatusFilter();
1694
1971
  },
1972
+ rightIcon: "close",
1695
1973
  text: this.state.selectedStatusFilter
1696
1974
  });
1697
1975
  }
@@ -1713,13 +1991,9 @@ class JobList extends React.Component {
1713
1991
  userFilter = /*#__PURE__*/React__default["default"].createElement(Components$7.Tag, {
1714
1992
  className: "marginRight-10",
1715
1993
  onClick: () => {
1716
- this.openFilter('user');
1717
- },
1718
- rightIcon: "close",
1719
- rightClick: e => {
1720
- e.stopPropagation();
1721
1994
  this.removeUserFilter();
1722
1995
  },
1996
+ rightIcon: "close",
1723
1997
  text: this.state.selectedUserFilterText
1724
1998
  });
1725
1999
  }
@@ -1727,13 +2001,9 @@ class JobList extends React.Component {
1727
2001
  requesterFilter = /*#__PURE__*/React__default["default"].createElement(Components$7.Tag, {
1728
2002
  className: "marginRight-10",
1729
2003
  onClick: () => {
1730
- this.openFilter('requester');
1731
- },
1732
- rightIcon: "close",
1733
- rightClick: e => {
1734
- e.stopPropagation();
1735
2004
  this.removeRequesterFilter();
1736
2005
  },
2006
+ rightIcon: "close",
1737
2007
  text: this.state.selectedRequesterFilterText
1738
2008
  });
1739
2009
  }
@@ -1811,7 +2081,7 @@ class JobTypes extends React.Component {
1811
2081
  super(props);
1812
2082
  _defineProperty__default["default"](this, "getJobTypes", async () => {
1813
2083
  try {
1814
- const res = await maintenanceActions.getJobTypes(this.props.auth.site);
2084
+ const res = await maintenanceActions$1.getJobTypes(this.props.auth.site);
1815
2085
  if (res.data != null) this.props.jobTypesLoaded(res.data);
1816
2086
  } catch (error) {
1817
2087
  console.error('getJobTypes', error);
@@ -1836,7 +2106,7 @@ class JobTypes extends React.Component {
1836
2106
  _defineProperty__default["default"](this, "onRemoveJobType", async ev => {
1837
2107
  if (!window.confirm("Are you sure you want to delete ".concat(ev.typeName, "?"))) return;
1838
2108
  try {
1839
- await maintenanceActions.deleteJobType(this.props.auth.site, ev.id);
2109
+ await maintenanceActions$1.deleteJobType(this.props.auth.site, ev.id);
1840
2110
  const index = ___default["default"].findIndex(this.state.jobList, job => {
1841
2111
  return job != null && job.id === ev.id;
1842
2112
  });
@@ -2237,43 +2507,43 @@ class Configuration extends React.Component {
2237
2507
  return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement("p", {
2238
2508
  className: "fontMedium fontSize-36 text-dark"
2239
2509
  }, "Statuses"), /*#__PURE__*/React__default["default"].createElement("div", {
2240
- style: styles$6.statusCategoryHeading
2510
+ style: styles$5.statusCategoryHeading
2241
2511
  }, /*#__PURE__*/React__default["default"].createElement("span", {
2242
2512
  className: "fontMedium fontSize-16 text-bold"
2243
2513
  }, "Status Category")), statusTypes.map((status, index) => {
2244
2514
  return /*#__PURE__*/React__default["default"].createElement("div", {
2245
2515
  key: "".concat(status.text, "_").concat(index),
2246
- style: styles$6.statusTypeContainer
2516
+ style: styles$5.statusTypeContainer
2247
2517
  }, /*#__PURE__*/React__default["default"].createElement("div", {
2248
2518
  key: status.text,
2249
2519
  className: "statusLabel",
2250
- style: _objectSpread$5(_objectSpread$5({}, styles$6.statusTextContainer), {}, {
2520
+ style: _objectSpread$5(_objectSpread$5({}, styles$5.statusTextContainer), {}, {
2251
2521
  backgroundColor: status.color
2252
2522
  })
2253
2523
  }, /*#__PURE__*/React__default["default"].createElement("span", {
2254
2524
  className: "statusLabel_text"
2255
2525
  }, status.text)), /*#__PURE__*/React__default["default"].createElement("div", {
2256
- style: styles$6.statusCategoryContainer
2526
+ style: styles$5.statusCategoryContainer
2257
2527
  }, /*#__PURE__*/React__default["default"].createElement("span", {
2258
2528
  className: "fontMedium fontSize-16 text-dark"
2259
2529
  }, status.category)), /*#__PURE__*/React__default["default"].createElement(FontAwesome__default["default"], {
2260
- style: _objectSpread$5(_objectSpread$5({}, styles$6.statusIcon), {}, {
2530
+ style: _objectSpread$5(_objectSpread$5({}, styles$5.statusIcon), {}, {
2261
2531
  visibility: index === 0 ? 'hidden' : 'visible'
2262
2532
  }),
2263
2533
  name: 'arrow-up',
2264
2534
  onClick: () => this.onMoveStatus(index, true)
2265
2535
  }), /*#__PURE__*/React__default["default"].createElement(FontAwesome__default["default"], {
2266
- style: _objectSpread$5(_objectSpread$5({}, styles$6.statusIcon), {}, {
2536
+ style: _objectSpread$5(_objectSpread$5({}, styles$5.statusIcon), {}, {
2267
2537
  visibility: index === statusTypes.length - 1 ? 'hidden' : 'visible'
2268
2538
  }),
2269
2539
  name: 'arrow-down',
2270
2540
  onClick: () => this.onMoveStatus(index, false)
2271
2541
  }), /*#__PURE__*/React__default["default"].createElement(FontAwesome__default["default"], {
2272
- style: _objectSpread$5({}, styles$6.statusIcon),
2542
+ style: _objectSpread$5({}, styles$5.statusIcon),
2273
2543
  name: "pencil",
2274
2544
  onClick: () => this.onEditStatus(index)
2275
2545
  }), /*#__PURE__*/React__default["default"].createElement(FontAwesome__default["default"], {
2276
- style: _objectSpread$5({}, styles$6.statusIcon),
2546
+ style: _objectSpread$5({}, styles$5.statusIcon),
2277
2547
  name: "minus-circle",
2278
2548
  onClick: () => this.onDeleteStatus(index)
2279
2549
  }));
@@ -2322,7 +2592,7 @@ class Configuration extends React.Component {
2322
2592
  renderSuccess() {
2323
2593
  if (!this.state.success) return null;
2324
2594
  return /*#__PURE__*/React__default["default"].createElement("span", {
2325
- style: _objectSpread$5(_objectSpread$5({}, styles$6.savedText), {}, {
2595
+ style: _objectSpread$5(_objectSpread$5({}, styles$5.savedText), {}, {
2326
2596
  color: Colours$2.COLOUR_GREEN
2327
2597
  })
2328
2598
  }, "Saved");
@@ -2439,7 +2709,7 @@ class Configuration extends React.Component {
2439
2709
  }, this.renderSubmit(), this.renderSuccess()), this.renderNewStatusPopup());
2440
2710
  }
2441
2711
  }
2442
- const styles$6 = {
2712
+ const styles$5 = {
2443
2713
  statusCategoryHeading: {
2444
2714
  marginLeft: 130,
2445
2715
  width: 160,
@@ -2498,52 +2768,6 @@ const {
2498
2768
  class RequestsHub extends React.Component {
2499
2769
  constructor(props) {
2500
2770
  super(props);
2501
- _defineProperty__default["default"](this, "setData", () => {
2502
- const allList = [];
2503
- this.state.allList.forEach(ev => {
2504
- if (ev != null && !ev.Deleted) allList.push(ev);
2505
- });
2506
- const upcoming = ___default["default"].filter(allList, ev => {
2507
- if (!ev) return false;
2508
- if (ev.status && ev.status === 'Completed') return false;
2509
- return true;
2510
- });
2511
- const completed = ___default["default"].filter(allList, ev => {
2512
- if (!ev) return false;
2513
- if (ev.status && ev.status === 'Completed') return true;
2514
- return false;
2515
- });
2516
-
2517
- // console.log('setData', upcoming, completed);
2518
- this.setState({
2519
- allList,
2520
- upcoming,
2521
- completed
2522
- });
2523
- });
2524
- _defineProperty__default["default"](this, "getData", () => {
2525
- const {
2526
- auth
2527
- } = this.props;
2528
- this.setState({
2529
- loadingAll: true
2530
- }, async () => {
2531
- try {
2532
- const res = await maintenanceActions.getJobsRecursive(auth.site);
2533
- this.setState({
2534
- loadingAll: false
2535
- });
2536
- if (!___default["default"].isEmpty(res) && res[0].site === auth.site) {
2537
- this.props.jobsLoaded(res);
2538
- }
2539
- } catch (error) {
2540
- console.error('getData', error);
2541
- this.setState({
2542
- loadingAll: false
2543
- });
2544
- }
2545
- });
2546
- });
2547
2771
  _defineProperty__default["default"](this, "onAddNew", () => {
2548
2772
  const {
2549
2773
  auth
@@ -2570,38 +2794,10 @@ class RequestsHub extends React.Component {
2570
2794
  _defineProperty__default["default"](this, "getSideBarSectionColour", id => this.state.selectedSection === id ? {
2571
2795
  backgroundColor: '#fff'
2572
2796
  } : {});
2573
- _defineProperty__default["default"](this, "renderStats", (stat, loading) => loading ? /*#__PURE__*/React__default["default"].createElement(FontAwesome__default["default"], {
2574
- style: styles$5.spinner,
2575
- name: "spinner fa-pulse fa-fw"
2576
- }) : stat);
2577
2797
  this.state = {
2578
- selectedSection: 'all',
2579
- location: '',
2580
- loadingAll: false,
2581
- loadingSubmissions: false,
2582
- submissionEntries: [],
2583
- allList: [],
2584
- completed: [],
2585
- upcoming: [],
2586
- now: moment__default["default"].utc(),
2587
- onlyFuture: true,
2588
- search: ''
2798
+ selectedSection: 'all'
2589
2799
  };
2590
2800
  }
2591
- UNSAFE_componentWillMount() {
2592
- this.updateProps(this.props);
2593
- }
2594
- componentDidMount() {
2595
- this.getData();
2596
- }
2597
- UNSAFE_componentWillReceiveProps(nextProps) {
2598
- if (!___default["default"].isEqual(this.props.jobs, nextProps.jobs)) this.updateProps(nextProps);
2599
- }
2600
- updateProps(props) {
2601
- this.setState({
2602
- allList: props.jobs
2603
- }, this.setData);
2604
- }
2605
2801
  renderLeftBar() {
2606
2802
  const sectionItems = [];
2607
2803
  if (this.canAddNew()) {
@@ -2671,9 +2867,7 @@ class RequestsHub extends React.Component {
2671
2867
  } else if (this.state.selectedSection === 'config') {
2672
2868
  return /*#__PURE__*/React__default["default"].createElement(Configuration$1, null);
2673
2869
  }
2674
- return /*#__PURE__*/React__default["default"].createElement(JobList$1, {
2675
- source: this.state.allList
2676
- });
2870
+ return /*#__PURE__*/React__default["default"].createElement(JobList$1, null);
2677
2871
  }
2678
2872
  render() {
2679
2873
  return /*#__PURE__*/React__default["default"].createElement("div", {
@@ -2685,42 +2879,16 @@ class RequestsHub extends React.Component {
2685
2879
  }, this.renderRight())));
2686
2880
  }
2687
2881
  }
2688
- const styles$5 = {
2689
- sideBarTitleSection: {
2690
- lineHeight: '40px',
2691
- marginTop: 30,
2692
- marginBottom: 30,
2693
- paddingLeft: 24,
2694
- paddingRight: 24
2695
- },
2696
- sideBarSection: {
2697
- weight: '100%',
2698
- minWidth: 200,
2699
- padding: 32,
2700
- paddingLeft: 24,
2701
- cursor: 'pointer',
2702
- display: 'flex',
2703
- flexDirection: 'column',
2704
- justifyContent: 'center'
2705
- },
2706
- spinner: {
2707
- fontSize: 32,
2708
- color: FeatureConfig.env.colourBrandingOff
2709
- }
2710
- };
2711
2882
  const mapStateToProps$4 = state => {
2712
2883
  const {
2713
2884
  auth
2714
2885
  } = state;
2715
2886
  return {
2716
- jobs: state[values.reducerKey].jobs,
2717
2887
  auth,
2718
2888
  strings: state.strings && state.strings.config || {}
2719
2889
  };
2720
2890
  };
2721
- var RequestsHub$1 = reactRedux.connect(mapStateToProps$4, {
2722
- jobsLoaded
2723
- })(reactRouter.withRouter(RequestsHub));
2891
+ var RequestsHub$1 = reactRedux.connect(mapStateToProps$4)(reactRouter.withRouter(RequestsHub));
2724
2892
 
2725
2893
  function ownKeys$4(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
2726
2894
  function _objectSpread$4(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys$4(Object(t), !0).forEach(function (r) { _defineProperty__default["default"](e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys$4(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
@@ -2737,7 +2905,7 @@ class Job extends React.Component {
2737
2905
  super(props);
2738
2906
  _defineProperty__default["default"](this, "getJob", async () => {
2739
2907
  try {
2740
- const res = await maintenanceActions.getJob(this.props.auth.site, this.state.jobId);
2908
+ const res = await maintenanceActions$1.getJob(this.props.auth.site, this.state.jobId);
2741
2909
  this.setState({
2742
2910
  updating: false
2743
2911
  });
@@ -2749,7 +2917,7 @@ class Job extends React.Component {
2749
2917
  });
2750
2918
  _defineProperty__default["default"](this, "getAssignees", async () => {
2751
2919
  try {
2752
- const res = await maintenanceActions.getAssignees(this.props.auth.site);
2920
+ const res = await maintenanceActions$1.getAssignees(this.props.auth.site);
2753
2921
  this.setState({
2754
2922
  assignees: res.data.Users
2755
2923
  });
@@ -2762,7 +2930,7 @@ class Job extends React.Component {
2762
2930
  this.setState({
2763
2931
  loadingExternalSync: true
2764
2932
  });
2765
- const res = await maintenanceActions.getExternalSync(this.state.jobId);
2933
+ const res = await maintenanceActions$1.getExternalSync(this.state.jobId);
2766
2934
  this.setState({
2767
2935
  externalSync: res.data,
2768
2936
  loadingExternalSync: false
@@ -2787,7 +2955,7 @@ class Job extends React.Component {
2787
2955
  retrySyncError: null
2788
2956
  });
2789
2957
  try {
2790
- await maintenanceActions.retrySync(job.id);
2958
+ await maintenanceActions$1.retrySync(job.id);
2791
2959
  // Refresh job data to get updated history
2792
2960
  await this.getJob();
2793
2961
  this.setState({
@@ -2953,7 +3121,7 @@ class Job extends React.Component {
2953
3121
  // Method to handle user assignment
2954
3122
  _defineProperty__default["default"](this, "onAssignUser", async userId => {
2955
3123
  try {
2956
- const res = await maintenanceActions.assignJob(this.state.jobId, userId);
3124
+ const res = await maintenanceActions$1.assignJob(this.state.jobId, userId);
2957
3125
  this.getJob();
2958
3126
  } catch (err) {
2959
3127
  console.error("onAssignUser", err);
@@ -2965,12 +3133,12 @@ class Job extends React.Component {
2965
3133
  this.setState({
2966
3134
  submittingNote: true
2967
3135
  });
2968
- const res = await (this.state.editingNote ? maintenanceActions.editNote(this.state.jobId, this.state.editingNote, this.state.noteInput, this.state.noteAttachments.map(a => {
3136
+ const res = await (this.state.editingNote ? maintenanceActions$1.editNote(this.state.jobId, this.state.editingNote, this.state.noteInput, this.state.noteAttachments.map(a => {
2969
3137
  return {
2970
3138
  Title: a.Title,
2971
3139
  Source: a.Source
2972
3140
  };
2973
- }), this.state.noteImages) : maintenanceActions.addNote(this.state.jobId, this.state.noteInput, this.state.noteAttachments.map(a => {
3141
+ }), this.state.noteImages) : maintenanceActions$1.addNote(this.state.jobId, this.state.noteInput, this.state.noteAttachments.map(a => {
2974
3142
  return {
2975
3143
  Title: a.Title,
2976
3144
  Source: a.Source
@@ -2998,7 +3166,7 @@ class Job extends React.Component {
2998
3166
  });
2999
3167
  return;
3000
3168
  }
3001
- maintenanceActions.deleteNote(this.state.jobId, n.Id);
3169
+ maintenanceActions$1.deleteNote(this.state.jobId, n.Id);
3002
3170
  const newNotes = ___default["default"].filter(this.state.job.Notes, note => note.Id !== n.Id);
3003
3171
  const newJob = _objectSpread$4({}, this.state.job);
3004
3172
  newJob.Notes = newNotes;
@@ -3036,7 +3204,7 @@ class Job extends React.Component {
3036
3204
  seen: true,
3037
3205
  status: job.status || "Unassigned"
3038
3206
  };
3039
- await maintenanceActions.editJob(update, auth.site);
3207
+ await maintenanceActions$1.editJob(update, auth.site);
3040
3208
  } catch (error) {
3041
3209
  this.setState({
3042
3210
  updating: false
@@ -3090,7 +3258,7 @@ class Job extends React.Component {
3090
3258
  priorityChangerOpen: false
3091
3259
  });
3092
3260
  try {
3093
- const res = await maintenanceActions.editJobPriority(this.state.job.id, priority);
3261
+ const res = await maintenanceActions$1.editJobPriority(this.state.job.id, priority);
3094
3262
  const {
3095
3263
  job
3096
3264
  } = res.data;
@@ -3115,7 +3283,7 @@ class Job extends React.Component {
3115
3283
  statusChangerOpen: false
3116
3284
  });
3117
3285
  try {
3118
- const res = await maintenanceActions.editJobStatus(this.state.job.id, status);
3286
+ const res = await maintenanceActions$1.editJobStatus(this.state.job.id, status);
3119
3287
  const {
3120
3288
  job
3121
3289
  } = res.data;
@@ -3992,7 +4160,7 @@ class AddJob extends React.Component {
3992
4160
  _this = this;
3993
4161
  _defineProperty__default["default"](this, "getJob", async () => {
3994
4162
  try {
3995
- const res = await maintenanceActions.getJob(this.props.auth.site, this.state.jobId);
4163
+ const res = await maintenanceActions$1.getJob(this.props.auth.site, this.state.jobId);
3996
4164
  res.data.location = res.data.site;
3997
4165
  const {
3998
4166
  userID,
@@ -4026,7 +4194,7 @@ class AddJob extends React.Component {
4026
4194
  });
4027
4195
  _defineProperty__default["default"](this, "getJobTypes", async () => {
4028
4196
  try {
4029
- const res = await maintenanceActions.getJobTypes(this.props.auth.site);
4197
+ const res = await maintenanceActions$1.getJobTypes(this.props.auth.site);
4030
4198
  this.setState({
4031
4199
  types: res.data
4032
4200
  });
@@ -4286,7 +4454,7 @@ class AddJob extends React.Component {
4286
4454
  customFields: this.state.customFields
4287
4455
  };
4288
4456
  if (this.state.id != null) {
4289
- maintenanceActions.editJob(job, this.props.auth.site).then(res => {
4457
+ maintenanceActions$1.editJob(job, this.props.auth.site).then(res => {
4290
4458
  this.setState({
4291
4459
  success: true,
4292
4460
  updating: false
@@ -4300,12 +4468,12 @@ class AddJob extends React.Component {
4300
4468
  });
4301
4469
  } else {
4302
4470
  // Create New Job
4303
- maintenanceActions.createJob(job).then(res => {
4471
+ maintenanceActions$1.createJob(job).then(res => {
4304
4472
  this.setState({
4305
4473
  success: true,
4306
4474
  updating: false
4307
4475
  });
4308
- this.props.jobsUpdate(this.props.auth.site);
4476
+ // JobList fetches fresh data on mount — no action needed here
4309
4477
  }).catch(res => {
4310
4478
  this.setState({
4311
4479
  updating: false
@@ -4970,7 +5138,6 @@ const mapStateToProps$2 = state => {
4970
5138
  };
4971
5139
  };
4972
5140
  var AddJob$1 = reactRedux.connect(mapStateToProps$2, {
4973
- jobsUpdate,
4974
5141
  jobsLoaded,
4975
5142
  addRecentlyCreated: Actions.addRecentlyCreated
4976
5143
  })(reactRouter.withRouter(AddJob));
@@ -4992,7 +5159,7 @@ class AddJobType extends React.Component {
4992
5159
  super(props);
4993
5160
  _defineProperty__default["default"](this, "getJobType", async () => {
4994
5161
  try {
4995
- const res = await maintenanceActions.getJobType(this.props.auth.site, this.state.jobTypeId);
5162
+ const res = await maintenanceActions$1.getJobType(this.props.auth.site, this.state.jobTypeId);
4996
5163
  const {
4997
5164
  typeName,
4998
5165
  email,
@@ -5164,9 +5331,9 @@ class AddJobType extends React.Component {
5164
5331
  }, async () => {
5165
5332
  try {
5166
5333
  if (jobTypeId) {
5167
- await maintenanceActions.editJobType(site, jobTypeId, jobTypeName, jobTypeEmail, jobTypeDescription, jobTypeLevel, hasCustomFields, customFields);
5334
+ await maintenanceActions$1.editJobType(site, jobTypeId, jobTypeName, jobTypeEmail, jobTypeDescription, jobTypeLevel, hasCustomFields, customFields);
5168
5335
  } else {
5169
- await maintenanceActions.addJobType(site, jobTypeName, jobTypeEmail, jobTypeDescription, jobTypeLevel, hasCustomFields, customFields);
5336
+ await maintenanceActions$1.addJobType(site, jobTypeName, jobTypeEmail, jobTypeDescription, jobTypeLevel, hasCustomFields, customFields);
5170
5337
  }
5171
5338
  this.props.jobTypesUpdate(site);
5172
5339
  this.setState({