Passed
Push — dev ( c71d4d...da46c8 )
by
unknown
05:00
created

resources/assets/js/components/Application/Skills/skillsHelpers.ts   A

Complexity

Total Complexity 11
Complexity/F 0

Size

Lines of Code 131
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 11
eloc 78
mnd 11
bc 11
fnc 0
dl 0
loc 131
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import { ExperienceSkill } from "../../../models/types";
2
import { IconStatus } from "../../StatusIcon";
3
import { hasKey } from "../../../helpers/queries";
4
import { countNumberOfWords } from "../../WordCounter/helpers";
5
6
export interface SkillStatus {
7
  [skillId: string]: {
8
    experiences: {
9
      [experienceTypeAndId: string]: IconStatus;
10
    };
11
  };
12
}
13
14
/**
15
 * Constructs an initial state object to pass to the statusReducer function.
16
 * Accepts an array of experiences and creates an object of shape SkillStatus.
17
 *
18
 * @param experiences Array of ExperienceSkill.
19
 * @param wordLimit Maximum number of words allowed in an ExperienceSkill justification.
20
 * @returns SkillStatus.
21
 */
22
export const initialStatus = (
23
  experiences: ExperienceSkill[],
24
  wordLimit: number,
25
): SkillStatus =>
26
  experiences.reduce(
27
    (status: SkillStatus, experience: ExperienceSkill): SkillStatus => {
28
      if (!hasKey(status, experience.skill_id)) {
29
        status[experience.skill_id] = {
30
          experiences: {},
31
        };
32
      }
33
      // If justification is in the required range, mark it complete.
34
      // If justification is null, an empty string, or too long, mark it an error.
35
      const expSkillStatus =
36
        experience.justification !== null &&
37
        experience.justification.length > 0 &&
38
        countNumberOfWords(experience.justification) <= wordLimit
39
          ? IconStatus.COMPLETE
40
          : IconStatus.ERROR;
41
      status[experience.skill_id].experiences[
42
        `${experience.experience_type}_${experience.experience_id}`
43
      ] = expSkillStatus;
44
      return status;
45
    },
46
    {},
47
  );
48
49
/**
50
 * Return the IconStatus for the Skill based on the defined statuses of all
51
 * the nested experiences. The possibilities are COMPLETE, ERROR and DEFAULT.
52
 * If one experience has an ERROR, that will be returned. If all experiences are
53
 * COMPLETE, that will be returned. Otherwise, DEFAULT will be returned.
54
 *
55
 * @param statusShape Provided state object containing all statuses for all skills.
56
 * @param skillId Skill ID for the particular skill to check.
57
 *
58
 * @returns IconStatus
59
 */
60
export const computeParentStatus = (
61
  statusShape: SkillStatus,
62
  skillId: number,
63
): IconStatus => {
64
  if (statusShape[skillId]) {
65
    const skillStatus = statusShape[skillId];
66
    let errorCount = 0;
67
    let completeCount = 0;
68
    Object.values(skillStatus.experiences).forEach((experience) => {
69
      if (experience === IconStatus.ERROR) {
70
        errorCount += 1;
71
      }
72
      if (experience === IconStatus.COMPLETE) {
73
        completeCount += 1;
74
      }
75
    });
76
77
    if (errorCount > 0) {
78
      return IconStatus.ERROR;
79
    }
80
    if (completeCount === Object.values(skillStatus.experiences).length) {
81
      return IconStatus.COMPLETE;
82
    }
83
  }
84
85
  return IconStatus.DEFAULT;
86
};
87
88
/** Get current experienceSkill status if it's stored in status store, or return DEFAULT. */
89
export const computeExperienceStatus = (
90
  statusShape: SkillStatus,
91
  experienceSkill: ExperienceSkill,
92
): IconStatus => {
93
  const skillId = experienceSkill.skill_id;
94
  const experienceKey = `${experienceSkill.experience_type}_${experienceSkill.experience_id}`;
95
  return hasKey(statusShape, skillId) &&
96
    hasKey(statusShape[skillId].experiences, experienceKey)
97
    ? statusShape[skillId].experiences[experienceKey]
98
    : IconStatus.DEFAULT;
99
};
100
101
/**
102
 * Updates a slice of the status state based on provided payload.
103
 * Requires the Skill ID, Experience ID, and new status.
104
 *
105
 * @param state SkillStatus state object.
106
 * @param action Object with a payload of skillId, experienceId, and status.
107
 * @returns SkillStatus.
108
 */
109
export const statusReducer = (
110
  state: SkillStatus,
111
  action: {
112
    payload: {
113
      skillId: number;
114
      experienceId: number;
115
      experienceType: string;
116
      status: IconStatus;
117
    };
118
  },
119
): SkillStatus => {
120
  return {
121
    ...state,
122
    [action.payload.skillId]: {
123
      experiences: {
124
        ...(state[action.payload.skillId]?.experiences ?? {}),
125
        [`${action.payload.experienceType}_${action.payload.experienceId}`]: action
126
          .payload.status,
127
      },
128
    },
129
  };
130
};
131