Passed
Push — feature/add-2fa-support ( 23e818...85b1f3 )
by Chris
24:19 queued 11:16
created

resources/assets/js/components/AssessmentPlan/AssessmentPlanAlert.tsx   A

Complexity

Total Complexity 6
Complexity/F 0

Size

Lines of Code 296
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 234
dl 0
loc 296
rs 10
c 0
b 0
f 0
wmc 6
mnd 6
bc 6
fnc 0
bpm 0
cpm 0
noi 0
1
import React from "react";
2
import { connect } from "react-redux";
3
import some from "lodash/some";
4
import {
5
  FormattedMessage,
6
  WrappedComponentProps,
7
  injectIntl,
8
} from "react-intl";
9
import { AssessmentPlanNotification, Skill } from "../../models/types";
10
import {
11
  notificationIsUpdating,
12
  notificationsAreFetching,
13
} from "../../store/AssessmentPlanNotification/assessmentPlanNotificationSelectors";
14
import { RootState } from "../../store/store";
15
import { DispatchType } from "../../configureStore";
16
import { updateAssessmentPlanNotification } from "../../store/AssessmentPlanNotification/assessmentPlanNotificationActions";
17
import { getSkills } from "../../store/Skill/skillSelector";
18
import { mapToObject, getId, hasKey } from "../../helpers/queries";
19
20
interface AssessmentPlanAlertProps {
21
  notifications: AssessmentPlanNotification[];
22
  skills: Skill[];
23
  isFetching: boolean;
24
  isUpdating: boolean;
25
  handleDismiss: () => void;
26
}
27
28
export const AssessmentPlanAlert: React.FunctionComponent<
29
  AssessmentPlanAlertProps & WrappedComponentProps
