Passed
Push — task/update-profile-experience ( 3aacad...e72667 )
by Tristan
05:18
created

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

Complexity

Total Complexity 13
Complexity/F 0

Size

Lines of Code 255
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

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