Passed
Push — feature/application-urls ( 912c99...540418 )
by Tristan
07:23 queued 02:05
created

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

Complexity

Total Complexity 13
Complexity/F 0

Size

Lines of Code 279
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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