Passed
Push — task/hygrogen-react-components ( 2e5865...0716f4 )
by Yonathan
05:39 queued 02:00
created

resources/assets/js/components/CommentForm.tsx   A

Complexity

Total Complexity 6
Complexity/F 0

Size

Lines of Code 252
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 6
eloc 192
c 0
b 0
f 0
dl 0
loc 252
rs 10
mnd 6
bc 6
fnc 0
bpm 0
cpm 0
noi 0
1
import * as React from "react";
2
import { connect } from "react-redux";
3
import { Formik, Form, FastField } from "formik";
4
import * as Yup from "yup";
5
import { useIntl, defineMessages, FormattedMessage } from "react-intl";
6
import { validationMessages } from "./Form/Messages";
7
import { CommentTypeId, LocationId } from "../models/lookupConstants";
8
import TextAreaInput from "./Form/TextAreaInput";
9
import SelectInput from "./Form/SelectInput";
10
import { Comment } from "../models/types";
11
import { DispatchType } from "../configureStore";
12
import { createComment } from "../store/Job/jobActions";
13
import { emptyComment } from "../models/jobUtil";
14
15
const formMessages = defineMessages({
16
  commentLabel: {
17
    id: "commentForm.comment.label",
18
    defaultMessage: "Add a Comment",
19
    description: "The label displayed for the comment input field.",
20
  },
21
  commentPlaceholder: {
22
    id: "commentForm.comment.placeholder",
23
    defaultMessage: "eg. Enter your question, recommendation, etc.",
24
    description: "The placeholder displayed for the comment input field.",
25
  },
26
  commentTypeLabel: {
27
    id: "commentForm.commentType.label",
28
    defaultMessage: "Type of Comment",
29
    description: "The label displayed for the comment type select box.",
30
  },
31
  commentTypeNullSelection: {
32
    id: "commentForm.commentType.nullSelection",
33
    defaultMessage: "Select a comment type...",
34
    description: "Null selection from list of comment types",
35
  },
36
  commentLocationLabel: {
37
    id: "commentForm.commentLocation.label",
38
    defaultMessage: "Location of Comment",
39
    description: "The label displayed for the location select box.",
40
  },
41
  commentLocationNullSelection: {
42
    id: "commentForm.commentLocation.nullSelection",
43
    defaultMessage: "Select a location...",
44
    description: "Null selection from list of comment locations",
45
  },
46
});
47
48
export const commentTypeMessages = defineMessages({
49
  question: {
50
    id: "commentType.question",
51
    defaultMessage: "Question",
52
    description: "Comment type from list of comment types",
53
  },
54
  recommendation: {
55
    id: "commentType.recommendation",
56
    defaultMessage: "Recommendation",
57
    description: "Comment type from list of comment types",
58
  },
59
  requiredAction: {
60
    id: "commentType.requiredAction",
61
    defaultMessage: "Required Action",
62
    description: "Comment type from list of comment types",
63
  },
64
  comment: {
65
    id: "commentType.comment",
66
    defaultMessage: "Comment",
67
    description: "Comment type from list of comment types",
68
  },
69
});
70
71
interface CommentFormProps {
72
  jobId: number;
73
  isHrAdvisor: boolean;
74
  location: string;
75
  locationOptions?: { value: string; label: string }[];
76
  handleCreateComment: (jobId: number, newComment: Comment) => Promise<Comment>;
77
}
78
79
interface CommentFormValues {
80
  comment: string;
81
  commentType: number | "";
82
  commentLocation?: string | "";
83
}
84
85
export const CommentForm: React.FunctionComponent<CommentFormProps> = ({
86
  isHrAdvisor,
87
  handleCreateComment,
88
  jobId,
89
  location,
90
  locationOptions,
91
}: CommentFormProps): React.ReactElement => {
92
  const intl = useIntl();
93
  const initialValues: CommentFormValues = {
94
    comment: "",
95
    commentType: "",
96
    commentLocation: "",
97
  };
98
99
  const commentSchema = Yup.object().shape({
100
    comment: Yup.string().required(
101
      intl.formatMessage(validationMessages.required),
102
    ),
103
    ...(isHrAdvisor && {
104
      commentType: Yup.number()
105
        .oneOf(
106
          Object.values(CommentTypeId),
107
          intl.formatMessage(validationMessages.invalidSelection),
108
        )
109
        .required(intl.formatMessage(validationMessages.required)),
110
    }),
111
    ...(locationOptions && {
112
      commentLocation: Yup.string()
113
        .oneOf(
114
          Object.values(LocationId),
115
          intl.formatMessage(validationMessages.invalidSelection),
116
        )
117
        .required(intl.formatMessage(validationMessages.required)),
118
    }),
119
  });
120
121
  return (
122
    <section>
123
      <Formik
124
        initialValues={initialValues}
125
        validationSchema={commentSchema}
126
        onSubmit={(values, { setSubmitting, resetForm }): void => {
127
          const newComment: Comment = {
128
            ...emptyComment(),
129
            location: values.commentLocation
130
              ? values.commentLocation
131
              : location,
132
            comment: values.comment,
133
            type_id: isHrAdvisor ? Number(values.commentType) : null,
134
          };
135
          handleCreateComment(jobId, newComment)
136
            .then(() => {
137
              setSubmitting(false);
138
              resetForm();
139
            })
140
            .catch(() => {
141
              setSubmitting(false);
142
            });
143
        }}
144
      >
145
        {({ isSubmitting }): React.ReactElement => (
146
          <Form data-c-grid="gutter(all, 1) middle">
147
            <FastField
148
              id="comment_form_input"
149
              type="text"
150
              name="comment"
151
              component={TextAreaInput}
152
              required
153
              grid="tl(1of1)"
154
              label={intl.formatMessage(formMessages.commentLabel)}
155
              placeholder={intl.formatMessage(formMessages.commentPlaceholder)}
156
            />
157
            {locationOptions && (
158
              <FastField
159
                name="commentLocation"
160
                id="comment_form_location"
161
                label={intl.formatMessage(formMessages.commentLocationLabel)}
162
                grid={locationOptions && isHrAdvisor ? "tl(1of2)" : "tl(2of3)"}
163
                component={SelectInput}
164
                required
165
                nullSelection={intl.formatMessage(
166
                  formMessages.commentLocationNullSelection,
167
                )}
168
                options={locationOptions.map(({ value, label }) => ({
169
                  value,
170
                  label,
171
                }))}
172
              />
173
            )}
174
            {isHrAdvisor && (
175
              <FastField
176
                id="comment_form_type"
177
                name="commentType"
178
                component={SelectInput}
179
                required
180
                grid={locationOptions && isHrAdvisor ? "tl(1of2)" : "tl(2of3)"}
181
                nullSelection={intl.formatMessage(
182
                  formMessages.commentTypeNullSelection,
183
                )}
184
                label={intl.formatMessage(formMessages.commentTypeLabel)}
185
                options={[
186
                  {
187
                    value: CommentTypeId.question,
188
                    label: intl.formatMessage(commentTypeMessages.question),
189
                  },
190
                  {
191
                    value: CommentTypeId.recommendation,
192
                    label: intl.formatMessage(
193
                      commentTypeMessages.recommendation,
194
                    ),
195
                  },
196
                  {
197
                    value: CommentTypeId.requiredAction,
198
                    label: intl.formatMessage(
199
                      commentTypeMessages.requiredAction,
200
                    ),
201
                  },
202
                ]}
203
              />
204
            )}
205
            <div
206
              data-c-grid-item={
207
                (locationOptions && isHrAdvisor) || !isHrAdvisor
208
                  ? "tl(1of1)"
209
                  : "tl(1of3)"
210
              }
211
              data-c-align="base(center) tl(right)"
212
            >
213
              <button
214
                type="submit"
215
                disabled={isSubmitting}
216
                data-c-button="solid(c1)"
217
                data-c-radius="rounded"
218
              >
219
                <FormattedMessage
220
                  id="commentForm.submitButton.label"
221
                  defaultMessage="Submit Comment"
222
                  description="The text displayed on the submit button in the comment form."
223
                />
224
              </button>
225
            </div>
226
          </Form>
227
        )}
228
      </Formik>
229
    </section>
230
  );
231
};
232
233
const mapDispatchToProps = (
234
  dispatch: DispatchType,
235
): {
236
  handleCreateComment: (jobId: number, newComment: Comment) => Promise<Comment>;
237
} => ({
238
  handleCreateComment: async (
239
    jobId: number,
240
    newComment: Comment,
241
  ): Promise<Comment> => {
242
    const result = await dispatch(createComment(jobId, newComment));
243
    if (!result.error) {
244
      const resultComment = await result.payload;
245
      return resultComment;
246
    }
247
    return Promise.reject(result.payload);
248
  },
249
});
250
251
export default connect(() => ({}), mapDispatchToProps)(CommentForm);
252