30
> = ({
31
  notifications,
32
  skills,
33
  isFetching,
34
  isUpdating,
35
  handleDismiss,
36
  intl,
37
}): React.ReactElement | null => {
38
  if ((notifications.length === 0 && !isFetching) || isUpdating) {
39
    return null;
40
  }
41
  if (notifications.length === 0 && isFetching) {
42
    return (
43
      <div
44
        data-c-alert="error"
45
        data-c-radius="rounded"
46
        data-c-padding="half"
47
        role="alert"
48
        data-c-margin="top(double)"
49
        data-c-grid="middle"
50
      >
51
        <p
52
          data-c-margin="bottom(quarter)"
53
          data-c-heading="h5"
54
          data-c-font-weight="bold"
55
        >
56
          <i aria-hidden="true" className="fa fa-spinner fa-spin" />
57
          <FormattedMessage
58
            id="assessmentPlan.alert.checking"
59
            defaultMessage="Checking if Job changed recently..."
60
            description="Alert title while notifications are still being loaded."
61
          />
62
        </p>
63
      </div>
64
    );
65
  }
66
  const skillsById = mapToObject(skills, getId);
67
  const skillName = (skillId: number): string =>
68
    hasKey(skillsById, skillId)
69
      ? skillsById[skillId][intl.locale].name
70
      : "UNKNOWN SKILL";
71
  const createNotifications = notifications.filter(
72
    (notification): boolean => notification.type === "CREATE",
73
  );
74
  const skillHasChanged = (
75
    notification: AssessmentPlanNotification,
76
  ): boolean => {
77
    return (
78
      notification.skill_id_new !== null &&
79
      notification.skill_id !== notification.skill_id_new
80
    );
81
  };
82
  const levelHasChanged = (
83
    notification: AssessmentPlanNotification,
84
  ): boolean => {
85
    return (
86
      (notification.skill_level_id_new !== null &&
87
        notification.skill_level_id !== notification.skill_level_id_new) ||
88
      (notification.criteria_type_id_new !== null &&
89
        notification.criteria_type_id !== notification.criteria_type_id_new)
90
    );
91
  };
92
93
  const updateSkillNotifications = notifications.filter(
94
    (notification): boolean =>
95
      notification.type === "UPDATE" &&
96
      skillHasChanged(notification) &&
97
      !levelHasChanged(notification),
98
  );
99
  const updateLevelNotifications = notifications.filter(
100
    (notification): boolean =>
101
      notification.type === "UPDATE" &&
102
      !skillHasChanged(notification) &&
103
      levelHasChanged(notification),
104
  );
105
  const updateBothNotifications = notifications.filter(
106
    (notification): boolean =>
107
      notification.type === "UPDATE" &&
108
      skillHasChanged(notification) &&
109
      levelHasChanged(notification),
110
  );
111
  const deleteNotifications = notifications.filter(
112
    (notification): boolean => notification.type === "DELETE",
113
  );
114
  return (
115
    <div
116
      data-c-alert="error"
117
      data-c-radius="rounded"
118
      data-c-padding="half"
119
      role="alert"
120
      data-c-margin="top(double)"
121
      data-c-grid="middle"
122
    >
123
      <div data-c-grid-item="base(2of3) tl(4of5)">
124
        <p
125
          data-c-margin="bottom(quarter)"
126
          data-c-heading="h5"
127
          data-c-font-weight="bold"
128
        >
129
          <i aria-hidden="true" className="fa fa-exclamation-circle" />
130
          <FormattedMessage
131
            id="assessmentPlan.alert.title"
132
            defaultMessage="This job changed recently!"
133
            description="Title for the alert for changes to this job's criteria."
134
          />
135
        </p>
136
137
        <p>
138
          <FormattedMessage
139
            id="assessmentPlan.alert.explanation"
140
            defaultMessage="Parts of the Screening Plan have been changed to match."
141
            description="Explanation that the screening plan may have changed."
142
          />
143
        </p>
144
145
        {createNotifications.length > 0 && (
146
          <p>
147
            <FormattedMessage
148
              id="assessmentPlan.alert.created"
149
              defaultMessage="{skills} {count, plural, one {was} other {were} } added."
150
              description="Description of the new criteria that have been added to this job."
151
              values={{
152
                skills: createNotifications
153
                  .map((notification): string =>
154
                    skillName(notification.skill_id),
155
                  )
156
                  .join(", "),
157
                count: createNotifications.length,
158
              }}
159
            />
160
          </p>
161
        )}
162
        {updateLevelNotifications.length > 0 && (
163
          <p>
164
            <FormattedMessage
165
              id="assessmentPlan.alert.skillLevelUpdated"
166
              defaultMessage="{skills} {count, plural, one {was} other {were} } updated."
167
              description="Description of the criteria that had their level changed."
168
              values={{
169
                skills: updateLevelNotifications
170
                  .map((notification): string =>
171
                    skillName(notification.skill_id),
172
                  )
173
                  .join(", "),
174
                count: updateLevelNotifications.length,
175
              }}
176
            />
177
          </p>
178
        )}
179
        {updateSkillNotifications.map(
180
          (notification): React.ReactElement => (
181
            <p key={`skillUpdatedAlert_${notification.id}`}>
182
              <FormattedMessage
183
                id="assessmentPlan.alert.skillUpdated"
184
                defaultMessage="{oldSkill} was changed to {newSkill}."
185
                description="Description of a criterion which was changed from one skill to another."
186
                values={{
187
                  oldSkill: skillName(notification.skill_id),
188
                  newSkill:
189
                    notification.skill_id_new !== null
190
                      ? skillName(notification.skill_id_new)
191
                      : "UNKNOWN",
192
                }}
193
              />
194
            </p>
195
          ),
196
        )}
197
        {updateBothNotifications.map(
198
          (notification): React.ReactElement => (
199
            <p key={`skillAndLevelUpdatedAlert_${notification.id}`}>
200
              <FormattedMessage
201
                id="assessmentPlan.alert.skillAndLevelUpdated"
202
                defaultMessage="{oldSkill} was changed to {newSkill} and was updated."
203
                description="Description of a criterion which was changed from one skill to another and had its level changed."
204
                values={{
205
                  oldSkill: skillName(notification.skill_id),
206
                  newSkill:
207
                    notification.skill_id_new !== null
208
                      ? skillName(notification.skill_id_new)
209
                      : "UNKNOWN",
210
                }}
211
              />
212
            </p>
213
          ),
214
        )}
215
        {deleteNotifications.length > 0 && (
216
          <p>
217
            <FormattedMessage
218
              id="assessmentPlan.alert.deleted"
219
              defaultMessage="{skills} {count, plural, one {was} other {were} } removed."
220
              description="Description of criteria which were removed from the job."
221
              values={{
222
                skills: deleteNotifications
223
                  .map((notification): string =>
224
                    skillName(notification.skill_id),
225
                  )
226
                  .join(", "),
227
                count: deleteNotifications.length,
228
              }}
229
            />
230
          </p>
231
        )}
232
      </div>
233
234
      <div
235
        data-c-alignment="base(right)"
236
        data-c-grid-item="base(1of3) tl(1of5)"
237
      >
238
        <button
239
          className="button-trash"
240
          type="button"
241
          onClick={(): void => handleDismiss()}
242
          disabled={isUpdating}
243
        >
244
          {isUpdating ? (
245
            <i className="fa fa-spinner fa-spin" />
246
          ) : (
247
            <i className="fa fa-trash" />
248
          )}
249
        </button>
250
      </div>
251
    </div>
252
  );
253
};
254
255
interface AssessmentPlanAlertContainerProps {
256
  notifications: AssessmentPlanNotification[];
257
}
258
259
const mapStateToProps = (
260
  state: RootState,
261
  ownProps: AssessmentPlanAlertContainerProps,
262
): {
263
  skills: Skill[];
264
  isFetching: boolean;
265
  isUpdating: boolean;
266
} => ({
267
  skills: getSkills(state),
268
  isFetching: notificationsAreFetching(state),
269
  isUpdating: some(ownProps.notifications, (notification): boolean =>
270
    notificationIsUpdating(state, notification.id),
271
  ),
272
});
273
274
const mapDispatchToProps = (
275
  dispatch: DispatchType,
276
  ownProps: AssessmentPlanAlertContainerProps,
277
): { handleDismiss: () => void } => ({
278
  handleDismiss: (): void => {
279
    ownProps.notifications.forEach((notification): void => {
280
      dispatch(
281
        updateAssessmentPlanNotification({
282
          ...notification,
283
          acknowledged: true,
284
        }),
285
      );
286
    });
287
  },
288
});
289
290
export const AssessmentPlanAlertContainer = connect(
291
  mapStateToProps,
292
  mapDispatchToProps,
293
)(injectIntl(AssessmentPlanAlert));
294
295
export default AssessmentPlanAlertContainer;
296