Passed
Push — feature/response-screening ( e0fe27 )
by Chris
07:06
created

resources/assets/js/components/StrategicTalentResponse/ResponseScreening/ApplicantBucket.tsx   A

Complexity

Total Complexity 7
Complexity/F 0

Size

Lines of Code 348
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 7
eloc 273
mnd 7
bc 7
fnc 0
dl 0
loc 348
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
rs 10
1
/* eslint camelcase: "off", @typescript-eslint/camelcase: "off" */
2
import React, { useState } from "react";
3
import { defineMessages, FormattedMessage, useIntl } from "react-intl";
4
import { FastField, Formik, Form } from "formik";
5
import SelectInput from "../../Form/SelectInput";
6
import { Application } from "../../../models/types";
7
import { Portal } from "../../../models/app";
8
import * as routes from "../../../helpers/routes";
9
import {
10
  ResponseScreeningBuckets,
11
  ResponseReviewStatuses,
12
} from "../../../models/localizedConstants";
13
import { ResponseScreeningBuckets as ResponseBuckets } from "../../../models/lookupConstants";
14
15
const displayMessages = defineMessages({
16
  viewApplication: {
17
    id: "responseScreening.bucket.viewApplicationLabel",
18
    defaultMessage: "View Application",
19
    description: "Label for 'View Application' link.",
20
  },
21
  viewProfile: {
22
    id: "responseScreening.bucket.viewProfileLabel",
23
    defaultMessage: "View Profile",
24
    description: "Label for 'View Profile' link.",
25
  },
26
  notes: {
27
    id: "responseScreening.bucket.notesLabel",
28
    defaultMessage: "Add/Edit Notes",
29
    description: "Label for 'Add/Edit Notes' button.",
30
  },
31
  save: {
32
    id: "responseScreening.bucket.saveLabel",
33
    defaultMessage: "Save Changes",
34
    description: "Label for 'Save Changes' button.",
35
  },
36
  selectStatusDefault: {
37
    id: "responseScreening.bucket.selectStatusDefault",
38
    defaultMessage: "Select a status...",
39
    description: "Default option text for the Status dropdown.",
40
  },
41
  selectStatusLabel: {
42
    id: "responseScreening.bucket.selectStatusLabel",
43
    defaultMessage: "Review Status",
44
    description: "Label for the Status dropdown.",
45
  },
46
  selectDepartmentDefault: {
47
    id: "responseScreening.bucket.selectDepartmentDefault",
48
    defaultMessage: "Select a department...",
49
    description: "Default option text for the Department dropdown.",
50
  },
51
  selectDepartmentLabel: {
52
    id: "responseScreening.bucket.selectDepartmentLabel",
53
    defaultMessage: "Department Allocation",
54
    description: "Label for the Department dropdown.",
55
  },
56
  clickView: {
57
    id: "responseScreening.bucket.accessibleViewLabel",
58
    defaultMessage: "Click to view...",
59
    description: "Accessible text for screen reading accordion elements.",
60
  },
61
});
62
63
enum IconStatus {
64
  ASSESSMENT = "question",
65
  READY = "check",
66
  RECEIVED = "exclamation",
67
}
68
69
interface StatusIconProps {
70
  status: IconStatus;
71
  color: string;
72
  small: boolean;
73
}
74
75
const StatusIcon: React.FC<StatusIconProps> = ({
76
  status,
77
  color,
78
  small,
79
}): React.ReactElement => {
80
  return (
81
    <i
82
      className={`fas fa-${status}-circle`}
83
      data-c-color={color}
84
      data-c-font-size={small ? "small" : ""}
85
    />
86
  );
87
};
88
89
interface ApplicationReviewProps {
90
  application: Application;
91
  departmentEditable: boolean;
92
  portal: Portal;
93
}
94
95
const ApplicationReview: React.FC<ApplicationReviewProps> = ({
96
  application,
97
  departmentEditable,
98
  portal,
99
}): React.ReactElement => {
100
  const intl = useIntl();
101
  const applicantUrlMap: { [key in typeof portal]: string } = {
102
    hr: routes.hrApplicantShow(intl.locale, application.id),
103
    manager: routes.managerApplicantShow(intl.locale, application.id),
104
  };
105
  const applicationUrlMap: { [key in typeof portal]: string } = {
106
    hr: routes.hrApplicationShow(intl.locale, application.id),
107
    manager: routes.managerApplicationShow(intl.locale, application.id),
108
  };
109
  const applicantUrl = applicantUrlMap[portal];
110
  const applicationUrl = applicationUrlMap[portal];
111
112
  const reviewOptions = Object.values(ResponseReviewStatuses).map((status): {
113
    value: number;
114
    label: string;
115
  } => ({
116
    value: status.id,
117
    label: intl.formatMessage(status.name),
118
  }));
119
120
  let rowIcon: React.ReactElement;
121
122
  switch (application.application_review?.review_status_id) {
123
    case 4:
124
    case 5:
125
    case 7:
126
      rowIcon = (
127
        <StatusIcon status={IconStatus.READY} color="go" small={false} />
128
      );
129
      break;
130
    case 6:
131
      rowIcon = (
132
        <StatusIcon status={IconStatus.ASSESSMENT} color="slow" small={false} />
133
      );
134
      break;
135
    default:
136
      rowIcon = (
137
        <StatusIcon status={IconStatus.RECEIVED} color="c1" small={false} />
138
      );
139
  }
140
141
  return (
142
    <div className="applicant">
143
      <Formik
144
        initialValues={{
145
          reviewStatus:
146
            application.application_review?.review_status_id || null,
147
          department: null,
148
        }}
149
        onSubmit={(values, { setSubmitting }) => {
150
          console.log(values);
151
          setSubmitting(false);
152
        }}
153
      >
154
        <Form data-c-grid="gutter(all, 1) middle">
155
          <div data-c-grid-item="base(1of4)">
156
            <div>
157
              {rowIcon}
158
              <div>
159
                <p data-c-font-weight="bold" data-c-font-size="h4">
160
                  {application.applicant.user.full_name}
161
                </p>
162
                <p data-c-margin="bottom(.5)">
163
                  <a
164
                    href={`mailto:${application.applicant.user.email}`}
165
                    title=""
166
                  >
167
                    {application.applicant.user.email}
168
                  </a>
169
                </p>
170
                <p data-c-font-size="small">
171
                  <a href={applicationUrl} title="" data-c-margin="right(.5)">
172
                    <FormattedMessage {...displayMessages.viewApplication} />
173
                  </a>
174
                  <a href={applicantUrl} title="">
175
                    <FormattedMessage {...displayMessages.viewProfile} />
176
                  </a>
177
                </p>
178
              </div>
179
            </div>
180
          </div>
181
          <FastField
182
            id={`review-status-select-${application.applicant_id}`}
183
            name="reviewStatus"
184
            label={intl.formatMessage(displayMessages.selectStatusLabel)}
185
            grid="base(1of4)"
186
            component={SelectInput}
187
            required
188
            nullSelection={intl.formatMessage(
189
              displayMessages.selectStatusDefault,
190
            )}
191
            options={reviewOptions}
192
          />
193
          {departmentEditable && (
194
            <FastField
195
              id={`department-allocation-select-${application.applicant_id}`}
196
              name="department"
197
              label={intl.formatMessage(displayMessages.selectDepartmentLabel)}
198
              grid="base(1of4)"
199
              component={SelectInput}
200
              required
201
              nullSelection={intl.formatMessage(
202
                displayMessages.selectDepartmentDefault,
203
              )}
204
              options={[
205
                { label: "Health Canada", value: 1 },
206
                { label: "Department of Defense", value: 2 },
207
              ]}
208
            />
209
          )}
210
          <div data-c-grid-item="base(1of4)" data-c-align="base(right)">
211
            <button
212
              data-c-button="outline(c1)"
213
              type="button"
214
              data-c-radius="rounded"
215
            >
216
              <i className="fas fa-plus" />
217
              <span>
218
                <FormattedMessage {...displayMessages.notes} />
219
              </span>
220
            </button>
221
            <button
222
              data-c-button="solid(c1)"
223
              type="submit"
224
              data-c-radius="rounded"
225
            >
226
              <span>
227
                <FormattedMessage {...displayMessages.save} />
228
              </span>
229
            </button>
230
          </div>
231
        </Form>
232
      </Formik>
233
    </div>
234
  );
235
};
236
237
interface ApplicantBucketProps {
238
  bucket: string;
239
  applications: Application[];
240
  portal: Portal;
241
}
242
243
const ApplicantBucket: React.FC<ApplicantBucketProps> = ({
244
  bucket,
245
  applications,
246
  portal,
247
}): React.ReactElement => {
248
  const [isExpanded, setIsExpanded] = useState(false);
249
  const {
250
    title: bucketTitle,
251
    description: bucketDescription,
252
  } = ResponseScreeningBuckets[bucket];
253
254
  const handleExpandClick = (): void => {
255
    setIsExpanded(!isExpanded);
256
  };
257
258
  return (
259
    <div
260
      data-c-accordion=""
261
      data-c-background="white(100)"
262
      data-c-margin="top(.5)"
263
      data-c-card=""
264
      className={isExpanded ? "active" : ""}
265
    >
266
      <button
267
        aria-expanded={isExpanded}
268
        data-c-accordion-trigger=""
269
        tabIndex={0}
270
        type="button"
271
        onClick={handleExpandClick}
272
      >
273
        <div data-c-padding="top(normal) right bottom(normal) left(normal)">
274
          <p data-c-font-weight="bold" data-c-font-size="h3">
275
            <FormattedMessage {...bucketTitle} /> ({applications.length})
276
          </p>
277
          <p data-c-margin="top(quarter)" data-c-colour="gray">
278
            {bucket === ResponseBuckets.Consideration ? (
279
              <FormattedMessage
280
                id={ResponseScreeningBuckets.consideration.title.id}
281
                defaultMessage={
282
                  ResponseScreeningBuckets.consideration.description
283
                    .defaultMessage
284
                }
285
                description={
286
                  ResponseScreeningBuckets.consideration.description.description
287
                }
288
                values={{
289
                  iconAssessment: (
290
                    <StatusIcon
291
                      status={IconStatus.ASSESSMENT}
292
                      color="slow"
293
                      small
294
                    />
295
                  ),
296
                  iconReady: (
297
                    <StatusIcon status={IconStatus.READY} color="go" small />
298
                  ),
299
                  iconReceived: (
300
                    <StatusIcon status={IconStatus.RECEIVED} color="c1" small />
301
                  ),
302
                }}
303
              />
304
            ) : (
305
              <FormattedMessage {...bucketDescription} />
306
            )}
307
          </p>
308
        </div>
309
        <span data-c-visibility="invisible">
310
          <FormattedMessage {...displayMessages.clickView} />
311
        </span>
312
        {isExpanded ? (
313
          <i
314
            aria-hidden="true"
315
            className="fas fa-minus"
316
            data-c-accordion-remove=""
317
            data-c-colour="black"
318
          />
319
        ) : (
320
          <i
321
            aria-hidden="true"
322
            className="fas fa-plus"
323
            data-c-accordion-add=""
324
            data-c-colour="black"
325
          />
326
        )}
327
      </button>
328
      <div
329
        aria-hidden={!isExpanded}
330
        data-c-accordion-content=""
331
        data-c-padding="right(normal) left(normal)"
332
      >
333
        <div data-c-padding="bottom(normal)">
334
          {applications.map(application => (
335
            <ApplicationReview
336
              application={application}
337
              departmentEditable={bucket === ResponseBuckets.Allocated}
338
              portal={portal}
339
            />
340
          ))}
341
        </div>
342
      </div>
343
    </div>
344
  );
345
};
346
347
export default ApplicantBucket;
348