Passed
Push — dev ( 5d6e8c...ff5779 )
by
unknown
05:52
created

resources/assets/js/components/JobBuilder/JobBuilderStep.tsx   A

Complexity

Total Complexity 12
Complexity/F 0

Size

Lines of Code 221
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 12
eloc 176
mnd 12
bc 12
fnc 0
dl 0
loc 221
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import React, { useEffect, useState } from "react";
2
import { FormattedMessage } from "react-intl";
3
import { connect } from "react-redux";
4
import nprogress from "nprogress";
5
import {
6
  Job,
7
  JobPosterKeyTask,
8
  Criteria,
9
  Skill,
10
  Department,
11
} from "../../models/types";
12
import { JobBuilderPage } from "./jobBuilderHelpers";
13
import {
14
  getJob,
15
  getTasksByJob,
16
  getCriteriaByJob,
17
} from "../../store/Job/jobSelector";
18
import { getSkills } from "../../store/Skill/skillSelector";
19
import { DispatchType } from "../../configureStore";
20
import {
21
  fetchJob,
22
  fetchJobTasks,
23
  fetchCriteria,
24
  setSelectedJob,
25
} from "../../store/Job/jobActions";
26
import { fetchSkills } from "../../store/Skill/skillActions";
27
import JobBuilderProgressTracker from "./JobBuilderProgressTracker";
28
import { getDepartments } from "../../store/Department/deptSelector";
29
import { getDepartments as fetchDepartments } from "../../store/Department/deptActions";
30
import { RootState } from "../../store/store";
31
32
interface JobBuilderStepProps {
33
  jobId: number | null;
34
  job: Job | null;
35
  loadJob: (jobId: number) => Promise<void>;
36
  keyTasks: JobPosterKeyTask[];
37
  loadTasks: (jobId: number) => Promise<void>;
38
  criteria: Criteria[];
39
  loadCriteria: (jobId: number) => Promise<void>;
40
  // List of known department options.
41
  departments: Department[];
42
  // This is called when departments is empty to fetch departments.
43
  loadDepartments: () => Promise<void>;
44
  skills: Skill[];
45
  loadSkills: () => Promise<void>;
46
  currentPage: JobBuilderPage | null;
47
  forceIsLoading?: boolean;
48
  children: React.ReactNode;
49
}
50
51
const JobBuilderStep: React.FunctionComponent<JobBuilderStepProps> = ({
52
  jobId,
53
  job,
54
  loadJob,
55
  keyTasks,
56
  loadTasks,
57
  criteria,
58
  loadCriteria,
59
  loadSkills,
60
  departments,
61
  loadDepartments,
62
  currentPage,
63
  forceIsLoading,
64
  children,
65
}): React.ReactElement => {
66
  // Trigger fetching of job details
67
  const [isLoadingJob, setIsLoadingJob] = useState(false);
68
  useEffect((): (() => void) => {
69
    let isSubscribed = true;
70
    if (jobId) {
71
      setIsLoadingJob(true);
72
      loadJob(jobId).finally((): void => {
73
        if (isSubscribed) {
74
          setIsLoadingJob(false);
75
        }
76
      });
77
    }
78
    return (): void => {
79
      isSubscribed = false;
80
    };
81
  }, [jobId, loadJob]);
82
  const [isLoadingTasks, setIsLoadingTasks] = useState(false);
83
  useEffect((): (() => void) => {
84
    let isSubscribed = true;
85
    if (jobId) {
86
      setIsLoadingTasks(true);
87
      loadTasks(jobId).finally((): void => {
88
        if (isSubscribed) {
89
          setIsLoadingTasks(false);
90
        }
91
      });
92
    }
93
    return (): void => {
94
      isSubscribed = false;
95
    };
96
  }, [jobId, loadTasks]);
97
  const [isLoadingCriteria, setIsLoadingCriteria] = useState(false);
98
  useEffect((): (() => void) => {
99
    let isSubscribed = true;
100
    if (jobId) {
101
      setIsLoadingCriteria(true);
102
      loadCriteria(jobId).finally((): void => {
103
        if (isSubscribed) {
104
          setIsLoadingCriteria(false);
105
        }
106
      });
107
    }
108
    return (): void => {
109
      isSubscribed = false;
110
    };
111
  }, [jobId, loadCriteria]);
112
113
  const dataIsLoading =
114
    forceIsLoading || isLoadingJob || isLoadingTasks || isLoadingCriteria;
115
116
  // Trigger fetching of other resources needed for Job Builder
117
  useEffect((): void => {
118
    if (departments.length === 0) {
119
      loadDepartments();
120
    }
121
  }, [departments, loadDepartments]);
122
  useEffect((): void => {
123
    loadSkills();
124
  }, [loadSkills]);
125
126
  useEffect((): void => {
127
    if (jobId !== null && job === null) {
128
      nprogress.start();
129
    } else if (currentPage !== "intro") {
130
      nprogress.done();
131
    }
132
  }, [currentPage, job, jobId]);
133
134
  return (
135
    <section>
136
      <JobBuilderProgressTracker
137
        job={job}
138
        jobId={jobId}
139
        tasks={keyTasks}
140
        criteria={criteria}
141
        dataIsLoading={dataIsLoading}
142
        currentPage={currentPage}
143
      />
144
      {jobId !== null && job === null && (
145
        <div
146
          data-c-container="form"
147
          data-c-padding="top(triple) bottom(triple)"
148
        >
149
          <div
150
            data-c-background="white(100)"
151
            data-c-card
152
            data-c-padding="all(double)"
153
            data-c-radius="rounded"
154
            data-c-align="base(centre)"
155
          >
156
            <p>
157
              <FormattedMessage
158
                id="jobBuilder.loading"
159
                defaultMessage="Your job is loading..."
160
                description="Message indicating that the current job is still being loaded."
161
              />
162
            </p>
163
          </div>
164
        </div>
165
      )}
166
      {children}
167
    </section>
168
  );
169
};
170
171
const mapStateToProps = (
172
  state: RootState,
173
  { jobId }: { jobId: number | null },
174
): {
175
  job: Job | null;
176
  keyTasks: JobPosterKeyTask[];
177
  criteria: Criteria[];
178
  skills: Skill[];
179
  departments: Department[];
180
} => ({
181
  job: jobId !== null ? getJob(state, { jobId }) : null,
182
  keyTasks: jobId !== null ? getTasksByJob(state, { jobId }) : [],
183
  criteria: jobId !== null ? getCriteriaByJob(state, { jobId }) : [],
184
  skills: getSkills(state),
185
  departments: getDepartments(state),
186
});
187
188
const mapDispatchToProps = (
189
  dispatch: DispatchType,
190
): {
191
  loadJob: (jobId: number) => Promise<void>;
192
  loadTasks: (jobId: number) => Promise<void>;
193
  loadCriteria: (jobId: number) => Promise<void>;
194
  loadSkills: () => Promise<void>;
195
  loadDepartments: () => Promise<void>;
196
} => ({
197
  loadJob: async (jobId: number): Promise<void> => {
198
    await dispatch(fetchJob(jobId));
199
    dispatch(setSelectedJob(jobId));
200
  },
201
  loadTasks: async (jobId: number): Promise<void> => {
202
    await dispatch(fetchJobTasks(jobId));
203
  },
204
  loadCriteria: async (jobId: number): Promise<void> => {
205
    await dispatch(fetchCriteria(jobId));
206
  },
207
  loadSkills: async (): Promise<void> => {
208
    await dispatch(fetchSkills());
209
  },
210
  loadDepartments: async (): Promise<void> => {
211
    await dispatch(fetchDepartments());
212
  },
213
});
214
215
export const JobBuilderStepContainer = connect(
216
  mapStateToProps,
217
  mapDispatchToProps,
218
)(JobBuilderStep);
219
220
export default JobBuilderStepContainer;
221