Passed
Push — feature/post-covid-application... ( 4340d4...92a3aa )
by Yonathan
08:31 queued 01:05
created

resources/assets/js/components/Application/ProgressBar/ProgressBar.tsx   A

Complexity

Total Complexity 3
Complexity/F 0

Size

Lines of Code 233
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 3
eloc 184
mnd 3
bc 3
fnc 0
dl 0
loc 233
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
rs 10
1
import * as React from "react";
2
import { defineMessages, FormattedMessage, useIntl } from "react-intl";
3
import { Link } from "../../../models/app";
4
import { getLocale } from "../../../helpers/localize";
5
import { navigate } from "../../../helpers/router";
6
import { readableTimeFromNow } from "../../../helpers/dates";
7
8
const stepNames = defineMessages({
9
  welcome: {
10
    id: "applicationTimeline.progressBar.welcome",
11
    defaultMessage: "Welcome",
12
  },
13
  step01: {
14
    id: "applicationTimeline.progressBar.step01",
15
    defaultMessage: "Step 1/6",
16
  },
17
  step02: {
18
    id: "applicationTimeline.progressBar.step02",
19
    defaultMessage: "Step 2/6",
20
  },
21
  step03: {
22
    id: "applicationTimeline.progressBar.step03",
23
    defaultMessage: "Step 3/6",
24
  },
25
  step04: {
26
    id: "applicationTimeline.progressBar.step04",
27
    defaultMessage: "Step 4/6",
28
  },
29
  step05: {
30
    id: "applicationTimeline.progressBar.step05",
31
    defaultMessage: "Step 5/6",
32
  },
33
  step06: {
34
    id: "applicationTimeline.progressBar.step06",
35
    defaultMessage: "Step 6/6",
36
  },
37
});
38
39
export const ProgressBarStepId = {
40
  welcome: 1,
41
  basicInfo: 2,
42
  experienceTutorial: 3,
43
  experience: 4,
44
  skillsTutorial: 5,
45
  skills: 6,
46
  myFit: 7,
47
  review: 8,
48
  finalSubmission: 9,
49
};
50
51
// Returns the list item element that corresponds to the steps status.
52
const createStep = (
53
  link: Link,
54
  status: ProgressBarStepStatus,
55
  icons: {
56
    [key in ProgressBarStepStatus]: { className: string; color: string };
57
  },
58
): React.ReactElement => {
59
  const isComplete: boolean = status === "complete";
60
  const isCurrent: boolean = status === "current";
61
  const hasError: boolean = status === "error";
62
63
  if (isComplete) {
64
    return (
65
      <li key={link.title}>
66
        <span data-c-visibility="invisible">
67
          <FormattedMessage
68
            id="applicationTimeline.progressbar.completedStepLabel"
69
            defaultMessage="Completed: "
70
            description="Visually hidden text used to indicate the completed steps."
71
          />
72
        </span>
73
        <a
74
          href={link.url}
75
          title={link.title}
76
          onClick={(e) => {
77
            e.preventDefault();
78
            navigate(link.url);
79
          }}
80
        >
81
          <span data-c-visibility="invisible">{link.text}</span>
82
          <i
83
            className={icons[status].className}
84
            data-c-color={icons[status].color}
85
          />
86
        </a>
87
      </li>
88
    );
89
  }
90
  if (isCurrent) {
91
    return (
92
      <li key={link.title} title={link.title}>
93
        <span data-c-visibility="invisible">
94
          <FormattedMessage
95
            id="applicationTimeline.progressbar.currentStepLabel"
96
            defaultMessage="Current: "
97
            description="Visually hidden text used to indicate the current steps."
98
          />
99
        </span>
100
        <span data-c-visibility="invisible">{link.text}</span>
101
        <i
102
          className={icons[status].className}
103
          data-c-color={icons[status].color}
104
        />
105
      </li>
106
    );
107
  }
108
  if (hasError) {
109
    return (
110
      <li key={link.title}>
111
        <span data-c-visibility="invisible">
112
          <FormattedMessage
113
            id="applicationTimeline.progressbar.errorStepLabel"
114
            defaultMessage="Error: "
115
            description="Visually hidden text used to indicate the steps with errors."
116
          />
117
        </span>
118
        <a href={link.url} title={link.title}>
119
          <span data-c-visibility="invisible">{link.text}</span>
120
          <i
121
            className={icons[status].className}
122
            data-c-color={icons[status].color}
123
          />
124
        </a>
125
      </li>
126
    );
127
  }
128
  return (
129
    <li key={link.title} title={link.title}>
130
      <span data-c-visibility="invisible">{link.text}</span>
131
      <i
132
        className={icons[status].className}
133
        data-c-color={icons[status].color}
134
      />
135
    </li>
136
  );
137
};
138
139
export type ProgressBarStepStatus =
140
  | "default"
