Passed
Push — task/update-profile-experience ( 9cba2e...f7db59 )
by Tristan
07:12
created

resources/assets/js/components/ApplicantProfile/Experience/ProfileSkillSubform.tsx   A

Complexity

Total Complexity 1
Complexity/F 0

Size

Lines of Code 209
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 1
eloc 170
mnd 1
bc 1
fnc 0
dl 0
loc 209
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import React, { FunctionComponent } from "react";
2
import {
3
  FormattedMessage,
4
  useIntl,
5
  defineMessages,
6
  IntlShape,
7
} from "react-intl";
8
import * as Yup from "yup";
9
import { Field, FieldArray } from "formik";
10
import { getLocale, localizeFieldNonNull } from "../../../helpers/localize";
11
import { validationMessages } from "../../Form/Messages";
12
import { Experience, Skill } from "../../../models/types";
13
import TextAreaInput from "../../Form/TextAreaInput";
14
import CheckboxInput from "../../Form/CheckboxInput";
15
import { countNumberOfWords } from "../../WordCounter/helpers";
16
import { ExperienceSubmitData } from "./ExperienceModalCommon";
17
import {
18
  deleteProperty,
19
  getId,
20
  mapToObjectTrans,
21
  objectMap,
22
} from "../../../helpers/queries";
23
24
const messages = defineMessages({
25
  justificationLabel: {
26
    id: "profile.experience.skillSubform.justificationLabel",
27
    defaultMessage: "Explanation",
28
    description: "Label for the 'How did you use this skill?' field.",
29
  },
30
  justificationPlaceholder: {
31
    id: "profile.experience.skillSubform.justificationPlaceholder",
32
    defaultMessage: "How did you use this skill...",
33
    description:
34
      "Placeholder text for the 'How did you use this skill?' field.",
35
  },
36
});
37
38
export interface SkillFormValues {
39
  skills: {
40
    [key: number]: {
41
      selected: boolean;
42
      justification: string;
43
    };
44
  };
45
}
46
47
export const submitDataToFormSkills = (
48
  data: ExperienceSubmitData<Experience>,
49
  allSkills: Skill[],
50
): SkillFormValues => {
51
  const skillToData = (skill: Skill) => {
52
    const savedSkillData = data.savedSkills.find((x) => x.skillId === skill.id);
53
    return savedSkillData
54
      ? { selected: true, justification: savedSkillData.justification }
55
      : { selected: false, justification: "" };
56
  };
57
  return { skills: mapToObjectTrans(allSkills, getId, skillToData) };
58
};
59
60
export const formSkillsToSubmitData = (
61
  skillFormValues: SkillFormValues,
62
): ExperienceSubmitData<Experience>["savedSkills"] =>
63
  objectMap(skillFormValues.skills, (skillId, { selected, justification }) => ({
64
    skillId: Number(skillId),
65
    selected,
66
    justification,
67
  }))
68
    .filter((x) => x.selected)
69
    .map((x) => deleteProperty(x, "selected"));
