Passed
Push — feature/application-urls ( 373c87...912c99 )
by Tristan
04:04
created

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

Complexity

Total Complexity 11
Complexity/F 0

Size

Lines of Code 165
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 11
eloc 132
mnd 11
bc 11
fnc 0
dl 0
loc 165
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import React, { useEffect } from "react";
2
import { useIntl } from "react-intl";
3
import { connect } from "react-redux";
4
import { Job, JobPosterKeyTask, Criteria } from "../../models/types";
5
import {
6
  criteriaProgressState,
7
  jobTasksProgressState,
8
  jobImpactProgressState,
9
  jobBuilderEnvProgressState,
10
  jobBuilderIntroProgressState,
11
  jobBuilderDetailsProgressState,
12
  JobBuilderPage,
13
  pageToUrlBuilder,
14
} from "./jobBuilderHelpers";
15
import { ProgressTrackerState } from "../ProgressTracker/types";
16
import { RootState } from "../../store/store";
17
import {
18
  getJob,
19
  getTasksByJob,
20
  getCriteriaByJob,
21
  getJobIsUpdating,
22
  getTasksForJobIsUpdating,
23
  getCriteriaForJobIsUpdating,
24
} from "../../store/Job/jobSelector";
25
import JobBuilderStepContainer from "./JobBuilderStep";
26
import { redirect } from "../../helpers/router";
27
28
interface RedirectToLastIncompleteStepProps {
29
  jobId: number;
30
  job: Job | null;
31
  jobIsLoading: boolean;
32
  tasks: JobPosterKeyTask[];
33
  tasksIsLoading: boolean;
34
  criteria: Criteria[];
35
  criteriaIsLoading: boolean;
36
  handleRedirect: (url: string) => void;
37
}
38
39
export type PageStates = {
40
  [page in JobBuilderPage]: () => ProgressTrackerState;
41
};
42
43
/**
44
 * Return the first "error" page (ie incomplete page) without any "null" pages (ie still loading) preceding it.
45
 * If loading pages precede any incomplete pages, return null.
46
 * @param pageStates
47
 * @param pageOrder
48
 */
49
export const firstIncompletePage = (
50
  pageStates: PageStates,
51
  pageOrder: JobBuilderPage[],
52
): JobBuilderPage | null => {
53
  if (pageOrder.length === 0) {
54
    return null;
55
  }
56
  const page = pageOrder[0];
57
  const state = pageStates[page]();
58
  if (state === "null") {
59
    return null;
60
  }
61
  if (state === "error") {
62
    return page;
63
  }
64
  return firstIncompletePage(pageStates, pageOrder.slice(1));
65
};
66
67
export const RedirectToLastIncompleteStep: React.FunctionComponent<RedirectToLastIncompleteStepProps> = ({
68
  jobId,
69
  job,
70
  jobIsLoading,
71
  tasks,
72
  tasksIsLoading,
73
  criteria,
74
  criteriaIsLoading,
75
  handleRedirect,
76
}): React.ReactElement | null => {
77
  const intl = useIntl();
78
79
  const { locale } = intl;
80
  if (locale !== "en" && locale !== "fr") {
81
    throw new Error("Unexpected locale");
82
  }
83
84
  const pageStates: PageStates = {
85
    intro: () =>
86
      jobIsLoading ? "null" : jobBuilderIntroProgressState(job, false),
87
    details: () =>
88
      jobIsLoading
89
        ? "null"
90
        : jobBuilderDetailsProgressState(job, locale, false),
91
    env: () =>
92
      jobIsLoading ? "null" : jobBuilderEnvProgressState(job, locale, false),
93
    impact: () =>
94
      jobIsLoading ? "null" : jobImpactProgressState(job, locale, false),
95
    tasks: () =>
96
      tasksIsLoading ? "null" : jobTasksProgressState(tasks, locale, false),
97
    skills: () =>
98
      criteriaIsLoading
99
        ? "null"
100
        : criteriaProgressState(criteria, locale, false),
101
    review: () => "error",
102
  };
103
  // This is necessary because browsers do not guarantee object attribute order will be maintained (such as in pageStates)
104
  const pageOrder: JobBuilderPage[] = [
105
    "intro",
106
    "details",
107
    "env",
108
    "impact",
109
    "tasks",
110
    "skills",
111
    "review",
112
  ];
113
114
  const returnToPage = firstIncompletePage(pageStates, pageOrder);
115
  useEffect((): void => {
116
    if (returnToPage) {
117
      const url = pageToUrlBuilder(returnToPage)(locale, jobId);
118
      handleRedirect(url);
119
    }
120
  }, [returnToPage, handleRedirect, locale, jobId]);
121
122
  return null;
123
};
124
125
const mapStateToProps = (
126
  state: RootState,
127
  { jobId }: { jobId: number },
128
): {
129
  job: Job | null;
130
  jobIsLoading: boolean;
131
  tasks: JobPosterKeyTask[];
132
  tasksIsLoading: boolean;
133
  criteria: Criteria[];
134
  criteriaIsLoading: boolean;
135
} => {
136
  const job = getJob(state, { jobId });
137
  // for isLoading args, check that job is not null, becuase job must have loaded at least once for us to trust results.
138
  return {
139
    job,
140
    jobIsLoading: getJobIsUpdating(state, jobId) || job === null,
141
    tasks: getTasksByJob(state, { jobId }),
142
    tasksIsLoading: getTasksForJobIsUpdating(state, jobId) || job === null,
143
    criteria: getCriteriaByJob(state, { jobId }),
144
    criteriaIsLoading:
145
      getCriteriaForJobIsUpdating(state, jobId) || job === null,
146
  };
147
};
148
149
export const RedirectToLastIncompleteStepConnected = connect(mapStateToProps)(
150
  RedirectToLastIncompleteStep,
151
);
152
153
export const RedirectPage: React.FC<{ jobId: number }> = ({ jobId }) => {
154
  return (
155
    <JobBuilderStepContainer jobId={jobId} currentPage={null} forceIsLoading>
156
      <RedirectToLastIncompleteStepConnected
157
        jobId={jobId}
158
        handleRedirect={redirect}
159
      />
160
    </JobBuilderStepContainer>
161
  );
162
};
163
164
export default RedirectPage;
165