Passed
Push — feature/timeline-progress-bar ( 7cef58...c7c754 )
by Yonathan
03:20
created

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

Complexity

Total Complexity 3
Complexity/F 0

Size

Lines of Code 239
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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