@plusscommunities/pluss-maintenance-app-forms 7.0.21 → 8.0.1-auth.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/module/apis/maintenanceActions.js +12 -4
  2. package/dist/module/apis/maintenanceActions.js.map +1 -1
  3. package/dist/module/components/FilterPopupMenu.js +30 -14
  4. package/dist/module/components/FilterPopupMenu.js.map +1 -1
  5. package/dist/module/components/MaintenanceList.js +199 -25
  6. package/dist/module/components/MaintenanceList.js.map +1 -1
  7. package/dist/module/components/MaintenanceListItem.js +2 -1
  8. package/dist/module/components/MaintenanceListItem.js.map +1 -1
  9. package/dist/module/components/MaintenanceWidgetItem.js +2 -1
  10. package/dist/module/components/MaintenanceWidgetItem.js.map +1 -1
  11. package/dist/module/components/PrioritySelectorPopup.js +2 -1
  12. package/dist/module/components/PrioritySelectorPopup.js.map +1 -1
  13. package/dist/module/components/StatusSelectorPopup.js +2 -1
  14. package/dist/module/components/StatusSelectorPopup.js.map +1 -1
  15. package/dist/module/components/WidgetSmall.js +5 -4
  16. package/dist/module/components/WidgetSmall.js.map +1 -1
  17. package/dist/module/core.config.js +1 -2
  18. package/dist/module/core.config.js.map +1 -1
  19. package/dist/module/screens/JobTypePicker.js +2 -1
  20. package/dist/module/screens/JobTypePicker.js.map +1 -1
  21. package/dist/module/screens/MaintenanceUserPicker.js +73 -91
  22. package/dist/module/screens/MaintenanceUserPicker.js.map +1 -1
  23. package/dist/module/screens/RequestDetail.js +76 -29
  24. package/dist/module/screens/RequestDetail.js.map +1 -1
  25. package/dist/module/screens/RequestNotes.js +11 -7
  26. package/dist/module/screens/RequestNotes.js.map +1 -1
  27. package/dist/module/screens/ServiceRequest.js +43 -42
  28. package/dist/module/screens/ServiceRequest.js.map +1 -1
  29. package/dist/module/values.config.a.js +1 -2
  30. package/dist/module/values.config.a.js.map +1 -1
  31. package/dist/module/values.config.default.js +1 -2
  32. package/dist/module/values.config.default.js.map +1 -1
  33. package/dist/module/values.config.enquiry.js +1 -2
  34. package/dist/module/values.config.enquiry.js.map +1 -1
  35. package/dist/module/values.config.feedback.js +1 -2
  36. package/dist/module/values.config.feedback.js.map +1 -1
  37. package/dist/module/values.config.food.js +1 -2
  38. package/dist/module/values.config.food.js.map +1 -1
  39. package/package.json +18 -14
  40. package/src/apis/maintenanceActions.js +17 -7
  41. package/src/components/FilterPopupMenu.js +67 -40
  42. package/src/components/MaintenanceList.js +234 -40
  43. package/src/components/MaintenanceListItem.js +2 -1
  44. package/src/components/MaintenanceWidgetItem.js +2 -1
  45. package/src/components/PrioritySelectorPopup.js +2 -1
  46. package/src/components/StatusSelectorPopup.js +2 -1
  47. package/src/components/WidgetSmall.js +12 -4
  48. package/src/core.config.js +0 -2
  49. package/src/screens/JobTypePicker.js +2 -1
  50. package/src/screens/MaintenanceUserPicker.js +77 -114
  51. package/src/screens/RequestDetail.js +88 -32
  52. package/src/screens/RequestNotes.js +17 -10
  53. package/src/screens/ServiceRequest.js +85 -44
  54. package/src/values.config.a.js +0 -1
  55. package/src/values.config.default.js +0 -1
  56. package/src/values.config.enquiry.js +0 -1
  57. package/src/values.config.feedback.js +0 -1
  58. package/src/values.config.food.js +0 -1
@@ -1,10 +1,11 @@
1
1
  import React, { Component } from "react";
2
+ import { Text } from "@plusscommunities/pluss-core-app/components";
2
3
  import {
3
4
  View,
4
5
  StyleSheet,
5
6
  FlatList,
6
7
  TouchableOpacity,
7
- Text,
8
+ Animated,
8
9
  } from "react-native";
9
10
  import _ from "lodash";
