Passed
Push — feature/timeline ( d18b1c...ddfcdc )
by Yonathan
06:15 queued 11s
created

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

Complexity

Total Complexity 19
Complexity/F 0

Size

Lines of Code 272
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 19
eloc 222
mnd 19
bc 19
fnc 0
dl 0
loc 272
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 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
      const baseUrl = window.location.origin;
121
      window.location.href = `${baseUrl}${jobBuilderDetails(
122
        chosenLang,
123
        newJob.id,
124
      )}`;
125
    }
126
  };
127
128
  return (
129
    <JobBuilderStepContainer jobId={jobId} currentPage="intro">
130
      {/** Show the form when the existing job has loaded, or if this is a new job */}
131
      {manager !== null && user !== null && (job !== null || jobId === null) ? (
132
        <JobIntro
133
          job={job}
134
          manager={manager}
135
          user={user}
136
          departments={departments}
137
          handleSubmit={handleSubmit}
138
          handleContinue={handleContinue}
139
        />
140
      ) : (
141
        <div
142
          data-c-container="form"
143
          data-c-padding="top(triple) bottom(triple)"
144
        >
145
          <div
146
            data-c-background="white(100)"
147
            data-c-card
148
            data-c-padding="all(double)"
149
            data-c-radius="rounded"
150
            data-c-align="base(centre)"
151
          >
152
            <p>
153
              <FormattedMessage
154
                id="jobBuilder.intro.managerLoading"
155
                defaultMessage="Your Manager Profile is loading..."
156
                description="Message indicating that the manager profile is still being loaded."
157
              />
158
            </p>
159
          </div>
160
        </div>
161
      )}
162
    </JobBuilderStepContainer>
163
  );
164
};
165
166
const mapStateToProps = (
167
  state: RootState,
168
  { jobId }: { jobId: number | null },
169
): {
170
  job: Job | null;
171
  manager: Manager | null;
172
  departments: Department[];
173
  user: User | null;
174
  keyTasks: JobPosterKeyTask[];
175
  criteria: Criteria[];
176
} => ({
177
  job: getSelectedJob(state),
178
  manager: getSelectedManager(state),
179
  departments: getDepartments(state),
180
  user: getUserById(state, {
181
    // eslint-disable-next-line camelcase, @typescript-eslint/camelcase
182
    userId: getSelectedManager(state)?.user_id || 0,
183
  }),
184
  keyTasks: jobId !== null ? getTasksByJob(state, { jobId }) : [],
185
  criteria: jobId !== null ? getCriteriaByJob(state, { jobId }) : [],
186
});
187
188
const mapDispatchToProps = (
189
  dispatch: DispatchType,
190
): {
191
  handleCreateJob: (newJob: Job) => Promise<Job>;
192
  handleUpdateJob: (newJob: Job) => Promise<Job>;
193
  handleUpdateManager: (newManager: Manager) => Promise<Manager>;
194
  loadManager: (id: number) => Promise<void>;
195
  loadCurrentManager: () => Promise<void>;
196
  handleFetchUser: (userId: number) => Promise<void>;
197
} => ({
198
  handleCreateJob: async (newJob: Job): Promise<Job> => {
199
    const result = await dispatch(createJob(newJob));
200
    if (!result.error) {
201
      const resultJob = await result.payload;
202
      dispatch(setSelectedJob(resultJob.id));
203
      return resultJob;
204
    }
205
    return Promise.reject(result.payload);
206
  },
207
  handleUpdateJob: async (newJob: Job): Promise<Job> => {
208
    const result = await dispatch(updateJob(newJob));
209
    if (!result.error) {
210
      const resultJob = await result.payload;
211
      return resultJob;
212
    }
213
    return Promise.reject(result.payload);
214
  },
215
  handleUpdateManager: async (newManager: Manager): Promise<Manager> => {
216
    const result = await dispatch(updateManager(newManager));
217
    if (!result.error) {
218
      const resultManager = await result.payload;
219
      dispatch(setSelectedManager(resultManager.id));
220
      return resultManager;
221
    }
222
    return Promise.reject(result.payload);
223
  },
224
  loadManager: async (id: number): Promise<void> => {
225
    const result = await dispatch(fetchManager(id));
226
    if (!result.error) {
227
      const resultManager = await result.payload;
228
      dispatch(setSelectedManager(resultManager.id));
229
      return Promise.resolve();
230
    }
231
    return Promise.reject(result.error);
232
  },
233
  loadCurrentManager: async (): Promise<void> => {
234
    const result = await dispatch(fetchCurrentManager());
235
    if (!result.error) {
236
      const resultManager = await result.payload;
237
      dispatch(setSelectedManager(resultManager.id));
238
      return Promise.resolve();
239
    }
240
    return Promise.reject(result.error);
241
  },
242
  handleFetchUser: async (userId: number): Promise<void> => {
243
    const result = await dispatch(fetchUser(userId));
244
    if (!result.error) {
245
      return Promise.resolve();
246
    }
247
    return Promise.reject(result.error);
248
  },
249
});
250
251
const JobIntroPageContainer = connect(
252
  mapStateToProps,
253
  mapDispatchToProps,
254
)(JobIntroPage);
255
256
if (document.getElementById("job-builder-intro")) {
257
  const container: HTMLElement = document.getElementById(
258
    "job-builder-intro",
259
  ) as HTMLElement;
260
  const jobIdAttr = container.getAttribute("data-job-id");
261
  const jobId = jobIdAttr ? Number(jobIdAttr) : null;
262
263
  ReactDOM.render(
264
    <RootContainer>
265
      <JobIntroPageContainer jobId={jobId} />
266
    </RootContainer>,
267
    container,
268
  );
269
}
270
271
export default JobIntroPageContainer;
272