Passed
Push — feature/timeline-experience-st... ( 77886a...661d64 )
by Yonathan
04:01
created

resources/assets/js/components/JobBuilder/Intro/JobIntroPage.tsx   A

Complexity

Total Complexity 19
Complexity/F 0

Size

Lines of Code 268
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 19
eloc 219
dl 0
loc 268
rs 10
c 0
b 0
f 0
mnd 19
bc 19
fnc 0
bpm 0
cpm 0
noi 0
1
import React, { useEffect } from "react";
2
import nprogress from "nprogress";
3
import { FormattedMessage, useIntl } from "react-intl";
4
import { connect } from "react-redux";
5
import ReactDOM from "react-dom";
6
import JobIntro from "./JobIntro";
7
import {
8
  Job,
9
  Department,
10
  JobPosterKeyTask,
11
  Criteria,
12
  Manager,
13
  User,
14
} from "../../../models/types";
15
import { RootState } from "../../../store/store";
16
import {
17
  getSelectedJob,
18
  getTasksByJob,
19
  getCriteriaByJob,
20
} from "../../../store/Job/jobSelector";
21
import { DispatchType } from "../../../configureStore";
22
import {
23
  setSelectedJob,
24
  createJob,
25
  updateJob,
26
} from "../../../store/Job/jobActions";
27
import RootContainer from "../../RootContainer";
28
import { jobBuilderDetails } from "../../../helpers/routes";
29
import JobBuilderStepContainer from "../JobBuilderStep";
30
import { getDepartments } from "../../../store/Department/deptSelector";
31
import { navigate } from "../../../helpers/router";
32
import { getSelectedManager } from "../../../store/Manager/managerSelector";
33
import {
34
  updateManager,
35
  setSelectedManager,
36
  fetchManager,
37
  fetchCurrentManager,
38
} from "../../../store/Manager/managerActions";
39
import { getUserById } from "../../../store/User/userSelector";
40
import { fetchUser } from "../../../store/User/userActions";
41
import { getLocale } from "../../../helpers/localize";
42
43
interface JobIntroPageProps {
44
  // The id of the edited job, or null for a new job.
45
  jobId: number | null;
46
  // List of known department options.
47
  departments: Department[];
48
  // If not null, used to prepopulate form values.
49
  // Note: its possible for jobId to be non-null, but job to be null, if the data hasn't been loaded yet.
50
  job: Job | null;
51
  // The manager of this job.
52
  manager: Manager | null;
53
  // The user associated with the manager.
54
  user: User | null;
55
  // Creates a new job. Must return the new job if successful.
56
  handleCreateJob: (newJob: Job) => Promise<Job>;
57
  // Updates an existing job. Must return the updated job if successful.
58
  handleUpdateJob: (newJob: Job) => Promise<Job>;
59
  // Updates an existing Manager. Must return the updated manager if successful.
60
  handleUpdateManager: (manager: Manager) => Promise<Manager>;
61
  // Load a manager with a particular id.
62
  loadManager: (managerId: number) => Promise<void>;
63
  // Load the manager profile of the current authenticated user.
64
  loadCurrentManager: () => Promise<void>;
65
  handleFetchUser: (userId: number) => Promise<void>;
66
}
67
68
const JobIntroPage: React.FunctionComponent<JobIntroPageProps> = ({
69
  jobId,
70
  manager,
71
  user,
72
  job,
73
  departments,
74
  handleCreateJob,
75
  handleUpdateJob,
76
  handleUpdateManager,
77
  loadManager,
78
  loadCurrentManager,
79
  handleFetchUser,
80
}): React.ReactElement => {
81
  const intl = useIntl();
82
  const locale = getLocale(intl.locale);
83
  useEffect((): void => {
84
    if (manager === null) {
85
      nprogress.start();
86
      if (jobId === null) {
87
        loadCurrentManager();
88
      }
89
      if (job !== null) {
90
        loadManager(job.manager_id);
91
      }
92
    } else {
93
      nprogress.done();
94
    }
95
  }, [manager, jobId, job, loadCurrentManager, loadManager]);
96
97
  // Load user after Manager has loaded
98
  // eslint-disable-next-line camelcase
99
  const userId = manager?.user_id;
100
  useEffect((): void => {
101
    if (userId) {
102
      handleFetchUser(userId);
103
    }
104
  }, [handleFetchUser, userId]);
105
106
  const submitJob = job ? handleUpdateJob : handleCreateJob;
107
  const handleSubmit = async (
108
    updatedJob: Job,
109
    updatedManager: Manager,
110
  ): Promise<Job> => {
111
    const jobPromise = submitJob(updatedJob);
112
    await handleUpdateManager(updatedManager);
113
    return jobPromise;
114
  };
115
116
  const handleContinue = (chosenLang: string, newJob: Job): void => {
117
    if (locale === chosenLang) {
118
      navigate(jobBuilderDetails(chosenLang, newJob.id));
119
    } else {
120
      window.location.href = `${jobBuilderDetails(chosenLang, newJob.id)}`;
121
    }
122
  };
123
124
  return (
125
    <JobBuilderStepContainer jobId={jobId} currentPage="intro">
126
      {/** Show the form when the existing job has loaded, or if this is a new job */}
127
      {manager !== null && user !== null && (job !== null || jobId === null) ? (
128
        <JobIntro
129
          job={job}
130
          manager={manager}
131
          user={user}
132
          departments={departments}
133
          handleSubmit={handleSubmit}
134
          handleContinue={handleContinue}
135
        />
136
      ) : (
137
        <div
138
          data-c-container="form"
139
          data-c-padding="top(triple) bottom(triple)"
140
        >
141
          <div
142
            data-c-background="white(100)"
143
            data-c-card
144
            data-c-padding="all(double)"
145
            data-c-radius="rounded"
146
            data-c-align="base(centre)"
147
          >
148
            <p>
149
              <FormattedMessage
150
                id="jobBuilder.intro.managerLoading"
151
                defaultMessage="Your Manager Profile is loading..."
152
                description="Message indicating that the manager profile is still being loaded."
153
              />
154
            </p>
155
          </div>
156
        </div>
157
      )}
158
    </JobBuilderStepContainer>
159
  );
160
};
161
162
const mapStateToProps = (
163
  state: RootState,
164
  { jobId }: { jobId: number | null },
165
): {
166
  job: Job | null;
167
  manager: Manager | null;
168
  departments: Department[];
169
  user: User | null;
170
  keyTasks: JobPosterKeyTask[];
171
  criteria: Criteria[];
172
} => ({
173
  job: getSelectedJob(state),
174
  manager: getSelectedManager(state),
175
  departments: getDepartments(state),
176
  user: getUserById(state, {
177
    // eslint-disable-next-line camelcase, @typescript-eslint/camelcase
178
    userId: getSelectedManager(state)?.user_id || 0,
179
  }),
180
  keyTasks: jobId !== null ? getTasksByJob(state, { jobId }) : [],
181
  criteria: jobId !== null ? getCriteriaByJob(state, { jobId }) : [],
182
});
183
184
const mapDispatchToProps = (
185
  dispatch: DispatchType,
186
): {
187
  handleCreateJob: (newJob: Job) => Promise<Job>;
188
  handleUpdateJob: (newJob: Job) => Promise<Job>;
189
  handleUpdateManager: (newManager: Manager) => Promise<Manager>;
190
  loadManager: (id: number) => Promise<void>;
191
  loadCurrentManager: () => Promise<void>;
192
  handleFetchUser: (userId: number) => Promise<void>;
193
} => ({
194
  handleCreateJob: async (newJob: Job): Promise<Job> => {
195
    const result = await dispatch(createJob(newJob));
196
    if (!result.error) {
197
      const resultJob = await result.payload;
198
      dispatch(setSelectedJob(resultJob.id));
199
      return resultJob;
200
    }
201
    return Promise.reject(result.payload);
202
  },
203
  handleUpdateJob: async (newJob: Job): Promise<Job> => {
204
    const result = await dispatch(updateJob(newJob));
205
    if (!result.error) {
206
      const resultJob = await result.payload;
207
      return resultJob;
208
    }
209
    return Promise.reject(result.payload);
210
  },
211
  handleUpdateManager: async (newManager: Manager): Promise<Manager> => {
212
    const result = await dispatch(updateManager(newManager));
213
    if (!result.error) {
214
      const resultManager = await result.payload;
215
      dispatch(setSelectedManager(resultManager.id));
216
      return resultManager;
217
    }
218
    return Promise.reject(result.payload);
219
  },
220
  loadManager: async (id: number): Promise<void> => {
221
    const result = await dispatch(fetchManager(id));
222
    if (!result.error) {
223
      const resultManager = await result.payload;
224
      dispatch(setSelectedManager(resultManager.id));
225
      return Promise.resolve();
226
    }
227
    return Promise.reject(result.error);
228
  },
229
  loadCurrentManager: async (): Promise<void> => {
230
    const result = await dispatch(fetchCurrentManager());
231
    if (!result.error) {
232
      const resultManager = await result.payload;
233
      dispatch(setSelectedManager(resultManager.id));
234
      return Promise.resolve();
235
    }
236
    return Promise.reject(result.error);
237
  },
238
  handleFetchUser: async (userId: number): Promise<void> => {
239
    const result = await dispatch(fetchUser(userId));
240
    if (!result.error) {
241
      return Promise.resolve();
242
    }
243
    return Promise.reject(result.error);
244
  },
245
});
246
247
const JobIntroPageContainer = connect(
248
  mapStateToProps,
249
  mapDispatchToProps,
250
)(JobIntroPage);
251
252
if (document.getElementById("job-builder-intro")) {
253
  const container: HTMLElement = document.getElementById(
254
    "job-builder-intro",
255
  ) as HTMLElement;
256
  const jobIdAttr = container.getAttribute("data-job-id");
257
  const jobId = jobIdAttr ? Number(jobIdAttr) : null;
258
259
  ReactDOM.render(
260
    <RootContainer>
261
      <JobIntroPageContainer jobId={jobId} />
262
    </RootContainer>,
263
    container,
264
  );
265
}
266
267
export default JobIntroPageContainer;
268