141
  | "complete"
142
  | "error"
143
  | "current";
144
145
interface ProgressBarProps {
146
  /** The closing date of the job poster. */
147
  closeDateTime: Date;
148
  /** The current step number. This is required for the informational steps, since they do not use a list item. */
149
  stepNumber: number;
150
  /** List of the steps. */
151
  steps: { link: Link; status: ProgressBarStepStatus }[];
152
}
153
154
const ProgressBar: React.FunctionComponent<ProgressBarProps> = ({
155
  steps,
156
  closeDateTime,
157
  stepNumber,
158
}) => {
159
  const intl = useIntl();
160
  const locale = getLocale(intl.locale);
161
162
  // There are nine steps throughout the timeline, some steps using the same title (informational steps). This maps the step number to its corresponding title.
163
  const getStepName = {
164
    [ProgressBarStepId.welcome]: intl.formatMessage(stepNames.welcome),
165
    [ProgressBarStepId.basicInfo]: intl.formatMessage(stepNames.step01),
166
    [ProgressBarStepId.experienceTutorial]: intl.formatMessage(
167
      stepNames.step02,
168
    ),
169
    [ProgressBarStepId.experience]: intl.formatMessage(stepNames.step02),
170
    [ProgressBarStepId.skillsTutorial]: intl.formatMessage(stepNames.step03),
171
    [ProgressBarStepId.skills]: intl.formatMessage(stepNames.step03),
172
    [ProgressBarStepId.myFit]: intl.formatMessage(stepNames.step04),
173
    [ProgressBarStepId.review]: intl.formatMessage(stepNames.step05),
174
    [ProgressBarStepId.finalSubmission]: intl.formatMessage(stepNames.step06),
175
  };
176
177
  const icons: {
178
    [key in ProgressBarStepStatus]: { className: string; color: string };
179
  } = {
180
    default: {
181
      className: "fas fa-circle",
182
      color: "grey",
183
    },
184
    complete: {
185
      className: "fas fa-check-circle",
186
      color: "go",
187
    },
188
    error: {
189
      className: "fas fa-exclamation-circle",
190
      color: "stop",
191
    },
192
    current: {
193
      className: "far fa-circle",
194
      color: "white",
195
    },
196
  };
197
198
  return (
199
    <div data-c-background="black(100)" data-c-padding="tb(1)">
200
      <div data-c-container="large">
201
        <div data-c-grid="gutter(all, 1)">
202
          <div data-c-grid-item="tl(1of2)" data-c-align="base(center) tl(left)">
203
            <span data-c-color="white">{getStepName[stepNumber]}</span>
204
            <ol className="applicant-application-progress-bar">
205
              {steps.map(
206
                ({ link, status }): React.ReactElement =>
207
                  createStep(link, status, icons),
208
              )}
209
            </ol>
210
          </div>
211
          <div
212
            data-c-grid-item="tl(1of2)"
213
            data-c-align="base(center) tl(right)"
214
          >
215
            <span data-c-color="white">
216
              <FormattedMessage
217
                id="applicationTimeline.progressbar.applicationDeadline"
218
                defaultMessage="Application Deadline: {timeLeft}"
219
                description="Label for the application deadline"
220
                values={{
221
                  timeLeft: readableTimeFromNow(locale, closeDateTime),
222
                }}
223
              />
224
            </span>
225
          </div>
226
        </div>
227
      </div>
228
    </div>
229
  );
230
};
231
232
export default ProgressBar;
233