Passed
Push — feature/connect-application-st... ( a6f23b...9d3dc6 )
by Chris
04:02
created

resources/assets/js/store/RatingGuideQuestion/ratingGuideQuestionSelectors.ts   A

Complexity

Total Complexity 11
Complexity/F 0

Size

Lines of Code 263
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 229
dl 0
loc 263
rs 10
c 0
b 0
f 0
wmc 11
mnd 11
bc 11
fnc 0
bpm 0
cpm 0
noi 0
1
import isEqual from "lodash/isEqual";
2
import { createSelector } from "reselect";
3
import createCachedSelector from "re-reselect";
4
import { RootState } from "../store";
5
import { RatingGuideQuestionState } from "./ratingGuideQuestionReducer";
6
import { RatingGuideQuestion } from "../../models/types";
7
import { getId, hasKey, mapToObjectTrans } from "../../helpers/queries";
8
import { deepEqualSelectorOptions } from "../cachedSelectors";
9
10
const stateSlice = (state: RootState): RatingGuideQuestionState =>
11
  state.ratingGuideQuestion;
12
13
const getCanonQuestionState = (
14
  state: RootState,
15
): { [id: number]: RatingGuideQuestion } =>
16
  stateSlice(state).ratingGuideQuestions;
17
18
const getTempQuestionState = (
19
  state: RootState,
20
): { [id: number]: RatingGuideQuestion } =>
21
  stateSlice(state).tempRatingGuideQuestions;
22
23
const getEditedQuestionState = (
24
  state: RootState,
25
): { [id: number]: RatingGuideQuestion } =>
26
  stateSlice(state).editedRatingGuideQuestions;
27
28
const getQuestionDeletes = (state: RootState): { [id: number]: number } =>
29
  stateSlice(state).ratingGuideQuestionDeletes;
30
31
const getTempQuestionSaving = (state: RootState): { [id: number]: boolean } =>
32
  stateSlice(state).tempRatingGuideQuestionSaving;
33
34
const getQuestionUpdates = (state: RootState): { [id: number]: number } =>
35
  stateSlice(state).ratingGuideQuestionUpdates;
36
37
const getCurrentQuestionState = createSelector(
38
  getCanonQuestionState,
39
  getEditedQuestionState,
40
  (canonQuestions, editedQuestions): { [id: number]: RatingGuideQuestion } => ({
41
    ...canonQuestions,
42
    ...editedQuestions,
43
  }),
44
);
45
46
/**
47
 * Returns current verisons of all assessments.
48
 * ie edited version if possible,
49
 * and not including those undergoing delete requests
50
 */
51
export const getCurrentRatingGuideQuestions = createSelector(
52
  [getCanonQuestionState, getEditedQuestionState, getQuestionDeletes],
53
  (questionState, editedQuestionState, deleteCount): RatingGuideQuestion[] => {
54
    const currentRatingGuideQuestions = {
55
      ...questionState,
56
      ...editedQuestionState,
57
    };
58
    return Object.values(currentRatingGuideQuestions).filter(
59
      (ratingGuideQuestion): boolean =>
60
        !hasKey(deleteCount, ratingGuideQuestion.id) ||
61
        deleteCount[ratingGuideQuestion.id] <= 0,
62
    );
63
  },
64
);
65
66
export const getTempRatingGuideQuestions = createSelector(
67
  getTempQuestionState,
68
  (tempQuestionState): RatingGuideQuestion[] =>
69
    Object.values(tempQuestionState),
70
);
71
72
export const getRatingGuideQuestionIds = createSelector(
73
  getCurrentQuestionState,
74
  (currentQuestions): number[] =>
75
    Object.keys(currentQuestions).map(id => Number(id)),
76
);
77
78
export const getTempRatingGuideQuestionIds = createSelector(
79
  getTempQuestionState,
80
  (tempQuestions): number[] => Object.keys(tempQuestions).map(id => Number(id)),
81
);
82
83
/**
84
 * Returns edited version, if available
85
 * */
