Passed
Push — dev ( dd5608...a67171 )
by
unknown
04:14
created

resources/assets/js/components/ApplicationReview/ReviewApplications.tsx   A

Complexity

Total Complexity 1
Complexity/F 0

Size

Lines of Code 191
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 1
eloc 146
c 0
b 0
f 0
dl 0
loc 191
rs 10
mnd 1
bc 1
fnc 0
bpm 0
cpm 0
noi 0
1
import React, { useCallback } from "react";
2
import dayjs from "dayjs";
3
import { FormattedMessage, defineMessages, useIntl } from "react-intl";
4
import { Application, Comment } from "../../models/types";
5
import { SelectOption } from "../Select";
6
import { applicationCategory } from "./helpers";
7
import ReviewCategory from "./ReviewCategory";
8
import ActivityFeed from "../ActivityFeed";
9
import { applicantReviewLocations } from "../../models/localizedConstants";
10
import { LocationId } from "../../models/lookupConstants";
11
import { Portal } from "../../models/app";
12
import { hasKey } from "../../helpers/queries";
13
14
const messages = defineMessages({
15
  underConsiderationTitle: {
16
    id: "review.applications.underConsideration.title",
17
    defaultMessage: "Under Consideration",
18
    description: "Under consideration category title",
19
  },
20
  underConsiderationDescription: {
21
    id: "review.applications.underConsideration.description",
22
    defaultMessage:
23
      "Review the applicants in the Veterans and Canadian Citizens section. If none or very few of these applicants meet the requirements, you can still consider non-Canadian Citizen applications in the Optional Consideration section",
24
    description: "Under consideration category description",
25
  },
26
  optionalConsiderationTitle: {
27
    id: "review.applications.optionalConsideration.title",
28
    defaultMessage: "Optional Consideration",
29
    description: "Optional consideration category title",
30
  },
31
  optionalConsiderationDescription: {
32
    id: "review.applications.optionalConsideration.description",
33
    defaultMessage:
34
      "In this group you will find the applicants who are not Canadian Citizens or do not claim to meet the essential criteria.",
35
    description: "Optional consideration category description",
36
  },
37
  screenedOutTitle: {
38
    id: "review.applications.screenedOut.title",
39
    defaultMessage: "No Longer Under Consideration",
40
    description: "Screened out category title",
41
  },
42
  screenedOutDescription: {
43
    id: "review.applications.screenedOut.description",
44
    defaultMessage: "These applications have already been screened out.",
45
    description: "Screened out category description",
46
  },
47
});
48
49
interface ReviewApplicationsProps {
50
  jobId: number;
51
  title: string;
52
  classification: string;
53
  closeDateTime: Date | null;
54
  applications: Application[];
55
  reviewStatusOptions: SelectOption[];
56
  onStatusChange: (applicationId: number, statusId: number | null) => void;
57
  onBulkStatusChange: (
58
    applicationIds: number[],
59
    statusId: number | null,
60
  ) => void;
61
  onNotesChange: (applicationId: number, notes: string | null) => void;
62
  savingStatuses: { applicationId: number; isSaving: boolean }[];
63
  portal: Portal;
64
}
65
66
const ReviewApplications: React.StatelessComponent<ReviewApplicationsProps> = ({
67
  jobId,
68
  title,
69
  classification,
70
  closeDateTime,
71
  applications,
72
  reviewStatusOptions,
73
  onStatusChange,
74
  onBulkStatusChange,
75
  onNotesChange,
76
  savingStatuses,
77
  portal,
78
}: ReviewApplicationsProps): React.ReactElement => {
79
  const intl = useIntl();
80
  const categories = [
81
    {
82
      id: messages.underConsiderationTitle.id,
83
      title: intl.formatMessage(messages.underConsiderationTitle),
84
      description: intl.formatMessage(messages.underConsiderationDescription),
85
      showScreenOutAll: false,
86
      applications: applications.filter(
87
        application => applicationCategory(application) === "primary",
88
      ),
89
      prioritizeVeterans: false,
90
    },
91
    {
92
      id: messages.optionalConsiderationTitle.id,
93
      title: intl.formatMessage(messages.optionalConsiderationTitle),
94
      description: intl.formatMessage(
95
        messages.optionalConsiderationDescription,
96
      ),
97
      showScreenOutAll: true,
98
      applications: applications.filter(
99
        (application): boolean =>
100
          applicationCategory(application) === "optional",
101
      ),
102
      prioritizeVeterans: true,
103
    },
104
    {
105
      id: messages.screenedOutTitle.id,
106
      title: intl.formatMessage(messages.screenedOutTitle),
107
      description: intl.formatMessage(messages.screenedOutDescription),
108
      showScreenOutAll: false,
109
      applications: applications.filter(
110
        (application): boolean =>
111
          applicationCategory(application) === "screened-out",
112
      ),
113
      prioritizeVeterans: true,
114
    },
115
  ];
116
117
  const filterComments = useCallback(
118
    (comment: Comment): boolean =>
119
      hasKey(applicantReviewLocations, comment.location),
120
    [],
121
  );
122
123
  return (
124
    <section className="applicant-review container--layout-xl">
125
      <div className="flex-grid gutter">
126
        <div className="box med-1of2 job-title-wrapper">
127
          <span>
128
            <FormattedMessage
129
              id="review.applications.indexPageTitle"
130
              defaultMessage="Applications for: {jobTitle} {jobClassification}"
131
              description="Welcome header on Job Applications index page"
132
              values={{
133
                jobTitle: title,
134
                jobClassification: classification,
135
              }}
136
            />
137
          </span>
138
        </div>
139
140
        <div className="box med-1of2 timer-wrapper">
141
          <i className="fas fa-stopwatch" />
142
          &nbsp;
143
          <FormattedMessage
144
            id="job.daysSinceClosed"
145
            defaultMessage="{dayCount, plural,
146
              =0 {No Days}
147
            one {# Day}
148
          other {# Days}
149
        } Since Close"
150
            description="Welcome header on app main page"
151
            values={{
152
              // TODO: Think more carefully about how to handle null fields
153
              dayCount: dayjs().diff(
154
                closeDateTime ? dayjs(closeDateTime) : dayjs(),
155
                "day",
156
              ),
157
            }}
158
          />
159
        </div>
160
      </div>
161
      <div data-clone>
162
        <div data-c-margin="bottom(1)">
163
          <ActivityFeed
164
            jobId={jobId}
165
            isHrAdvisor={portal === "hr"}
166
            generalLocation={LocationId.applicantsGeneric}
167
            locationMessages={applicantReviewLocations}
168
            filterComments={filterComments}
169
          />
170
        </div>
171
      </div>
172
      {categories.map(
173
        (category): React.ReactElement => (
174
          <ReviewCategory
175
            key={category.id}
176
            {...category}
177
            reviewStatusOptions={reviewStatusOptions}
178
            onStatusChange={onStatusChange}
179
            onNotesChange={onNotesChange}
180
            savingStatuses={savingStatuses}
181
            onBulkStatusChange={onBulkStatusChange}
182
            portal={portal}
183
          />
184
        ),
185
      )}
186
    </section>
187
  );
188
};
189
190
export default ReviewApplications;
191