Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 02c6a82

Browse files
committedJul 4, 2021
Added working period alert column.
1 parent ead7ac8 commit 02c6a82

File tree

11 files changed

+183
-23
lines changed

11 files changed

+183
-23
lines changed
 

‎src/constants/workPeriods.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// @ts-ignore
22
import { API } from "../../config";
3+
import * as ALERT from "./workPeriods/alerts";
34
import * as API_CHALLENGE_PAYMENT_STATUS from "./workPeriods/apiChallengePaymentStatus";
45
import * as API_PAYMENT_STATUS from "./workPeriods/apiPaymentStatus";
56
import * as API_SORT_BY from "./workPeriods/apiSortBy";
@@ -9,6 +10,7 @@ import * as PAYMENT_STATUS from "./workPeriods/paymentStatus";
910
import * as REASON_DISABLED from "./workPeriods/reasonDisabled";
1011

1112
export {
13+
ALERT,
1214
API_CHALLENGE_PAYMENT_STATUS,
1315
API_PAYMENT_STATUS,
1416
API_SORT_BY,
@@ -144,3 +146,8 @@ export const REASON_DISABLED_MESSAGE_MAP = {
144146
[REASON_DISABLED.NO_DAYS_TO_PAY_FOR]: "There are no days to pay for",
145147
[REASON_DISABLED.NO_MEMBER_RATE]: "Member Rate should be greater than 0",
146148
};
149+
150+
export const ALERT_MESSAGE_MAP = {
151+
[ALERT.BA_NOT_ASSIGNED]: "BA - Not Assigned",
152+
[ALERT.LAST_BOOKING_WEEK]: "Last Booking Week",
153+
};

‎src/constants/workPeriods/alerts.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const BA_NOT_ASSIGNED = "BA_NOT_ASSIGNED";
2+
export const LAST_BOOKING_WEEK = "LAST_BOOKING_WEEK";
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React, { useMemo } from "react";
2+
import PT from "prop-types";
3+
import cn from "classnames";
4+
import Tooltip from "components/Tooltip";
5+
import { ALERT_MESSAGE_MAP } from "constants/workPeriods";
6+
import styles from "./styles.module.scss";
7+
8+
/**
9+
* Displays alerts for working period.
10+
*
11+
* @param {Object} props component properties
12+
* @param {string[]} [props.alerts] array of alert ids
13+
* @param {string} [props.className] class name to be added to alerts wrapper
14+
* @returns {JSX.Element}
15+
*/
16+
const PeriodAlerts = ({ alerts, className }) => {
17+
const alertsTooltipContent = useMemo(() => {
18+
return alerts
19+
? alerts.map((alertId) => (
20+
<div key={alertId}>{ALERT_MESSAGE_MAP[alertId]}</div>
21+
))
22+
: null;
23+
}, [alerts]);
24+
25+
return (
26+
<Tooltip
27+
content={alertsTooltipContent}
28+
isDisabled={!alerts}
29+
targetClassName={cn(
30+
styles.container,
31+
{ [styles.hasAlerts]: !!alerts },
32+
className
33+
)}
34+
tooltipClassName={styles.tooltip}
35+
>
36+
{alerts
37+
? alerts.map((alertId) => ALERT_MESSAGE_MAP[alertId]).join(", ")
38+
: "None"}
39+
</Tooltip>
40+
);
41+
};
42+
43+
PeriodAlerts.propTypes = {
44+
alerts: PT.arrayOf(PT.string),
45+
className: PT.string,
46+
};
47+
48+
export default PeriodAlerts;
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
@import "styles/mixins";
2+
@import "styles/variables";
3+
4+
.container {
5+
display: inline-block;
6+
7+
&.hasAlerts {
8+
border-radius: 5px;
9+
padding: 3px 5px 1px;
10+
height: 20px;
11+
max-width: 15em;
12+
line-height: 16px;
13+
font-size: 11px;
14+
@include roboto-medium;
15+
text-align: left;
16+
white-space: nowrap;
17+
overflow: hidden;
18+
text-overflow: ellipsis;
19+
background-color: #ffc43d;
20+
21+
&::before {
22+
content: "!";
23+
display: inline-block;
24+
margin-right: 4px;
25+
border: 2px solid $text-color;
26+
border-radius: 7px;
27+
padding: 1px 0 0;
28+
width: 13px;
29+
height: 13px;
30+
line-height: 8px;
31+
font-size: 10px;
32+
@include roboto-bold;
33+
text-align: center;
34+
}
35+
}
36+
}
37+
38+
.tooltip {
39+
white-space: nowrap;
40+
}

‎src/routes/WorkPeriods/components/PeriodDetails/index.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ const PeriodDetails = ({
8989
)}
9090
>
9191
{periodsIsLoading ? (
92-
<td colSpan={9}>
92+
<td colSpan={10}>
9393
<div className={styles.loadingIndicator}>Loading...</div>
9494
</td>
9595
) : (
@@ -122,7 +122,7 @@ const PeriodDetails = ({
122122
</Button>
123123
</div>
124124
</td>
125-
<td colSpan={6} className={styles.periodHistory}>
125+
<td colSpan={7} className={styles.periodHistory}>
126126
<div className={styles.periodsContainer}>
127127
<div className={styles.periodsHeader}>
128128
<span className={styles.periodsHeaderTitle}>History</span>

‎src/routes/WorkPeriods/components/PeriodItem/index.jsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import { useUpdateEffect } from "utils/hooks";
3030
import { formatUserHandleLink, formatWeeklyRate } from "utils/formatters";
3131
import { stopPropagation } from "utils/misc";
3232
import styles from "./styles.module.scss";
33+
import PeriodAlerts from "../PeriodAlerts";
3334

3435
/**
3536
* Displays the working period data row to be used in PeriodList component.
@@ -38,6 +39,7 @@ import styles from "./styles.module.scss";
3839
* @param {boolean} [props.isDisabled] whether the item is disabled
3940
* @param {boolean} props.isSelected whether the item is selected
4041
* @param {Object} props.item object describing a working period
42+
* @param {Array} [props.alerts] array with alert ids
4143
* @param {Object} props.data changeable working period data such as working days
4244
* @param {Object} [props.details] object with working period details
4345
* @param {Object} [props.reasonFailed] error object denoting payment processing failure
@@ -48,6 +50,7 @@ const PeriodItem = ({
4850
isDisabled = false,
4951
isSelected,
5052
item,
53+
alerts,
5154
data,
5255
details,
5356
reasonFailed,
@@ -177,6 +180,9 @@ const PeriodItem = ({
177180
</td>
178181
<td className={styles.startDate}>{item.startDate}</td>
179182
<td className={styles.endDate}>{item.endDate}</td>
183+
<td className={styles.alert}>
184+
<PeriodAlerts alerts={alerts} />
185+
</td>
180186
<td className={styles.weeklyRate}>
181187
<span>{formatWeeklyRate(item.weeklyRate)}</span>
182188
</td>
@@ -261,6 +267,7 @@ PeriodItem.propTypes = {
261267
endDate: PT.string.isRequired,
262268
weeklyRate: PT.number,
263269
}).isRequired,
270+
alerts: PT.arrayOf(PT.string),
264271
data: PT.shape({
265272
daysWorked: PT.number.isRequired,
266273
daysPaid: PT.number.isRequired,

‎src/routes/WorkPeriods/components/PeriodList/index.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import PeriodItem from "../PeriodItem";
88
import PeriodListHead from "../PeriodListHead";
99
import {
1010
getWorkPeriods,
11+
getWorkPeriodsAlerts,
1112
getWorkPeriodsData,
1213
getWorkPeriodsDetails,
1314
getWorkPeriodsDisabled,
@@ -26,6 +27,7 @@ import styles from "./styles.module.scss";
2627
*/
2728
const PeriodList = ({ className }) => {
2829
const periods = useSelector(getWorkPeriods);
30+
const periodsAlerts = useSelector(getWorkPeriodsAlerts);
2931
const [periodsData] = useSelector(getWorkPeriodsData);
3032
const periodsDetails = useSelector(getWorkPeriodsDetails);
3133
const [periodsDisabledMap] = useSelector(getWorkPeriodsDisabled);
@@ -49,14 +51,15 @@ const PeriodList = ({ className }) => {
4951
</thead>
5052
<tbody>
5153
<tr>
52-
<td colSpan={9} className={styles.listTopMargin}></td>
54+
<td colSpan={10} className={styles.listTopMargin}></td>
5355
</tr>
5456
{periods.map((period) => (
5557
<PeriodItem
5658
key={period.id}
5759
isDisabled={isProcessingPayments}
5860
isSelected={periodsSelectedSet.has(period.id)}
5961
item={period}
62+
alerts={periodsAlerts[period.id]}
6063
data={periodsData[period.id]}
6164
details={periodsDetails[period.id]}
6265
reasonFailed={periodsFailed[period.id]}

‎src/routes/WorkPeriods/components/PeriodListHead/index.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ const HEAD_CELLS = [
7575
{ label: "Team Name", id: SORT_BY.TEAM_NAME, disableSort: true },
7676
{ label: "Start Date", id: SORT_BY.START_DATE, className: "startDate" },
7777
{ label: "End Date", id: SORT_BY.END_DATE, className: "endDate" },
78+
{ label: "Alert", id: SORT_BY.ALERT, disableSort: true, className: "alert" },
7879
{ label: "Weekly Rate", id: SORT_BY.WEEKLY_RATE, className: "weeklyRate" },
7980
{ label: "Total Paid", id: SORT_BY.PAYMENT_TOTAL, className: "totalPaid" },
8081
{ label: "Status", id: SORT_BY.PAYMENT_STATUS },

‎src/store/reducers/workPeriods.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,19 @@ import {
1111
SORT_ORDER_DEFAULT,
1212
URL_QUERY_PARAM_MAP,
1313
REASON_DISABLED,
14+
ALERT,
1415
} from "constants/workPeriods";
1516
import {
1617
filterPeriodsByStartDate,
1718
getWeekByDate,
1819
updateOptionMap,
1920
} from "utils/misc";
2021
import {
21-
addReasonDisabled,
22+
addValueImmutable,
23+
createPeriodAlerts,
2224
createAssignedBillingAccountOption,
2325
findReasonsDisabled,
24-
removeReasonDisabled,
26+
removeValueImmutable,
2527
} from "utils/workPeriods";
2628

2729
const cancelSourceDummy = { cancel: () => {} };
@@ -75,6 +77,7 @@ const initialState = updateStateFromQuery(window.location.search, {
7577
isSelectedPeriodsVisible: false,
7678
pagination: initPagination(),
7779
periods: [],
80+
periodsAlerts: {},
7881
periodsById: {},
7982
periodsData: [{}],
8083
periodsDetails: {},
@@ -102,6 +105,7 @@ const actionHandlers = {
102105
isSelectedPeriodsAll: false,
103106
isSelectedPeriodsVisible: false,
104107
periods: [],
108+
periodsAlerts: {},
105109
periodsById: {},
106110
periodsData: [{}],
107111
periodsDetails: {},
@@ -119,16 +123,22 @@ const actionHandlers = {
119123
oldPagination.pageCount !== pageCount
120124
? { ...oldPagination, totalCount, pageCount }
121125
: oldPagination;
126+
const periodsAlerts = {};
122127
const periodsById = {};
123128
const periodsData = {};
124129
const periodsDisabledMap = new Map();
130+
const periodEndDate = state.filters.dateRange[1];
125131
for (let period of periods) {
126132
periodsById[period.id] = true;
127133
periodsData[period.id] = initPeriodData(period);
128134
let reasonsDisabled = findReasonsDisabled(period);
129135
if (reasonsDisabled) {
130136
periodsDisabledMap.set(period.id, reasonsDisabled);
131137
}
138+
let alerts = createPeriodAlerts(period, periodEndDate);
139+
if (alerts) {
140+
periodsAlerts[period.id] = alerts;
141+
}
132142
delete period.data;
133143
}
134144
return {
@@ -137,6 +147,7 @@ const actionHandlers = {
137147
error: null,
138148
pagination,
139149
periods,
150+
periodsAlerts,
140151
periodsById,
141152
periodsData: [periodsData],
142153
periodsDisabled: [periodsDisabledMap],
@@ -342,7 +353,7 @@ const actionHandlers = {
342353
// updating reasons for which the period's selection may be disabled
343354
const periodsDisabledMap = state.periodsDisabled[0];
344355
const oldReasonsDisabled = periodsDisabledMap.get(periodId);
345-
const reasonsDisabled = removeReasonDisabled(
356+
const reasonsDisabled = removeValueImmutable(
346357
oldReasonsDisabled,
347358
REASON_DISABLED.NO_BILLING_ACCOUNT
348359
);
@@ -355,6 +366,18 @@ const actionHandlers = {
355366
state.periodsDisabled = [periodsDisabledMap];
356367
updateSelectedPeriodsFlags(state);
357368
}
369+
// updating period's alerts
370+
const periodsAlerts = state.periodsAlerts;
371+
const oldAlerts = periodsAlerts[periodId];
372+
const alerts = removeValueImmutable(oldAlerts, ALERT.BA_NOT_ASSIGNED);
373+
if (oldAlerts !== alerts) {
374+
if (alerts) {
375+
periodsAlerts[periodId] = alerts;
376+
} else {
377+
delete periodsAlerts[periodId];
378+
}
379+
state.periodsAlerts = { ...periodsAlerts };
380+
}
358381
return state;
359382
},
360383
[ACTION_TYPE.WP_SET_DETAILS_HIDE_PAST_PERIODS]: (
@@ -699,11 +722,11 @@ function updateStateAfterWorkingDaysChange(periodId, state) {
699722
const oldReasonsDisabled = periodsDisabledMap.get(periodId);
700723
let reasonsDisabled =
701724
periodData.daysWorked === periodData.daysPaid
702-
? addReasonDisabled(
725+
? addValueImmutable(
703726
oldReasonsDisabled,
704727
REASON_DISABLED.NO_DAYS_TO_PAY_FOR
705728
)
706-
: removeReasonDisabled(
729+
: removeValueImmutable(
707730
oldReasonsDisabled,
708731
REASON_DISABLED.NO_DAYS_TO_PAY_FOR
709732
);

‎src/store/selectors/workPeriods.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ export const getWorkPeriodsStateSlice = (state) => state.workPeriods;
1414
*/
1515
export const getWorkPeriods = (state) => state.workPeriods.periods;
1616

17+
/**
18+
* Returns an object with period ids as keys and alerts' arrays as values;
19+
*
20+
* @param {Object} state redux root state
21+
* @returns {Object}
22+
*/
23+
export const getWorkPeriodsAlerts = (state) => state.workPeriods.periodsAlerts;
24+
1725
/**
1826
* Returns working periods' details.
1927
*

‎src/utils/workPeriods.js

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import moment from "moment";
22
import {
3+
ALERT,
34
API_CHALLENGE_PAYMENT_STATUS_MAP,
45
API_PAYMENT_STATUS_MAP,
56
DATE_FORMAT_API,
@@ -9,6 +10,24 @@ import {
910
URL_QUERY_PARAM_MAP,
1011
} from "constants/workPeriods";
1112

13+
/**
14+
* Returns an array of working period's alert ids.
15+
*
16+
* @param {Object} period working period basic data object
17+
* @param {Object} periodEndDate Moment object with current period end date
18+
* @returns {Array}
19+
*/
20+
export function createPeriodAlerts(period, periodEndDate) {
21+
const alerts = [];
22+
if (!period.billingAccountId) {
23+
alerts.push(ALERT.BA_NOT_ASSIGNED);
24+
}
25+
if (periodEndDate.isSameOrAfter(period.endDate)) {
26+
alerts.push(ALERT.LAST_BOOKING_WEEK);
27+
}
28+
return alerts.length ? alerts : undefined;
29+
}
30+
1231
/**
1332
* Checks for reasons the specified working period should be disabled for
1433
* payment processing.
@@ -31,29 +50,27 @@ export function findReasonsDisabled(period) {
3150
return reasons.length ? reasons : undefined;
3251
}
3352

34-
export function createAlerts(period, bookingEndDate) {}
35-
36-
export function addReasonDisabled(reasons, reason) {
37-
if (!reasons) {
38-
return [reason];
53+
export function addValueImmutable(items, value) {
54+
if (!items) {
55+
return [value];
3956
}
40-
if (reasons.indexOf(reason) < 0) {
41-
reasons = [...reasons, reason];
57+
if (items.indexOf(value) < 0) {
58+
items = [...items, value];
4259
}
43-
return reasons;
60+
return items;
4461
}
4562

46-
export function removeReasonDisabled(reasons, reason) {
47-
if (!reasons) {
63+
export function removeValueImmutable(items, value) {
64+
if (!items) {
4865
return undefined;
4966
}
50-
let index = reasons.indexOf(reason);
67+
let index = items.indexOf(value);
5168
if (index >= 0) {
52-
let newReasons = [...reasons];
53-
newReasons.splice(index, 1);
54-
return newReasons.length ? newReasons : undefined;
69+
let newItems = [...items];
70+
newItems.splice(index, 1);
71+
return newItems.length ? newItems : undefined;
5572
}
56-
return reasons;
73+
return items;
5774
}
5875

5976
/**
@@ -102,9 +119,11 @@ export function normalizePeriodItems(items) {
102119
billingAccountId: billingAccountId === null ? 0 : billingAccountId,
103120
teamName: "",
104121
userHandle: workPeriod.userHandle || "",
122+
// resource booking period start date
105123
startDate: item.startDate
106124
? moment(item.startDate).format(DATE_FORMAT_UI)
107125
: "",
126+
// resource booking period end date
108127
endDate: item.endDate ? moment(item.endDate).format(DATE_FORMAT_UI) : "",
109128
weeklyRate: item.memberRate,
110129
data: normalizePeriodData(workPeriod),
@@ -118,7 +137,9 @@ export function normalizeDetailsPeriodItems(items) {
118137
for (let item of items) {
119138
periods.push({
120139
id: item.id,
140+
// working period start date
121141
startDate: item.startDate ? moment(item.startDate).valueOf() : 0,
142+
// working period end date
122143
endDate: item.endDate ? moment(item.endDate).valueOf() : 0,
123144
weeklyRate: item.memberRate,
124145
data: normalizePeriodData(item),

0 commit comments

Comments
 (0)
This repository has been archived.