@plusscommunities/pluss-maintenance-web-a 1.1.30 → 1.1.31

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.
@@ -35,6 +35,9 @@ class Job extends Component {
35
35
  assignees: [],
36
36
  externalSync: null,
37
37
  loadingExternalSync: false,
38
+ retryingSync: false,
39
+ retrySyncError: null,
40
+ retrySyncInitiated: false,
38
41
  };
39
42
  }
40
43
 
@@ -86,6 +89,25 @@ class Job extends Component {
86
89
  }
87
90
  };
88
91
 
92
+ onRetrySync = async () => {
93
+ const { job } = this.state;
94
+ if (!job || this.state.retryingSync) return;
95
+
96
+ this.setState({ retryingSync: true, retrySyncError: null });
97
+
98
+ try {
99
+ await maintenanceActions.retrySync(job.id);
100
+ // Refresh job data to get updated history
101
+ await this.getJob();
102
+ this.setState({ retryingSync: false, retrySyncInitiated: true });
103
+ } catch (error) {
104
+ console.error('onRetrySync', error);
105
+ const errorMessage =
106
+ (error && error.response && error.response.data && error.response.data.error) || 'Failed to retry sync. Please try again.';
107
+ this.setState({ retryingSync: false, retrySyncError: errorMessage });
108
+ }
109
+ };
110
+
89
111
  getStatusType = (status) => {
90
112
  const { statusTypes } = this.props;
91
113
  let statusType = statusTypes.find((s) => s.text === status);
@@ -852,34 +874,113 @@ class Job extends Component {
852
874
  );
853
875
  }
854
876
 
877
+ hasSyncFailureWithoutSuccess() {
878
+ const { job } = this.state;
879
+ if (!job || !job.history) return false;
880
+
881
+ const history = job.history || [];
882
+ const hasSuccess = history.some((entry) => entry.EntryType === 'ExternalIDSet');
883
+ const hasFailure = history.some((entry) => entry.EntryType === 'ExternalIDSetFailed');
884
+
885
+ return hasFailure && !hasSuccess;
886
+ }
887
+
888
+ renderRetrySyncButton() {
889
+ const { auth } = this.props;
890
+ const { retryingSync, retrySyncInitiated } = this.state;
891
+
892
+ // Only show for users with maintenance tracking permission
893
+ if (!Session.validateAccess(auth.site, values.permissionMaintenanceTracking, auth)) return null;
894
+
895
+ // Only show if there's a failure without success and retry hasn't been initiated
896
+ if (!this.hasSyncFailureWithoutSuccess() || retrySyncInitiated) return null;
897
+
898
+ // Show spinner while retrying
899
+ if (retryingSync) {
900
+ return <FontAwesome style={{ fontSize: 20, color: Colours.COLOUR_DUSK_LIGHT, marginLeft: 8 }} name="spinner fa-pulse fa-fw" />;
901
+ }
902
+
903
+ return (
904
+ <div className="statusLabel pointer" onClick={this.onRetrySync} style={{ backgroundColor: Colours.COLOUR_RED, marginLeft: 8 }}>
905
+ <span className="statusLabel_text">Retry Sync</span>
906
+ </div>
907
+ );
908
+ }
909
+
910
+ renderExternalSyncStatus() {
911
+ const { retrySyncError, retrySyncInitiated } = this.state;
912
+
913
+ // Show error message if retry failed
914
+ if (retrySyncError) {
915
+ return (
916
+ <Components.Text type="body">
917
+ <FontAwesome className="userStatusIcon" name="times-circle" style={{ color: Colours.COLOUR_RED }} /> {retrySyncError}
918
+ </Components.Text>
919
+ );
920
+ }
921
+
922
+ // Show success message if retry was initiated
923
+ if (retrySyncInitiated) {
924
+ return (
925
+ <Components.Text type="body">
926
+ <FontAwesome className="userStatusIcon" name="check-circle" style={{ color: Colours.COLOUR_GREEN }} /> Sync retry initiated. Check
927
+ back shortly for results.
928
+ </Components.Text>
929
+ );
930
+ }
931
+
932
+ // Show failure message with instruction
933
+ if (this.hasSyncFailureWithoutSuccess()) {
934
+ return (
935
+ <Components.Text type="body">
936
+ <FontAwesome className="userStatusIcon" name="times-circle" style={{ color: Colours.COLOUR_RED }} /> External sync failed. Use the
937
+ retry button to attempt again.
938
+ </Components.Text>
939
+ );
940
+ }
941
+
942
+ return null;
943
+ }
944
+
855
945
  renderExternalSync() {
856
946
  const { externalSync, loadingExternalSync } = this.state;
857
947
 
858
- // Only show if external sync data exists
859
- if (!externalSync || loadingExternalSync) return null;
948
+ // Check if we should show this section at all
949
+ const hasExternalSyncData = externalSync && !loadingExternalSync;
950
+ const hasSyncFailure = this.hasSyncFailureWithoutSuccess();
951
+
952
+ // Show section if we have sync data OR if there's a failure that can be retried
953
+ if (!hasExternalSyncData && !hasSyncFailure) return null;
860
954
 
861
955
  return (
862
956
  <div className="padding-32 paddingVertical-40 bottomDivideBorder relative">
863
957
  <div className="newTopBar clearfix flex flex-reverse">
958
+ {this.renderRetrySyncButton()}
864
959
  <Components.Text type="formTitleSmall" className="flex-1">
865
960
  External Sync
866
961
  </Components.Text>
867
962
  </div>
868
963
  <div className="marginTop-16">
869
- {externalSync.systemType && (
870
- <Components.Text type="body" className="marginBottom-8">
871
- <strong>System:</strong> {externalSync.systemType}
872
- </Components.Text>
873
- )}
874
- {externalSync.externalId && (
875
- <Components.Text type="body" className="marginBottom-8">
876
- <strong>External ID:</strong> {externalSync.externalId}
877
- </Components.Text>
878
- )}
879
- {externalSync.syncedAt && (
880
- <Components.Text type="body" className="marginBottom-8">
881
- <strong>Synced:</strong> {moment.utc(externalSync.syncedAt).local().format('D MMM YYYY h:mma')}
882
- </Components.Text>
964
+ {hasExternalSyncData ? (
965
+ <>
966
+ {externalSync.systemType && (
967
+ <Components.Text type="body" className="marginBottom-8">
968
+ <strong>System:</strong> {externalSync.systemType}
969
+ </Components.Text>
970
+ )}
971
+ {externalSync.externalId && (
972
+ <Components.Text type="body" className="marginBottom-8">
973
+ <strong>External ID:</strong> {externalSync.externalId}
974
+ </Components.Text>
975
+ )}
976
+ {externalSync.syncedAt && (
977
+ <Components.Text type="body" className="marginBottom-8">
978
+ <strong>Synced:</strong> {moment.utc(externalSync.syncedAt).local().format('D MMM YYYY h:mma')}
979
+ </Components.Text>
980
+ )}
981
+ </>
982
+ ) : (
983
+ this.renderExternalSyncStatus()
883
984
  )}
884
985
  </div>
885
986
  </div>