Passed
Push — task/applicant-skill-save-api ( 2ebd5d...fa7a9c )
by
unknown
06:31 queued 10s
created

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

Complexity

Total Complexity 6
Complexity/F 0

Size

Lines of Code 289
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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