@plusscommunities/pluss-maintenance-app 6.0.16 → 6.0.18

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.
@@ -38,6 +38,8 @@ class RequestDetail extends Component {
38
38
  showMessages: false,
39
39
  assignees: [],
40
40
  selectedPDF: null,
41
+ externalSync: null,
42
+ loadingExternalSync: false,
41
43
  };
42
44
 
43
45
  this.scrollView = React.createRef();
@@ -51,6 +53,7 @@ class RequestDetail extends Component {
51
53
  this.getJob();
52
54
  this.updateJobState(this.props.job);
53
55
  this.getAssignees();
56
+ this.getExternalSync();
54
57
  }
55
58
 
56
59
  getJob = async () => {
@@ -60,6 +63,8 @@ class RequestDetail extends Component {
60
63
  // console.log('getJob', JSON.stringify(res.data, null, 2));
61
64
  this.props.jobAdded(res.data);
62
65
  this.updateJobState(res.data);
66
+ // Refresh external sync data when job is refreshed
67
+ this.getExternalSync();
63
68
  } catch (error) {
64
69
  console.log('getJob error', error.toString());
65
70
  // check for 403 or 404 error
@@ -85,6 +90,24 @@ class RequestDetail extends Component {
85
90
  }
86
91
  };
87
92
 
93
+ getExternalSync = async () => {
94
+ // Only fetch if user has maintenance tracking permission
95
+ if (!this.hasPermission()) return;
96
+ if (!this.props.job?.id) return;
97
+
98
+ try {
99
+ this.setState({ loadingExternalSync: true });
100
+ const res = await maintenanceActions.getExternalSync(this.props.job.id);
101
+ this.setState({ externalSync: res.data, loadingExternalSync: false });
102
+ } catch (error) {
103
+ // 404 is expected if no sync - don't show error
104
+ if (error.response?.status !== 404) {
105
+ console.log('getExternalSync error', error);
106
+ }
107
+ this.setState({ loadingExternalSync: false });
108
+ }
109
+ };
110
+
88
111
  updateJobState(defaultJob) {
89
112
  const job = _.find(this.props.jobs, j => j.id === this.props.job.id) || defaultJob;
90
113
  if (!job) {
@@ -542,6 +565,43 @@ class RequestDetail extends Component {
542
565
  return content;
543
566
  }
544
567
 
568
+ renderExternalSync() {
569
+ const { externalSync, loadingExternalSync } = this.state;
570
+
571
+ // Only show if user has permission and external sync data exists
572
+ if (!this.hasPermission()) return null;
573
+ if (!externalSync || loadingExternalSync) return null;
574
+
575
+ return (
576
+ <View style={[styles.externalSyncContainer, Helper.getShadowStyle()]}>
577
+ <View style={styles.externalSyncHeader}>
578
+ <Text style={styles.externalSyncTitle}>External Sync</Text>
579
+ <Icon name="sync" type="material" iconStyle={[styles.externalSyncIcon, { color: this.props.colourBrandingMain }]} />
580
+ </View>
581
+ <View style={styles.externalSyncContent}>
582
+ {externalSync.externalSystem && (
583
+ <View style={styles.externalSyncRow}>
584
+ <Text style={styles.externalSyncLabel}>System:</Text>
585
+ <Text style={styles.externalSyncValue}>{externalSync.externalSystem}</Text>
586
+ </View>
587
+ )}
588
+ {externalSync.externalId && (
589
+ <View style={styles.externalSyncRow}>
590
+ <Text style={styles.externalSyncLabel}>External ID:</Text>
591
+ <Text style={styles.externalSyncValue}>{externalSync.externalId}</Text>
592
+ </View>
593
+ )}
594
+ {externalSync.syncedAt && (
595
+ <View style={styles.externalSyncRow}>
596
+ <Text style={styles.externalSyncLabel}>Synced:</Text>
597
+ <Text style={styles.externalSyncValue}>{moment(externalSync.syncedAt).format('D MMM YYYY h:mma')}</Text>
598
+ </View>
599
+ )}
600
+ </View>
601
+ </View>
602
+ );
603
+ }
604
+
545
605
  renderCustomFields() {
546
606
  const { job } = this.state;
547
607
  const { customFields } = job;
@@ -702,6 +762,7 @@ class RequestDetail extends Component {
702
762
  <ScrollView ref={this.scrollView} contentContainerStyle={{ paddingBottom: 26 }} style={{ height: '100%' }}>
703
763
  <View style={styles.innerContainer}>
704
764
  {this.renderTop()}
765
+ {this.renderExternalSync()}
705
766
  {this.renderAssignee()}
706
767
  {this.rendeDetails()}
707
768
  {this.renderMessages()}
@@ -1028,6 +1089,48 @@ const styles = StyleSheet.create({
1028
1089
  fontSize: 16,
1029
1090
  color: '#65686D',
1030
1091
  },
1092
+ externalSyncContainer: {
1093
+ backgroundColor: '#fff',
1094
+ marginBottom: 16,
1095
+ borderRadius: 8,
1096
+ padding: 16,
1097
+ },
1098
+ externalSyncHeader: {
1099
+ flexDirection: 'row',
1100
+ justifyContent: 'space-between',
1101
+ alignItems: 'center',
1102
+ marginBottom: 12,
1103
+ paddingBottom: 12,
1104
+ borderBottomWidth: 1,
1105
+ borderBottomColor: Colours.LINEGREY,
1106
+ },
1107
+ externalSyncTitle: {
1108
+ fontFamily: 'sf-semibold',
1109
+ fontSize: 16,
1110
+ color: Colours.TEXT_DARKEST,
1111
+ },
1112
+ externalSyncIcon: {
1113
+ fontSize: 20,
1114
+ },
1115
+ externalSyncContent: {
1116
+ paddingTop: 4,
1117
+ },
1118
+ externalSyncRow: {
1119
+ flexDirection: 'row',
1120
+ marginBottom: 10,
1121
+ },
1122
+ externalSyncLabel: {
1123
+ fontFamily: 'sf-semibold',
1124
+ fontSize: 14,
1125
+ color: Colours.TEXT_DARK,
1126
+ width: 100,
1127
+ },
1128
+ externalSyncValue: {
1129
+ fontFamily: 'sf-regular',
1130
+ fontSize: 14,
1131
+ color: Colours.TEXT_DARKEST,
1132
+ flex: 1,
1133
+ },
1031
1134
  });
1032
1135
 
1033
1136
  const mapStateToProps = state => {
@@ -173,16 +173,25 @@ class MaintenanceRequest extends Component {
173
173
  if (userDetails.unit) {
174
174
  update.roomNumber = userDetails.unit;
175
175
  }
176
+ if (!this.state.customFields || !_.some(this.state.customFields, 'isTitle')) {
177
+ update.title = userDetails.displayName || null;
178
+ }
176
179
  }
177
180
  } catch (error) {
178
181
  // Permission denied (403) or other error - continue without auto-population
179
182
  // User can still manually enter contact details
180
183
  console.log('Could not fetch user details for auto-population:', error);
181
- }
184
+ } finally {
185
+ // In any case, we still need a title. Inform the user to set one form field as a title.
186
+ if (!state.title && !update.title) {
187
+ update.title = "[Missing title - Set one of the form field as title in the community manager]"
188
+ }
182
189
 
190
+ }
183
191
  this.setState(update);
184
192
  };
185
193
 
194
+
186
195
  onChangeAnswer = (fieldId, answer) => {
187
196
  const update = { customFields: _.cloneDeep(this.state.customFields) };
188
197
  const field = update.customFields[fieldId];