Passed
Push — feature/job-status-transitions ( 688dc7...931d64 )
by Tristan
11:00 queued 06:03
created

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

Complexity

Total Complexity 11
Complexity/F 0

Size

Lines of Code 231
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 11
eloc 183
mnd 11
bc 11
fnc 0
dl 0
loc 231
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
/* eslint-disable camelcase */
2
/* eslint-disable @typescript-eslint/camelcase */
3
import React, { useEffect, useState } from "react";
4
import { connect } from "react-redux";
5
import { FormattedMessage, useIntl } from "react-intl";
6
import Activity from "./Activity";
7
import { RootState } from "../store/store";
8
import { Comment, User } from "../models/types";
9
import { DispatchType } from "../configureStore";
10
import { fetchComments } from "../store/Job/jobActions";
11
import { fetchAllUsers } from "../store/User/userActions";
12
import { getComments, sortComments } from "../store/Job/jobSelector";
13
import { getUsers } from "../store/User/userSelector";
14
import { commentTypeMessages } from "./CommentForm";
15
import {
16
  generalLocationOption,
17
  specificLocationOption,
18
} from "../models/localizedConstants";
19
import { activityLocationUrl } from "../models/jobUtil";
20
import { LocationId } from "../models/lookupConstants";
21
import { getLocale, localizeFieldNonNull } from "../helpers/localize";
22
import { find } from "../helpers/queries";
23
24
interface ActivityListProps {
25
  jobId: number;
26
  isHrAdvisor: boolean;
27
  comments: Comment[];
28
  users: User[];
29
  handleFetchComments: (jobId: number) => Promise<void>;
30
  handleFetchUsers: () => Promise<void>;
31
  filterComments?: (comment: Comment) => boolean;
32
}
33
34
export const ActivityList: React.FunctionComponent<ActivityListProps> = ({
35
  jobId,
36
  comments,
37
  users,
38
  handleFetchComments,
39
  handleFetchUsers,
40
  isHrAdvisor,
41
}) => {
42
  const intl = useIntl();
43
  const locale = getLocale(intl.locale);
44
  const activities: Comment[] = [...comments];
45
  const [loadingActivities, setLoadingActivities] = useState(false);
46
  const [isError, setIsError] = useState(false);
47
48
  useEffect((): void => {
49
    setLoadingActivities(true);
50
    handleFetchComments(jobId)
51
      .then(() => {
52
        setLoadingActivities(false);
53
      })
54
      .catch(() => {
55
        setLoadingActivities(false);
56
        setIsError(true);
57
      });
58
  }, [handleFetchComments, jobId]);
59
60
  useEffect((): void => {
61
    setLoadingActivities(true);
62
    handleFetchUsers()
63
      .then(() => {
64
        setLoadingActivities(false);
65
      })
66
      .catch(() => {
67
        setLoadingActivities(false);
68
        setIsError(true);
69
      });
70
  }, [handleFetchUsers]);
71
72
  const activityType = (type: number | null): string => {
73
    switch (type) {
74
      case 1:
75
        return intl.formatMessage(commentTypeMessages.question);
76
      case 2:
77
        return intl.formatMessage(commentTypeMessages.recommendation);
78
      case 3:
79
        return intl.formatMessage(commentTypeMessages.requiredAction);
80
      default:
81
        return intl.formatMessage(commentTypeMessages.comment);
82
    }
83
  };
84
85
  const isValidLocation = (locationStr: string): boolean => {
86
    const validLocations = Object.values(LocationId) as ReadonlyArray<string>;
87
    return validLocations.includes(locationStr);
88
  };
89
90
  const getLocation = (locationStr: string): string =>
91
    isValidLocation(locationStr)
92
      ? `${intl.formatMessage(
93
          generalLocationOption(locationStr),
94
        )} > ${intl.formatMessage(specificLocationOption(locationStr))}`
95
      : "";
96
97
  return (
98
    <section data-c-padding="top(1)">
99
      <h3 data-c-font-size="h3" data-c-color="c2" data-c-margin="bottom(1)">
100
        <FormattedMessage
101
          id="activityfeed.title"
102
          defaultMessage="Activities"
103
          description="Title of activity feed."
104
        />
105
      </h3>
106
      {isError && (
107
        <p>
108
          <FormattedMessage
109
            id="activityfeed.error"
110
            defaultMessage="Something went wrong..."
111
            description="Error fetching activities."
112
          />
113
        </p>
114
      )}
115
      {loadingActivities ? (
116
        <div data-c-container="form" data-c-padding="top(1) bottom(1)">
117
          <div
118
            data-c-background="white(100)"
119
            data-c-card
120
            data-c-padding="all(double)"
121
            data-c-radius="rounded"
122
            data-c-align="base(centre)"
123
          >
124
            <p>
125
              <FormattedMessage
126
                id="activityfeed.loading"
127
                defaultMessage="Your activities are loading..."
128
                description="Message indicating that the activity feed is still being loaded."
129
              />
130
            </p>
131
          </div>
132
        </div>
133
      ) : (
134
        <>
135
          {activities && activities.length !== 0
136
            ? activities.map(
137
                ({
138
                  id,
139
                  comment,
140
                  location,
141
                  created_at,
142
                  type_id,
143
                  user_id,
144
                }: Comment): React.ReactElement => {
145
                  const user = find(users, user_id);
146
                  const fullName = user?.full_name ?? "";
147
                  const userRole = user?.user_role;
148
                  let displayRole = "";
149
                  if (userRole !== undefined) {
150
                    displayRole = localizeFieldNonNull(
151
                      locale,
152
                      userRole,
153
                      "name",
154
                    );
155
                  }
156
157
                  return (
158
                    <Activity
159
                      key={id}
160
                      name={fullName}
161
                      userRole={displayRole}
162
                      comment={comment}
163
                      location={getLocation(location)}
164
                      time={created_at}
165
                      type={activityType(type_id)}
166
                      link={{
167
                        url: activityLocationUrl(
168
                          isHrAdvisor,
169
                          location,
170
                          jobId,
171
                          locale,
172
                        ),
173
                        text: "",
174
                        title: "",
175
                      }}
176
                    />
177
                  );
178
                },
179
              )
180
            : !isError && (
181
                <p>
182
                  <FormattedMessage
183
                    id="activityfeed.noActivities"
184
                    defaultMessage="No activities."
185
                    description="Message displayed when activities is empty."
186
                  />
187
                </p>
188
              )}
189
        </>
190
      )}
191
    </section>
192
  );
193
};
194
195
const mapStateToProps = (
196
  state: RootState,
197
  {
198
    filterComments = (): boolean => true,
199
  }: { filterComments?: (comment: Comment) => boolean },
200
): {
201
  comments: Comment[];
202
  users: User[];
203
} => ({
204
  comments: sortComments(getComments(state).filter(filterComments)),
205
  users: getUsers(state),
206
});
207
208
const mapDispatchToProps = (
209
  dispatch: DispatchType,
210
): {
211
  handleFetchComments: (jobId: number) => Promise<void>;
212
  handleFetchUsers: () => Promise<void>;
213
} => ({
214
  handleFetchComments: async (jobId: number): Promise<void> => {
215
    const result = await dispatch(fetchComments(jobId));
216
    if (!result.error) {
217
      return Promise.resolve();
218
    }
219
    return Promise.reject(result.error);
220
  },
221
  handleFetchUsers: async (): Promise<void> => {
222
    const result = await dispatch(fetchAllUsers());
223
    if (!result.error) {
224
      return Promise.resolve();
225
    }
226
    return Promise.reject(result.error);
227
  },
228
});
229
230
export default connect(mapStateToProps, mapDispatchToProps)(ActivityList);
231