Passed
Push — master ( 6ceee8...d4902a )
by Tristan
20:51 queued 08:57
created

resources/assets/js/components/JobBuilder/jobBuilderHelpers.ts   A

Complexity

Total Complexity 13
Complexity/F 0

Size

Lines of Code 277
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 224
dl 0
loc 277
rs 10
c 0
b 0
f 0
wmc 13
mnd 13
bc 13
fnc 0
bpm 0
cpm 0
noi 2
1
import { Job, JobPosterKeyTask, Criteria } from "../../models/types";
2
import { ProgressTrackerState } from "../ProgressTracker/types";
3
import {
4
  jobBuilderIntro,
5
  jobBuilderDetails,
6
  jobBuilderEnv,
7
  jobBuilderImpact,
8
  jobBuilderTasks,
9
  jobBuilderSkills,
10
  jobBuilderReview,
11
} from "../../helpers/routes";
12
13
/** Job Builder Constants */
14
export const NUM_OF_TASKS = 6;
15
16
const isFilled = (value: any | null | undefined): boolean => {
0 ignored issues
show
introduced by
Unexpected any. Specify a different type.
Loading history...
17
  return value !== null && value !== undefined && value !== "";
18
};
19
20
const isEmpty = (value: any | null | undefined): boolean => {
0 ignored issues
show
introduced by
Unexpected any. Specify a different type.
Loading history...
21
  return value === null || value === undefined;
22
};
23
24
const jobIntroValues = (job: Job): (string | number | null)[] => [
25
  job.department_id,
26
  job.en.division,
27
  job.fr.division,
28
];
29
const isJobBuilderIntroComplete = (job: Job): boolean => {
30
  return jobIntroValues(job).every(isFilled);
31
};
32
const isJobBuilderIntroUntouched = (job: Job): boolean =>
33
  jobIntroValues(job).every(isEmpty);
34
35
/**
36
 * Return the ProgressTrackerItem state for the intro step.
37
 * @param job
38
 * @param allowUntouched If allowUntouched is set to true, the state will be "null" (instead of "error") when the step has never been touched.
39
 */
40
export const jobBuilderIntroProgressState = (
41
  job: Job | null,
42
  allowUntouched = false,
43
): ProgressTrackerState => {
44
  if (allowUntouched && (job === null || isJobBuilderIntroUntouched(job))) {
45
    return "null";
46
  }
47
  return job && isJobBuilderIntroComplete(job) ? "complete" : "error";
48
};
49
50
const jobDetailsValues = (
51
  job: Job,
52
  locale: string,
53
): (string | number | null | boolean)[] => [
54
  job.term_qty,
55
  job.classification_id,
56
  job.classification_level,
57
  job.security_clearance_id,
58
  job.language_requirement_id,
59
  job.province_id,
60
  job.remote_work_allowed,
61
  job.telework_allowed_frequency_id,
62
  job.flexible_hours_frequency_id,
63
  job[locale].title,
64
  job[locale].city,
65
];
66
67
export const isJobBuilderDetailsComplete = (
68
  job: Job,
69
  locale: string,
70
): boolean => {
71
  return jobDetailsValues(job, locale).every(isFilled);
72
};
73
74
export const isJobBuilderDetailsUntouched = (
75
  job: Job,
76
  locale: string,
77
): boolean => {
78
  const nullableValues = jobDetailsValues(job, locale).filter(
79
    (item): boolean => typeof item !== "boolean",
80
  );
81
  return nullableValues.every(isEmpty);
82
};
83
84
/**
85
 *
86
 * @param job
87
 * @param locale
88
 * @param allowUntouched If allowUntouched is set to true, the state will be "null" (instead of "error") when the step has never been touched.
89
 */
