Passed
Push — dev ( a72726...5a7351 )
by
unknown
05:21
created

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

Complexity

Total Complexity 13
Complexity/F 0

Size

Lines of Code 273
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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