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