10
11
  import { connect } from "react-redux";
@@ -23,6 +24,11 @@ import { Components, Colours, Helper } from "../core.config";
23
24
  import { values } from "../values.config";
24
25
 
25
26
  class MaintenanceList extends Component {
27
+ _refreshCounter = 0;
28
+ _filterApplying = false;
29
+ _filterDataReady = false;
30
+ _overlayOpacity = new Animated.Value(0);
31
+
26
32
  constructor(props) {
27
33
  super(props);
28
34
 
@@ -30,6 +36,9 @@ class MaintenanceList extends Component {
30
36
  types: [],
31
37
  filteredList: props.jobs,
32
38
  loading: false,
39
+ loadingMore: false,
40
+ hasMore: true,
41
+ lastKey: null,
33
42
  searchText: "",
34
43
  showFilterPopup: false,
35
44
  };
@@ -52,30 +61,91 @@ class MaintenanceList extends Component {
52
61
  this.resetDataSource();
53
62
  }
54
63
 
55
- refresh = () => {
64
+ expandStatus = (status) => {
65
+ if (status !== "Incomplete") return status;
66
+ const { statusTypes } = this.props;
67
+ if (!statusTypes || !statusTypes.length) return status;
68
+ const incompleteStatuses = statusTypes
69
+ .filter((s) => s.category !== "Completed")
70
+ .map((s) => s.text);
71
+ return incompleteStatuses.length > 0
72
+ ? incompleteStatuses.join(",")
73
+ : status;
74
+ };
75
+
76
+ refresh = (overrideFilters) => {
77
+ const currentCounter = ++this._refreshCounter;
56
78
  this.onLoadingChanged(true, async () => {
57
79
  try {
58
- const { jobfilters } = this.props;
59
- const res = await maintenanceActions.getJobsRecursive(
80
+ const jobfilters = overrideFilters || this.props.jobfilters;
81
+ const res = await maintenanceActions.getJobs2(
60
82
  this.props.site,
61
- jobfilters.status,
83
+ this.expandStatus(jobfilters.status),
62
84
  jobfilters.priority,
63
85
  jobfilters.type,
86
+ null, // No lastKey = first page
87
+ jobfilters.assignee,
64
88
  );
65
- // console.log('refresh', res ? JSON.stringify(res[0], null, 2) : null);
66
- if (
67
- !_.isEmpty(jobfilters.status) ||
68
- !_.isEmpty(jobfilters.priority) ||
69
- !_.isEmpty(jobfilters.type)
70
- ) {
71
- this.props.jobsAdded(res);
72
- } else {
73
- this.props.jobsLoaded(res);
74
- }
89
+
90
+ // Discard stale responses from earlier filter changes
91
+ if (currentCounter !== this._refreshCounter) return;
92
+
93
+ const jobs = res.data.Items;
94
+ const lastKey = res.data.LastKey;
95
+
96
+ // Signal that the API response is ready before dispatching to Redux
97
+ this._filterDataReady = true;
98
+
99
+ // Always replace jobs on refresh (filters are server-side)
100
+ this.props.jobsLoaded(jobs);
101
+
102
+ this.setState({
103
+ lastKey,
104
+ hasMore: !!lastKey,
105
+ });
75
106
  } catch (error) {
107
+ if (currentCounter !== this._refreshCounter) return;
76
108
  console.log("refresh error", error);
77
109
  } finally {
78
- this.onLoadingChanged(false);
110
+ if (currentCounter === this._refreshCounter) {
111
+ this.onLoadingChanged(false);
112
+ }
113
+ }
114
+ });
115
+ };
116
+
117
+ loadMore = () => {
118
+ const { loading, loadingMore, hasMore, lastKey, searchText } = this.state;
119
+
120
+ // Don't load if already loading, no more pages, or during search
121
+ if (loading || loadingMore || !hasMore || !lastKey || searchText) return;
122
+
123
+ this.setState({ loadingMore: true }, async () => {
124
+ try {
125
+ const { jobfilters } = this.props;
126
+ const res = await maintenanceActions.getJobs2(
127
+ this.props.site,
128
+ this.expandStatus(jobfilters.status),
129
+ jobfilters.priority,
130
+ jobfilters.type,
131
+ lastKey,
132
+ jobfilters.assignee,
133
+ );
134
+
135
+ const newJobs = res.data.Items;
136
+ const newLastKey = res.data.LastKey;
137
+
138
+ // Append to existing jobs
139
+ this.props.jobsAdded(newJobs);
140
+
141
+ this.setState({
142
+ lastKey: newLastKey,
143
+ hasMore: !!newLastKey,
144
+ });
145
+ } catch (error) {
146
+ console.log("loadMore error", error);
147
+ } finally {
148
+ this.setState({ loadingMore: false });
79
149
  }
80
150
  });
81
151
  };
@@ -121,12 +191,13 @@ class MaintenanceList extends Component {
121
191
  }
122
192
 
123
193
  resetDataSource = (source = "") => {
124
- const { jobfilters } = this.props;
125
194
  const { searchText } = this.state;
126
195
  const { jobs } = this.props;
127
196
 
128
197
  let filteredList = jobs;
129
198
  let jobIdMatch = null;
199
+
200
+ // Client-side search filtering (search is not supported server-side)
130
201
  if (searchText) {
131
202
  jobIdMatch = _.find(jobs, (j) => j.jobId === searchText);
132
203
  filteredList = jobs.filter((j) => {
@@ -140,22 +211,10 @@ class MaintenanceList extends Component {
140
211
  });
141
212
  if (!jobIdMatch) this.fetchJob(searchText);
142
213
  }
143
- if (jobfilters.status)
144
- filteredList = filteredList.filter((j) =>
145
- jobfilters.status.includes(j.status),
146
- );
147
- if (jobfilters.priority)
148
- filteredList = filteredList.filter((j) =>
149
- jobfilters.priority.includes(j.priority),
150
- );
151
- if (jobfilters.type)
152
- filteredList = filteredList.filter((j) =>
153
- jobfilters.type.includes(j.type),
154
- );
155
- if (jobfilters.assignee)
156
- filteredList = filteredList.filter((j) =>
157
- jobfilters.assignee.includes(j.AssigneeId),
158
- );
214
+
215
+ // Note: status, priority, type, and assignee filters are applied server-side
216
+ // via getJobs2, so no client-side filtering needed for those
217
+
159
218
  if (jobIdMatch) {
160
219
  const jobIndex = filteredList.indexOf(jobIdMatch);
161
220
  if (jobIndex > -1) {
@@ -163,9 +222,25 @@ class MaintenanceList extends Component {
163
222
  }
164
223
  filteredList.unshift(jobIdMatch);
165
224
  }
166
- if (source !== "search") this.refresh();
167
225
 
168
- this.setState({ filteredList });
226
+ // During a filter transition, don't clear the list until the API
227
+ // response has arrived and been processed. This prevents the empty
228
+ // state from flashing when jobs transiently drop to [].
229
+ if (
230
+ this._filterApplying &&
231
+ !this._filterDataReady &&
232
+ filteredList.length === 0
233
+ ) {
234
+ return;
235
+ }
236
+
237
+ this.setState({ filteredList }, () => {
238
+ if (this._filterDataReady) {
239
+ this._filterApplying = false;
240
+ this._filterDataReady = false;
241
+ this._showOverlay(false);
242
+ }
243
+ });
169
244
  };
170
245
 
171
246
  onLoadingChanged = (loading, callback) => {
@@ -191,8 +266,16 @@ class MaintenanceList extends Component {
191
266
  };
192
267
 
193
268
  onSelectFilter = (selected) => {
269
+ this._filterApplying = true;
270
+ this._filterDataReady = false;
271
+ this._showOverlay(true);
194
272
  this.props.jobsFilterLoaded(selected);
195
273
  this.onToggleFilter();
274
+ // Reset pagination but keep old list visible while loading
275
+ // Pass filters directly to avoid stale props from async Redux update
276
+ this.setState({ lastKey: null, hasMore: true }, () => {
277
+ this.refresh(selected);
278
+ });
196
279
  };
197
280
 
198
281
  getFilterButtonText = () => {
@@ -216,12 +299,59 @@ class MaintenanceList extends Component {
216
299
  return `Filtering by ${filterTexts.join(", ")}`;
217
300
  };
218
301
 
302
+ hasFiltersActive() {
303
+ const { jobfilters } = this.props;
304
+ return (
305
+ !_.isEmpty(jobfilters.status) ||
306
+ !_.isEmpty(jobfilters.priority) ||
307
+ !_.isEmpty(jobfilters.type) ||
308
+ !_.isEmpty(jobfilters.assignee)
309
+ );
310
+ }
311
+
312
+ clearFilters = () => {
313
+ const emptyFilters = {
314
+ status: "",
315
+ statusText: "",
316
+ priority: "",
317
+ priorityText: "",
318
+ type: "",
319
+ assignee: "",
320
+ assigneeName: "",
321
+ };
322
+ this.onSelectFilter(emptyFilters);
323
+ };
324
+
219
325
  renderEmptyList() {
220
- return this.state.loading ? null : (
221
- <Components.EmptyStateMain
222
- title={this.getEmptyStateText()}
223
- style={{ marginHorizontal: 16 }}
224
- />
326
+ if (this.state.loading || this._filterApplying) return null;
327
+
328
+ const hasFilters = this.hasFiltersActive();
329
+
330
+ return (
331
+ <View style={{ marginHorizontal: 16, alignItems: "center" }}>
332
+ <Components.EmptyStateMain
333
+ title={
334
+ hasFilters
335
+ ? "No requests match your filters"
336
+ : this.getEmptyStateText()
337
+ }
338
+ />
339
+ {hasFilters && (
340
+ <TouchableOpacity
341
+ onPress={this.clearFilters}
342
+ style={styles.clearFiltersButton}
343
+ >
344
+ <Text
345
+ style={[
346
+ styles.clearFiltersText,
347
+ { color: this.props.colourBrandingMain },
348
+ ]}
349
+ >
350
+ Clear filters
351
+ </Text>
352
+ </TouchableOpacity>
353
+ )}
354
+ </View>
225
355
  );
226
356
  }
227
357
 
@@ -291,10 +421,46 @@ class MaintenanceList extends Component {
291
421
  )}
292
422
  ListEmptyComponent={this.renderEmptyList()}
293
423
  ListHeaderComponent={this.renderListHeader()}
424
+ ListFooterComponent={this.renderListFooter()}
425
+ onEndReached={this.loadMore}
426
+ onEndReachedThreshold={0.5}
294
427
  />
295
428
  );
296
429
  }
297
430
 
431
+ renderListFooter = () => {
432
+ if (!this.state.loadingMore) return null;
433
+
434
+ return (
435
+ <View style={styles.loadingMore}>
436
+ <Components.LoadingIndicator visible={true} />
437
+ </View>
438
+ );
439
+ };
440
+
441
+ _showOverlay = (visible) => {
442
+ Animated.timing(this._overlayOpacity, {
443
+ toValue: visible ? 1 : 0,
444
+ duration: 200,
445
+ useNativeDriver: true,
446
+ }).start();
447
+ };
448
+
449
+ renderFilterOverlay() {
450
+ if (!this._filterApplying) return null;
451
+
452
+ return (
453
+ <Animated.View
454
+ style={[styles.filterOverlay, { opacity: this._overlayOpacity }]}
455
+ pointerEvents="none"
456
+ >
457
+ <View style={styles.filterOverlayInner}>
458
+ <Components.LoadingCircles />
459
+ </View>
460
+ </Animated.View>
461
+ );
462
+ }
463
+
298
464
  renderFilterPopup() {
299
465
  const { jobfilters } = this.props;
300
466
  const { showFilterPopup, types } = this.state;
@@ -317,6 +483,7 @@ class MaintenanceList extends Component {
317
483
  return (
318
484
  <View style={[styles.container, this.props.style]}>
319
485
  {this.renderList()}
486
+ {this.renderFilterOverlay()}
320
487
  {this.renderFilterPopup()}
321
488
  </View>
322
489
  );
@@ -368,6 +535,33 @@ const styles = StyleSheet.create({
368
535
  fontFamily: "sf-semibold",
369
536
  fontSize: 16,
370
537
  },
538
+ loadingMore: {
539
+ paddingVertical: 20,
540
+ alignItems: "center",
541
+ },
542
+ clearFiltersButton: {
543
+ marginTop: 12,
544
+ paddingVertical: 10,
545
+ paddingHorizontal: 24,
546
+ borderRadius: 8,
547
+ borderWidth: 1,
548
+ borderColor: "#ccc",
549
+ backgroundColor: "#f9f9f9",
550
+ },
551
+ clearFiltersText: {
552
+ fontFamily: "sf-semibold",
553
+ fontSize: 15,
554
+ },
555
+ filterOverlay: {
556
+ ...StyleSheet.absoluteFillObject,
557
+ justifyContent: "center",
558
+ alignItems: "center",
559
+ backgroundColor: "rgba(255, 255, 255, 0.7)",
560
+ },
561
+ filterOverlayInner: {
562
+ padding: 20,
563
+ borderRadius: 12,
564
+ },
371
565
  });
372
566
 
373
567
  const mapStateToProps = (state) => {
@@ -1,5 +1,6 @@
1
1
  import React, { Component } from "react";
2
- import { Image, Text, TouchableOpacity, View, StyleSheet } from "react-native";
2
+ import { Image, TouchableOpacity, View, StyleSheet } from "react-native";
3
+ import { Text } from "@plusscommunities/pluss-core-app/components";
3
4
  import _ from "lodash";
4
5
  import { Icon } from "@rneui/themed";
5
6
  import { connect } from "react-redux";
@@ -1,5 +1,6 @@
1
1
  import React, { Component } from "react";
2
- import { Text, View, StyleSheet, TouchableOpacity } from "react-native";
2
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
3
+ import { Text } from "@plusscommunities/pluss-core-app/components";
3
4
  import { connect } from "react-redux";
4
5
  import { Icon } from "@rneui/themed";
5
6
  import moment from "moment";
@@ -1,5 +1,6 @@
1
1
  import React, { PureComponent } from "react";
2
- import { View, StyleSheet, TouchableOpacity, Text } from "react-native";
2
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
3
+ import { Text } from "@plusscommunities/pluss-core-app/components";
3
4
  import { connect } from "react-redux";
4
5
  import { jobPriorityOptions } from "../helper";
5
6
  import { Components, Colours } from "../core.config";
@@ -1,5 +1,6 @@
1
1
  import React, { PureComponent } from "react";
2
- import { View, StyleSheet, TouchableOpacity, Text } from "react-native";
2
+ import { View, StyleSheet, TouchableOpacity } from "react-native";
3
+ import { Text } from "@plusscommunities/pluss-core-app/components";
3
4
  import { connect } from "react-redux";
4
5
  import { getJobStatusOptions } from "../helper";
5
6
  import { Components, Colours } from "../core.config";
@@ -1,5 +1,6 @@
1
1
  import React, { Component } from "react";
2
- import { Text, View, ScrollView, StyleSheet } from "react-native";
2
+ import { View, ScrollView, StyleSheet } from "react-native";
3
+ import { Text } from "@plusscommunities/pluss-core-app/components";
3
4
  import { connect } from "react-redux";
4
5
  import _ from "lodash";
5
6
  import { maintenanceActions } from "../apis";
@@ -47,9 +48,16 @@ class WidgetSmall extends Component {
47
48
  refresh = () => {
48
49
  this.onLoadingChanged(true, async () => {
49
50
  try {
50
- const res = await maintenanceActions.getJobsRecursive(this.props.site);
51
- // console.log('WidgetSmall - refresh', res.data);
52
- this.props.jobsLoaded(res);
51
+ const res = await maintenanceActions.getJobs2(
52
+ this.props.site,
53
+ "",
54
+ "",
55
+ "",
56
+ null,
57
+ "",
58
+ );
59
+ // console.log('WidgetSmall - refresh', res.data.Items);
60
+ this.props.jobsLoaded(res.data.Items);
53
61
  } catch (error) {
54
62
  console.log("refresh error", error);
55
63
  } finally {
@@ -3,7 +3,6 @@ import * as PlussCore from "@plusscommunities/pluss-core-app";
3
3
 
4
4
  const {
5
5
  Apis,
6
- Fonts,
7
6
  Actions,
8
7
  ActionTypes,
9
8
  Config,
@@ -16,7 +15,6 @@ const {
16
15
  } = PlussCore;
17
16
  export {
18
17
  Apis,
19
- Fonts,
20
18
  Actions,
21
19
  ActionTypes,
22
20
  Config,
@@ -1,6 +1,7 @@
1
1
  import React, { Component } from "react";
2
2
  import _ from "lodash";
3
- import { TouchableOpacity, View, ScrollView, Text } from "react-native";
3
+ import { TouchableOpacity, View, ScrollView } from "react-native";
4
+ import { Text } from "@plusscommunities/pluss-core-app/components";
4
5
  import { connect } from "react-redux";
5
6
  import { Icon } from "@rneui/themed";
6
7
  import { Services } from "../feature.config";