90
export const jobBuilderDetailsProgressState = (
91
  job: Job | null,
92
  locale: string,
93
  allowUntouched = false,
94
): ProgressTrackerState => {
95
  if (
96
    allowUntouched &&
97
    (job === null || isJobBuilderDetailsUntouched(job, locale))
98
  ) {
99
    return "null";
100
  }
101
  return job && isJobBuilderDetailsComplete(job, locale) ? "complete" : "error";
102
};
103
104
const jobEnvValues = (job: Job, locale: string): (string | number | null)[] => [
105
  job.team_size,
106
  job.fast_vs_steady,
107
  job.horizontal_vs_vertical,
108
  job.experimental_vs_ongoing,
109
  job.citizen_facing_vs_back_office,
110
  job.collaborative_vs_independent,
111
  job.work_env_features,
112
  job[locale].culture_summary,
113
];
114
const jobEnvValuesOptional = (
115
  job: Job,
116
  locale: string,
117
): (string | number | null)[] => [
118
  job[locale].work_env_description,
119
  job[locale].culture_special,
120
];
121
122
const isJobBuilderEnvComplete = (job: Job, locale: string): boolean => {
123
  return jobEnvValues(job, locale).every(isFilled);
124
};
125
const isJobBuilderEnvUntouched = (job: Job, locale: string): boolean => {
126
  const allJobValues = [
127
    ...jobEnvValues(job, locale),
128
    ...jobEnvValuesOptional(job, locale),
129
  ];
130
  const nullableValues = allJobValues.filter(
131
    (item): boolean => typeof item !== "boolean",
132
  );
133
  return nullableValues.every(isEmpty);
134
};
135
export const jobBuilderEnvProgressState = (
136
  job: Job | null,
137
  locale: string,
138
  allowUntouched = false,
139
): ProgressTrackerState => {
140
  if (
141
    allowUntouched &&
142
    (job === null || isJobBuilderEnvUntouched(job, locale))
143
  ) {
144
    return "null";
145
  }
146
  return job && isJobBuilderEnvComplete(job, locale) ? "complete" : "error";
147
};
148
149
const jobImpactValues = (job: Job, locale: "en" | "fr"): (string | null)[] => [
150
  job[locale].dept_impact,
151
  job[locale].team_impact,
152
  job[locale].hire_impact,
153
];
154
const isJobImpactComplete = (job: Job, locale: "en" | "fr"): boolean => {
155
  return jobImpactValues(job, locale).every(isFilled);
156
};
157
const isJobImpactUntouched = (job: Job, locale: "en" | "fr"): boolean => {
158
  return jobImpactValues(job, locale).every(isEmpty);
159
};
160
export const jobImpactProgressState = (
161
  job: Job | null,
162
  locale: "en" | "fr",
163
  allowUntouched = false,
164
): ProgressTrackerState => {
165
  if (allowUntouched && (job === null || isJobImpactUntouched(job, locale))) {
166
    return "null";
167
  }
168
  return job && isJobImpactComplete(job, locale) ? "complete" : "error";
169
};
170
171
const isKeyTaskComplete = (
172
  task: JobPosterKeyTask,
173
  locale: "en" | "fr",
174
): boolean => isFilled(task[locale].description);
175
const isJobTasksComplete = (
176
  tasks: JobPosterKeyTask[],
177
  locale: "en" | "fr",
178
): boolean => {
179
  return (
180
    tasks.length > 0 &&
181
    tasks.length <= NUM_OF_TASKS &&
182
    tasks.every((task): boolean => isKeyTaskComplete(task, locale))
183
  );
184
};
185
// FIXME: There is currently no way to know the difference between an empty list, and an untouched list of tasks
186
const isJobTasksUntouched = (tasks: JobPosterKeyTask[]): boolean =>
187
  tasks.length === 0;
188
export const jobTasksProgressState = (
189
  tasks: JobPosterKeyTask[],
190
  locale: "en" | "fr",
191
  allowUntouched = false,
192
): ProgressTrackerState => {
193
  if (allowUntouched && isJobTasksUntouched(tasks)) {
194
    return "null";
195
  }
196
  return isJobTasksComplete(tasks, locale) ? "complete" : "error";
197
};
198
199
const isCriterionComplete = (
200
  criterion: Criteria,
201
  locale: "en" | "fr",
202
): boolean => {
203
  const { description } = criterion[locale];
204
  return description !== null && description.length > 0;
205
};
206
// FIXME: There is currently no way to know the difference between an untouched list, and one where criteria have been added then removed
207
const isCriteriaUntouched = (criteria: Criteria[]): boolean =>
208
  criteria.length === 0;
209
const isCriteriaComplete = (
210
  criteria: Criteria[],
211
  locale: "en" | "fr",
212
): boolean => {
213
  return (
214
    criteria.length > 0 &&
215
    criteria.every((criterion): boolean =>
216
      isCriterionComplete(criterion, locale),
217
    )
218
  );
219
};
220
export const criteriaProgressState = (
221
  criteria: Criteria[],
222
  locale: "en" | "fr",
223
  allowUntouched = false,
224
): ProgressTrackerState => {
225
  if (allowUntouched && isCriteriaUntouched(criteria)) {
226
    return "null";
227
  }
228
  return isCriteriaComplete(criteria, locale) ? "complete" : "error";
229
};
230
231
export const isJobBuilderComplete = (
232
  job: Job,
233
  tasks: JobPosterKeyTask[],
234
  criteria: Criteria[],
235
  locale: "en" | "fr",
236
): boolean => {
237
  return (
238
    isJobBuilderIntroComplete(job) &&
239
    isJobBuilderDetailsComplete(job, locale) &&
240
    isJobBuilderEnvComplete(job, locale) &&
241
    isJobImpactComplete(job, locale) &&
242
    isJobTasksComplete(tasks, locale) &&
243
    isCriteriaComplete(criteria, locale)
244
  );
245
};
246
247
export type JobBuilderPage =
248
  | "intro"
249
  | "details"
250
  | "env"
251
  | "impact"
252
  | "tasks"
253
  | "skills"
254
  | "review";
255
256
export const pageToUrlBuilder = (
257
  page: JobBuilderPage,
258
): ((locale: string, jobId?: number) => string) => {
259
  switch (page) {
260
    case "intro":
261
      return jobBuilderIntro;
262
    case "details":
263
      return jobBuilderDetails;
264
    case "env":
265
      return jobBuilderEnv;
266
    case "impact":
267
      return jobBuilderImpact;
268
    case "tasks":
269
      return jobBuilderTasks;
270
    case "skills":
271
      return jobBuilderSkills;
272
    case "review":
273
    default:
274
      return jobBuilderReview;
275
  }
276
};
277