Passed
Push — feature/update-managers-applca... ( 20b55d...5257cf )
by Yonathan
05:13 queued 13s
created

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

Complexity

Total Complexity 8
Complexity/F 0

Size

Lines of Code 160
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 8
eloc 88
dl 0
loc 160
rs 10
c 0
b 0
f 0
mnd 8
bc 8
fnc 0
bpm 0
cpm 0
noi 0
1
import {
2
  Criteria,
3
  ExperienceSkill,
4
  Skill,
5
  Experience,
6
} from "../../../models/types";
7
import { mapToObject, getId, hasKey } from "../../../helpers/queries";
8
import { IconStatus } from "../../StatusIcon";
9
10
/**
11
 * Retrieves Skill associated to Criterion by ID.
12
 *
13
 * @param criterion Criteria object.
14
 * @param skills Array of Skill objects.
15
 * @returns null if not found or Skill object.
16
 */
17
export const getSkillOfCriteria = (
18
  criterion: Criteria,
19
  skills: Skill[],
20
): Skill | null => {
21
  const skillsById = mapToObject(skills, getId);
22
  return hasKey(skillsById, criterion.skill_id)
23
    ? skillsById[criterion.skill_id]
24
    : null;
25
};
26
27
/**
28
 * Retrieves array of ExperienceSkill objects associated to
29
 * Skill by ID.
30
 * @param skill Skill object.
31
 * @param experiences Array of ExperienceSkill objects.
32
 * @returns Array of ExperienceSkill objects.
33
 */
34
export const getExperiencesOfSkill = (
35
  skill: Skill,
36
  experiences: ExperienceSkill[],
37
): ExperienceSkill[] =>
38
  experiences.filter((experience) => experience.skill_id === skill.id);
39
40
export interface SkillStatus {
41
  [skillId: string]: {
42
    experiences: {
43
      [experienceTypeAndId: string]: IconStatus;
44
    };
45
  };
46
}
47
48
/**
49
 * Retrieves Experience associated to ExperienceSkill by Type and ID.
50
 *
51
 * @param experienceSkill ExperienceSkill object.
52
 * @param experiences Array of ExperienceSkill objects.
53
 * @returns null if not found or Experience object.
54
 */
55
export const getExperienceOfExperienceSkills = (
56
  experienceSkill: ExperienceSkill,
57
  experiences: Experience[],
58
): Experience | null => {
59
  const experiencesByType = experiences.filter(
60
    (experience) => experience.type === experienceSkill.experience_type,
61
  );
62
  const experiencesById = mapToObject(experiencesByType, getId);
63
  return hasKey(experiencesById, experienceSkill.experience_id)
64
    ? experiencesById[experienceSkill.experience_id]
65
    : null;
66
};
67
68
/**
69
 * Constructs an initial state object to pass to the statusReducer function.
70
 * Accepts an array of experiences and creates an object of shape SkillStatus.
71
 *
72
 * @param experiences Array of ExperienceSkill.
73
 * @returns SkillStatus.
74
 */
75
export const initialStatus = (experiences: ExperienceSkill[]): SkillStatus =>
76
  experiences.reduce((status, experience: ExperienceSkill) => {
77
    if (!status[experience.skill_id]) {
78
      status[experience.skill_id] = {
79
        experiences: {
80
          [`${experience.experience_type}_${experience.experience_id}`]: IconStatus.DEFAULT,
81
        },
82
      };
83
    }
84
85
    status[experience.skill_id].experiences[
86
      `${experience.experience_type}_${experience.experience_id}`
87
    ] = IconStatus.DEFAULT;
88
    return status;
89
  }, {});
90
91
/**
92
 * Return the IconStatus for the Skill based on the defined statuses of all
93
 * the nested experiences. The possibilities are COMPLETE, ERROR and DEFAULT.
94
 * If one experience has an ERROR, that will be returned. If all experiences are
95
 * COMPLETE, that will be returned. Otherwise, DEFAULT will be returned.
96
 *
97
 * @param statusShape Provided state object containing all statuses for all skills.
98
 * @param skillId Skill ID for the particular skill to check.
99
 *
100
 * @returns IconStatus
101
 */
102
export const computeParentStatus = (
103
  statusShape: SkillStatus,
104
  skillId: number,
105
): IconStatus => {
106
  if (statusShape[skillId]) {
107
    const skillStatus = statusShape[skillId];
108
    let errorCount = 0;
109
    let completeCount = 0;
110
    Object.values(skillStatus.experiences).forEach((experience) => {
111
      if (experience === IconStatus.ERROR) {
112
        errorCount += 1;
113
      }
114
      if (experience === IconStatus.COMPLETE) {
115
        completeCount += 1;
116
      }
117
    });
118
119
    if (errorCount > 0) {
120
      return IconStatus.ERROR;
121
    }
122
    if (completeCount === Object.values(skillStatus.experiences).length) {
123
      return IconStatus.COMPLETE;
124
    }
125
  }
126
127
  return IconStatus.DEFAULT;
128
};
129
130
/**
131
 * Updates a slice of the status state based on provided payload.
132
 * Requires the Skill ID, Experience ID, and new status.
133
 *
134
 * @param state SkillStatus state object.
135
 * @param action Object with a payload of skillId, experienceId, and status.
136
 * @returns SkillStatus.
137
 */
138
export const statusReducer = (
139
  state: SkillStatus,
140
  action: {
141
    payload: {
142
      skillId: number;
143
      experienceId: number;
144
      experienceType: string;
145
      status: IconStatus;
146
    };
147
  },
148
): SkillStatus => {
149
  return {
150
    ...state,
151
    [action.payload.skillId]: {
152
      experiences: {
153
        ...state[action.payload.skillId].experiences,
154
        [`${action.payload.experienceType}_${action.payload.experienceId}`]: action
155
          .payload.status,
156
      },
157
    },
158
  };
159
};
160