Passed
Push — feature/application-review-ui ( afbf92...be3688 )
by Chris
04:21
created

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

Complexity

Total Complexity 6
Complexity/F 0

Size

Lines of Code 104
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 6
eloc 56
mnd 6
bc 6
fnc 0
dl 0
loc 104
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
4
export interface SkillStatus {
5
  [skillId: string]: {
6
    experiences: {
7
      [experienceTypeAndId: string]: IconStatus;
8
    };
9
  };
10
}
11
12
/**
13
 * Constructs an initial state object to pass to the statusReducer function.
14
 * Accepts an array of experiences and creates an object of shape SkillStatus.
15
 *
16
 * @param experiences Array of ExperienceSkill.
17
 * @returns SkillStatus.
18
 */
19
export const initialStatus = (experiences: ExperienceSkill[]): SkillStatus =>
20
  experiences.reduce((status, experience: ExperienceSkill) => {
21
    if (!status[experience.skill_id]) {
22
      status[experience.skill_id] = {
23
        experiences: {
24
          [`${experience.experience_type}_${experience.experience_id}`]: IconStatus.DEFAULT,
25
        },
26
      };
27
    }
28
29
    status[experience.skill_id].experiences[
30
      `${experience.experience_type}_${experience.experience_id}`
31
    ] = IconStatus.DEFAULT;
32
    return status;
33
  }, {});
34
35
/**
36
 * Return the IconStatus for the Skill based on the defined statuses of all
37
 * the nested experiences. The possibilities are COMPLETE, ERROR and DEFAULT.
38
 * If one experience has an ERROR, that will be returned. If all experiences are
39
 * COMPLETE, that will be returned. Otherwise, DEFAULT will be returned.
40
 *
41
 * @param statusShape Provided state object containing all statuses for all skills.
42
 * @param skillId Skill ID for the particular skill to check.
43
 *
44
 * @returns IconStatus
45
 */
46
export const computeParentStatus = (
47
  statusShape: SkillStatus,
48
  skillId: number,
49
): IconStatus => {
50
  if (statusShape[skillId]) {
51
    const skillStatus = statusShape[skillId];
52
    let errorCount = 0;
53
    let completeCount = 0;
54
    Object.values(skillStatus.experiences).forEach((experience) => {
55
      if (experience === IconStatus.ERROR) {
56
        errorCount += 1;
57
      }
58
      if (experience === IconStatus.COMPLETE) {
59
        completeCount += 1;
60
      }
61
    });
62
63
    if (errorCount > 0) {
64
      return IconStatus.ERROR;
65
    }
66
    if (completeCount === Object.values(skillStatus.experiences).length) {
67
      return IconStatus.COMPLETE;
68
    }
69
  }
70
71
  return IconStatus.DEFAULT;
72
};
73
74
/**
75
 * Updates a slice of the status state based on provided payload.
76
 * Requires the Skill ID, Experience ID, and new status.
77
 *
78
 * @param state SkillStatus state object.
79
 * @param action Object with a payload of skillId, experienceId, and status.
80
 * @returns SkillStatus.
81
 */
82
export const statusReducer = (
83
  state: SkillStatus,
84
  action: {
85
    payload: {
86
      skillId: number;
87
      experienceId: number;
88
      experienceType: string;
89
      status: IconStatus;
90
    };
91
  },
92
): SkillStatus => {
93
  return {
94
    ...state,
95
    [action.payload.skillId]: {
96
      experiences: {
97
        ...state[action.payload.skillId].experiences,
98
        [`${action.payload.experienceType}_${action.payload.experienceId}`]: action
99
          .payload.status,
100
      },
101
    },
102
  };
103
};
104