70
71
const JUSTIFICATION_WORD_LIMIT = 100;
72
73
export const validationShape = (intl: IntlShape) => {
74
  const requiredMsg = intl.formatMessage(validationMessages.required);
75
  const overWordLimit = intl.formatMessage(validationMessages.overMaxWords, {
76
    numberOfWords: JUSTIFICATION_WORD_LIMIT,
77
  });
78
  return {
79
    skills: Yup.array().of(
80
      Yup.object().shape({
81
        selected: Yup.boolean(),
82
        justification: Yup.string().when("selected", {
83
          is: true,
84
          then: Yup.string()
85
            .test(
86
              "wordCount",
87
              overWordLimit,
88
              (value: string) =>
89
                countNumberOfWords(value) <= JUSTIFICATION_WORD_LIMIT,
90
            )
91
            .required(requiredMsg),
92
          otherwise: Yup.string().test(
93
            "wordCount",
94
            overWordLimit,
95
            (value: string) =>
96
              countNumberOfWords(value) <= JUSTIFICATION_WORD_LIMIT,
97
          ), // Enforce word limit even if justification isn't required.
98
        }),
99
      }),
100
    ),
101
  };
102
};
103
104
export interface ProfileSkillSubformProps {
105
  keyPrefix: string;
106
  skills: Skill[];
107
}
108
109
export const ProfileSkillSubform: FunctionComponent<ProfileSkillSubformProps> = ({
110
  keyPrefix,
111
  skills,
112
}) => {
113
  const intl = useIntl();
114
  const locale = getLocale(intl.locale);
115
116
  return (
117
    <>
118
      <div data-c-container="medium">
119
        <p
120
          data-c-margin="top(1) bottom(1)"
121
          data-c-font-size="h4"
122
          data-c-font-weight="bold"
123
          data-c-color="c3"
124
        >
125
          <FormattedMessage
126
            id="profile.experience.skillSubform.connectSubtitle"
127
            defaultMessage="Connect your Skills to this Experience"
128
            description="Subtitle of Connect-to-skills section."
129
          />
130
        </p>
131
        <p data-c-margin="bottom(1)">
132
          <FormattedMessage
133
            id="profile.experience.skillSubform.connectDescription"
134
            defaultMessage="Add any skills below that you learned or used in this experience. Hiring Managers see a lot of applicant profiles and you will need to set yourself apart if you want new job opportunities. You can do this by answering the following questions for each of the skills you add. This is the most important part of your profile if you're hoping a manager will find you."
135
            description="Explanation for Connect-to-skills section."
136
          />
137
        </p>
138
        <ul data-c-font-weight="bold" data-c-margin="bottom(1)">
139
          <li>
140
            <FormattedMessage
141
              id="profile.experience.skillSubform.question1"
142
              defaultMessage="What did you accomplish, create or deliver using this skill?"
143
              description="A question the user should answer when connecting a Skill to Experience."
144
            />
145
          </li>
146
          <li>
147
            <FormattedMessage
148
              id="profile.experience.skillSubform.question2"
149
              defaultMessage="What tasks or activities did you do that relate to this skill?"
150
              description="A question the user should answer when connecting a Skill to Experience."
151
            />
152
          </li>
153
          <li>
154
            <FormattedMessage
155
              id="profile.experience.skillSubform.question3"
156
              defaultMessage="Were there any special techniques or approaches that you used?"
157
              description="A question the user should answer when connecting a Skill to Experience."
158
            />
159
          </li>
160
          <li>
161
            <FormattedMessage
162
              id="profile.experience.skillSubform.question4"
163
              defaultMessage="How much responsibility did you have in this role?"
164
              description="A question the user should answer when connecting a Skill to Experience."
165
            />
166
          </li>
167
        </ul>
168
      </div>
169
      <div data-c-container="medium">
170
        <div data-c-grid="gutter(all, 1) middle">
171
          <FieldArray
172
            name="skills"
173
            render={(arrayHelpers) => (
174
              <div data-c-grid>
175
                {skills.map((skill) => (
176
                  <div key={skill.id} data-c-grid-item="tl(1of1)">
177
                    <div data-c-grid="gutter(all, 1)">
178
                      <Field
179
                        id={`${keyPrefix}-${skill.id}-selected`}
180
                        component={CheckboxInput}
181
                        name={`skills.${skill.id}.selected`}
182
                        grid="tl(1of4)"
183
                        label={localizeFieldNonNull(locale, skill, "name")}
184
                      />
185
                      <Field
186
                        id={`${keyPrefix}-${skill.id}-justification`}
187
                        type="text"
188
                        name={`skills.${skill.id}.justification`}
189
                        component={TextAreaInput}
190
                        required
191
                        grid="tl(3of4)"
192
                        label={intl.formatMessage(messages.justificationLabel)}
193
                        placeholder={intl.formatMessage(
194
                          messages.justificationPlaceholder,
195
                        )}
196
                        wordLimit={JUSTIFICATION_WORD_LIMIT}
197
                      />
198
                    </div>
199
                  </div>
200
                ))}
201
              </div>
202
            )}
203
          />
204
        </div>
205
      </div>
206
    </>
207
  );
208
};
209