86
export const getCurrentRatingGuideQuestionById = createSelector(
87
  getCurrentQuestionState,
88
  (state: RootState, id: number) => id,
89
  (questions, id): RatingGuideQuestion | null =>
90
    hasKey(questions, id) ? questions[id] : null,
91
);
92
93
export const getCanonRatingGuideQuestionById = createCachedSelector(
94
  getCanonQuestionState,
95
  (state: RootState, id: number): number => id,
96
  (questions, id): RatingGuideQuestion | null =>
97
    hasKey(questions, id) ? questions[id] : null,
98
)((state, id) => id);
99
100
export const getEditRatingGuideQuestionById = createCachedSelector(
101
  getEditedQuestionState,
102
  (state: RootState, id: number): number => id,
103
  (questions, id): RatingGuideQuestion | null =>
104
    hasKey(questions, id) ? questions[id] : null,
105
)((state, id) => id);
106
107
export const getTempRatingGuideQuestionById = createCachedSelector(
108
  getTempQuestionState,
109
  (state: RootState, id: number): number => id,
110
  (questions, id): RatingGuideQuestion | null =>
111
    hasKey(questions, id) ? questions[id] : null,
112
)((state, id) => id);
113
114
export const getRatingGuideQuestionsByJob = createCachedSelector(
115
  getCurrentRatingGuideQuestions,
116
  (state: RootState, props: { jobId: number }): number => props.jobId,
117
  (questions, jobId): RatingGuideQuestion[] =>
118
    questions.filter((question): boolean => question.job_poster_id === jobId),
119
)((state, props): number => props.jobId);
120
121
export const getRatingGuideQuestionIdsByJob = createCachedSelector(
122
  getRatingGuideQuestionsByJob,
123
  (questions): number[] => questions.map(getId),
124
)((state, props): number => props.jobId);
125
126
export const getRatingGuideQuestionsByJobAndAssessmentType = createCachedSelector(
127
  getCurrentRatingGuideQuestions,
128
  (
129
    state: RootState,
130
    props: { jobId: number; assessmentTypeId: number },
131
  ): number => props.jobId,
132
  (
133
    state: RootState,
134
    props: { jobId: number; assessmentTypeId: number },
135
  ): number => props.assessmentTypeId,
136
  (questions, jobId, assessmentTypeId): RatingGuideQuestion[] =>
137
    questions.filter(
138
      (question): boolean =>
139
        question.job_poster_id === jobId &&
140
        question.assessment_type_id === assessmentTypeId,
141
    ),
142
)((state, props): string => `${props.jobId} ${props.assessmentTypeId}`);
143
144
export const getRatingGuideQuestionIdsByJobAndAssessmentType = createCachedSelector(
145
  (
146
    state: RootState,
147
    props: { jobId: number; assessmentTypeId: number },
148
  ): number[] =>
149
    getRatingGuideQuestionsByJobAndAssessmentType(state, props).map(getId),
150
  (questionsIds): number[] => questionsIds,
151
)(
152
  (state, props): string => `${props.jobId} ${props.assessmentTypeId}`,
153
  deepEqualSelectorOptions,
154
);
155
156
export const getTempRatingGuideQuestionsByAssessment = createCachedSelector(
157
  getTempRatingGuideQuestions,
158
  (
159
    state: RootState,
160
    props: { jobId: number; assessmentTypeId: number },
161
  ): number => props.jobId,
162
  (
163
    state: RootState,
164
    props: { jobId: number; assessmentTypeId: number },
165
  ): number => props.assessmentTypeId,
166
  (questions, jobId, assessmentTypeId): RatingGuideQuestion[] =>
167
    questions.filter(
168
      (question): boolean =>
169
        question.job_poster_id === jobId &&
170
        question.assessment_type_id === assessmentTypeId,
171
    ),
172
)((state, props): string => `${props.jobId} ${props.assessmentTypeId}`);
173
174
export const getTempRatingGuideQuestionIdsByAssessment = createCachedSelector(
175
  (
176
    state: RootState,
177
    props: { jobId: number; assessmentTypeId: number },
178
  ): number[] =>
179
    getTempRatingGuideQuestionsByAssessment(state, props).map(getId),
180
  (questionsIds): number[] => questionsIds,
181
)(
182
  (state, props): string => `${props.jobId} ${props.assessmentTypeId}`,
183
  deepEqualSelectorOptions,
184
);
185
186
// TODO: test that this works like I think it does -- Tristan
187
/** Returns true if there is an edited verision which differs from canonical version */
188
export const ratingGuideQuestionIsEdited = createCachedSelector(
189
  getCanonRatingGuideQuestionById,
190
  getEditRatingGuideQuestionById,
191
  (canon, edited): boolean => {
192
    if (canon === null) {
193
      return true;
194
    }
195
    return edited !== null && !isEqual(edited, canon);
196
  },
197
)((state: RootState, id: number): number => id);
198
199
export const ratingGuideQuestionsAreEditedByAssessment = createCachedSelector(
200
  getRatingGuideQuestionIdsByJobAndAssessmentType,
201
  getCanonQuestionState,
202
  getEditedQuestionState,
203
  (questionIds, canonState, editedState): { [id: number]: boolean } =>
204
    mapToObjectTrans(
205
      questionIds,
206
      (id): number => id,
207
      (questionId): boolean => {
208
        const canon = hasKey(canonState, questionId)
209
          ? canonState[questionId]
210
          : null;
211
        const edited = hasKey(editedState, questionId)
212
          ? editedState[questionId]
213
          : null;
214
        return edited !== null && !isEqual(edited, canon);
215
      },
216
    ),
217
)((state, props): string => `${props.jobId}:${props.assessmentTypeId}`);
218
219
export const tempRatingGuideQuestionIsSaving = createCachedSelector(
220
  getTempQuestionSaving,
221
  (state: RootState, id: number): number => id,
222
  (tempSaving, id): boolean =>
223
    hasKey(tempSaving, id) ? tempSaving[id] : false,
224
)((state: RootState, id: number): number => id);
225
226
export const tempRatingGuideQuestionsAreSavingByAssessment = createCachedSelector(
227
  getTempRatingGuideQuestionIdsByAssessment,
228
  getTempQuestionSaving,
229
  (questionIds, savingState): { [id: number]: boolean } =>
230
    mapToObjectTrans(
231
      questionIds,
232
      (id): number => id,
233
      (questionId): boolean =>
234
        hasKey(savingState, questionId) ? savingState[questionId] : false,
235
    ),
236
)((state, props): string => `${props.jobId}:${props.assessmentTypeId}`);
237
238
export const ratingGuideQuestionIsUpdating = createCachedSelector(
239
  getQuestionUpdates,
240
  (state: RootState, id: number): number => id,
241
  (updateCounts, id): boolean =>
242
    hasKey(updateCounts, id) ? updateCounts[id] > 0 : false,
243
)((state: RootState, id: number): number => id);
244
245
export const ratingGuideQuestionsAreUpdatingByAssessment = createCachedSelector(
246
  getRatingGuideQuestionIdsByJobAndAssessmentType,
247
  getQuestionUpdates,
248
  (questionIds, updateCounts): { [id: number]: boolean } =>
249
    questionIds.reduce(
250
      (
251
        result: { [id: number]: boolean },
252
        questionId: number,
253
      ): { [id: number]: boolean } => {
254
        // eslint-disable-next-line no-param-reassign
255
        result[questionId] = hasKey(updateCounts, questionId)
256
          ? updateCounts[questionId] > 0
257
          : false;
258
        return result;
259
      },
260
      {},
261
    ),
262
)((state, jobId, assessmentTypeId): string => `${jobId} ${assessmentTypeId}`);
263