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

Modal component, payment cancelling, processing errors in popovers and other improvements #62

Merged
merged 12 commits into from
Jul 5, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Added working period alert column.
  • Loading branch information
MadOPcode committed Jul 4, 2021
commit 02c6a824cd7e4d16c3ce09bea5a980248795d80d
7 changes: 7 additions & 0 deletions src/constants/workPeriods.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-ignore
import { API } from "../../config";
import * as ALERT from "./workPeriods/alerts";
import * as API_CHALLENGE_PAYMENT_STATUS from "./workPeriods/apiChallengePaymentStatus";
import * as API_PAYMENT_STATUS from "./workPeriods/apiPaymentStatus";
import * as API_SORT_BY from "./workPeriods/apiSortBy";
Expand All @@ -9,6 +10,7 @@ import * as PAYMENT_STATUS from "./workPeriods/paymentStatus";
import * as REASON_DISABLED from "./workPeriods/reasonDisabled";

export {
ALERT,
API_CHALLENGE_PAYMENT_STATUS,
API_PAYMENT_STATUS,
API_SORT_BY,
Expand Down Expand Up @@ -144,3 +146,8 @@ export const REASON_DISABLED_MESSAGE_MAP = {
[REASON_DISABLED.NO_DAYS_TO_PAY_FOR]: "There are no days to pay for",
[REASON_DISABLED.NO_MEMBER_RATE]: "Member Rate should be greater than 0",
};

export const ALERT_MESSAGE_MAP = {
[ALERT.BA_NOT_ASSIGNED]: "BA - Not Assigned",
[ALERT.LAST_BOOKING_WEEK]: "Last Booking Week",
};
2 changes: 2 additions & 0 deletions src/constants/workPeriods/alerts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const BA_NOT_ASSIGNED = "BA_NOT_ASSIGNED";
export const LAST_BOOKING_WEEK = "LAST_BOOKING_WEEK";
48 changes: 48 additions & 0 deletions src/routes/WorkPeriods/components/PeriodAlerts/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, { useMemo } from "react";
import PT from "prop-types";
import cn from "classnames";
import Tooltip from "components/Tooltip";
import { ALERT_MESSAGE_MAP } from "constants/workPeriods";
import styles from "./styles.module.scss";

/**
* Displays alerts for working period.
*
* @param {Object} props component properties
* @param {string[]} [props.alerts] array of alert ids
* @param {string} [props.className] class name to be added to alerts wrapper
* @returns {JSX.Element}
*/
const PeriodAlerts = ({ alerts, className }) => {
const alertsTooltipContent = useMemo(() => {
return alerts
? alerts.map((alertId) => (
<div key={alertId}>{ALERT_MESSAGE_MAP[alertId]}</div>
))
: null;
}, [alerts]);

return (
<Tooltip
content={alertsTooltipContent}
isDisabled={!alerts}
targetClassName={cn(
styles.container,
{ [styles.hasAlerts]: !!alerts },
className
)}
tooltipClassName={styles.tooltip}
>
{alerts
? alerts.map((alertId) => ALERT_MESSAGE_MAP[alertId]).join(", ")
: "None"}
</Tooltip>
);
};

PeriodAlerts.propTypes = {
alerts: PT.arrayOf(PT.string),
className: PT.string,
};

export default PeriodAlerts;
40 changes: 40 additions & 0 deletions src/routes/WorkPeriods/components/PeriodAlerts/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
@import "styles/mixins";
@import "styles/variables";

.container {
display: inline-block;

&.hasAlerts {
border-radius: 5px;
padding: 3px 5px 1px;
height: 20px;
max-width: 15em;
line-height: 16px;
font-size: 11px;
@include roboto-medium;
text-align: left;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background-color: #ffc43d;

&::before {
content: "!";
display: inline-block;
margin-right: 4px;
border: 2px solid $text-color;
border-radius: 7px;
padding: 1px 0 0;
width: 13px;
height: 13px;
line-height: 8px;
font-size: 10px;
@include roboto-bold;
text-align: center;
}
}
}

.tooltip {
white-space: nowrap;
}
4 changes: 2 additions & 2 deletions src/routes/WorkPeriods/components/PeriodDetails/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const PeriodDetails = ({
)}
>
{periodsIsLoading ? (
<td colSpan={9}>
<td colSpan={10}>
<div className={styles.loadingIndicator}>Loading...</div>
</td>
) : (
Expand Down Expand Up @@ -122,7 +122,7 @@ const PeriodDetails = ({
</Button>
</div>
</td>
<td colSpan={6} className={styles.periodHistory}>
<td colSpan={7} className={styles.periodHistory}>
<div className={styles.periodsContainer}>
<div className={styles.periodsHeader}>
<span className={styles.periodsHeaderTitle}>History</span>
Expand Down
7 changes: 7 additions & 0 deletions src/routes/WorkPeriods/components/PeriodItem/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { useUpdateEffect } from "utils/hooks";
import { formatUserHandleLink, formatWeeklyRate } from "utils/formatters";
import { stopPropagation } from "utils/misc";
import styles from "./styles.module.scss";
import PeriodAlerts from "../PeriodAlerts";

/**
* Displays the working period data row to be used in PeriodList component.
Expand All @@ -38,6 +39,7 @@ import styles from "./styles.module.scss";
* @param {boolean} [props.isDisabled] whether the item is disabled
* @param {boolean} props.isSelected whether the item is selected
* @param {Object} props.item object describing a working period
* @param {Array} [props.alerts] array with alert ids
* @param {Object} props.data changeable working period data such as working days
* @param {Object} [props.details] object with working period details
* @param {Object} [props.reasonFailed] error object denoting payment processing failure
Expand All @@ -48,6 +50,7 @@ const PeriodItem = ({
isDisabled = false,
isSelected,
item,
alerts,
data,
details,
reasonFailed,
Expand Down Expand Up @@ -177,6 +180,9 @@ const PeriodItem = ({
</td>
<td className={styles.startDate}>{item.startDate}</td>
<td className={styles.endDate}>{item.endDate}</td>
<td className={styles.alert}>
<PeriodAlerts alerts={alerts} />
</td>
<td className={styles.weeklyRate}>
<span>{formatWeeklyRate(item.weeklyRate)}</span>
</td>
Expand Down Expand Up @@ -261,6 +267,7 @@ PeriodItem.propTypes = {
endDate: PT.string.isRequired,
weeklyRate: PT.number,
}).isRequired,
alerts: PT.arrayOf(PT.string),
data: PT.shape({
daysWorked: PT.number.isRequired,
daysPaid: PT.number.isRequired,
Expand Down
5 changes: 4 additions & 1 deletion src/routes/WorkPeriods/components/PeriodList/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import PeriodItem from "../PeriodItem";
import PeriodListHead from "../PeriodListHead";
import {
getWorkPeriods,
getWorkPeriodsAlerts,
getWorkPeriodsData,
getWorkPeriodsDetails,
getWorkPeriodsDisabled,
Expand All @@ -26,6 +27,7 @@ import styles from "./styles.module.scss";
*/
const PeriodList = ({ className }) => {
const periods = useSelector(getWorkPeriods);
const periodsAlerts = useSelector(getWorkPeriodsAlerts);
const [periodsData] = useSelector(getWorkPeriodsData);
const periodsDetails = useSelector(getWorkPeriodsDetails);
const [periodsDisabledMap] = useSelector(getWorkPeriodsDisabled);
Expand All @@ -49,14 +51,15 @@ const PeriodList = ({ className }) => {
</thead>
<tbody>
<tr>
<td colSpan={9} className={styles.listTopMargin}></td>
<td colSpan={10} className={styles.listTopMargin}></td>
</tr>
{periods.map((period) => (
<PeriodItem
key={period.id}
isDisabled={isProcessingPayments}
isSelected={periodsSelectedSet.has(period.id)}
item={period}
alerts={periodsAlerts[period.id]}
data={periodsData[period.id]}
details={periodsDetails[period.id]}
reasonFailed={periodsFailed[period.id]}
Expand Down
1 change: 1 addition & 0 deletions src/routes/WorkPeriods/components/PeriodListHead/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const HEAD_CELLS = [
{ label: "Team Name", id: SORT_BY.TEAM_NAME, disableSort: true },
{ label: "Start Date", id: SORT_BY.START_DATE, className: "startDate" },
{ label: "End Date", id: SORT_BY.END_DATE, className: "endDate" },
{ label: "Alert", id: SORT_BY.ALERT, disableSort: true, className: "alert" },
{ label: "Weekly Rate", id: SORT_BY.WEEKLY_RATE, className: "weeklyRate" },
{ label: "Total Paid", id: SORT_BY.PAYMENT_TOTAL, className: "totalPaid" },
{ label: "Status", id: SORT_BY.PAYMENT_STATUS },
Expand Down
33 changes: 28 additions & 5 deletions src/store/reducers/workPeriods.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@ import {
SORT_ORDER_DEFAULT,
URL_QUERY_PARAM_MAP,
REASON_DISABLED,
ALERT,
} from "constants/workPeriods";
import {
filterPeriodsByStartDate,
getWeekByDate,
updateOptionMap,
} from "utils/misc";
import {
addReasonDisabled,
addValueImmutable,
createPeriodAlerts,
createAssignedBillingAccountOption,
findReasonsDisabled,
removeReasonDisabled,
removeValueImmutable,
} from "utils/workPeriods";

const cancelSourceDummy = { cancel: () => {} };
Expand Down Expand Up @@ -75,6 +77,7 @@ const initialState = updateStateFromQuery(window.location.search, {
isSelectedPeriodsVisible: false,
pagination: initPagination(),
periods: [],
periodsAlerts: {},
periodsById: {},
periodsData: [{}],
periodsDetails: {},
Expand Down Expand Up @@ -102,6 +105,7 @@ const actionHandlers = {
isSelectedPeriodsAll: false,
isSelectedPeriodsVisible: false,
periods: [],
periodsAlerts: {},
periodsById: {},
periodsData: [{}],
periodsDetails: {},
Expand All @@ -119,16 +123,22 @@ const actionHandlers = {
oldPagination.pageCount !== pageCount
? { ...oldPagination, totalCount, pageCount }
: oldPagination;
const periodsAlerts = {};
const periodsById = {};
const periodsData = {};
const periodsDisabledMap = new Map();
const periodEndDate = state.filters.dateRange[1];
for (let period of periods) {
periodsById[period.id] = true;
periodsData[period.id] = initPeriodData(period);
let reasonsDisabled = findReasonsDisabled(period);
if (reasonsDisabled) {
periodsDisabledMap.set(period.id, reasonsDisabled);
}
let alerts = createPeriodAlerts(period, periodEndDate);
if (alerts) {
periodsAlerts[period.id] = alerts;
}
delete period.data;
}
return {
Expand All @@ -137,6 +147,7 @@ const actionHandlers = {
error: null,
pagination,
periods,
periodsAlerts,
periodsById,
periodsData: [periodsData],
periodsDisabled: [periodsDisabledMap],
Expand Down Expand Up @@ -342,7 +353,7 @@ const actionHandlers = {
// updating reasons for which the period's selection may be disabled
const periodsDisabledMap = state.periodsDisabled[0];
const oldReasonsDisabled = periodsDisabledMap.get(periodId);
const reasonsDisabled = removeReasonDisabled(
const reasonsDisabled = removeValueImmutable(
oldReasonsDisabled,
REASON_DISABLED.NO_BILLING_ACCOUNT
);
Expand All @@ -355,6 +366,18 @@ const actionHandlers = {
state.periodsDisabled = [periodsDisabledMap];
updateSelectedPeriodsFlags(state);
}
// updating period's alerts
const periodsAlerts = state.periodsAlerts;
const oldAlerts = periodsAlerts[periodId];
const alerts = removeValueImmutable(oldAlerts, ALERT.BA_NOT_ASSIGNED);
if (oldAlerts !== alerts) {
if (alerts) {
periodsAlerts[periodId] = alerts;
} else {
delete periodsAlerts[periodId];
}
state.periodsAlerts = { ...periodsAlerts };
}
return state;
},
[ACTION_TYPE.WP_SET_DETAILS_HIDE_PAST_PERIODS]: (
Expand Down Expand Up @@ -699,11 +722,11 @@ function updateStateAfterWorkingDaysChange(periodId, state) {
const oldReasonsDisabled = periodsDisabledMap.get(periodId);
let reasonsDisabled =
periodData.daysWorked === periodData.daysPaid
? addReasonDisabled(
? addValueImmutable(
oldReasonsDisabled,
REASON_DISABLED.NO_DAYS_TO_PAY_FOR
)
: removeReasonDisabled(
: removeValueImmutable(
oldReasonsDisabled,
REASON_DISABLED.NO_DAYS_TO_PAY_FOR
);
Expand Down
8 changes: 8 additions & 0 deletions src/store/selectors/workPeriods.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ export const getWorkPeriodsStateSlice = (state) => state.workPeriods;
*/
export const getWorkPeriods = (state) => state.workPeriods.periods;

/**
* Returns an object with period ids as keys and alerts' arrays as values;
*
* @param {Object} state redux root state
* @returns {Object}
*/
export const getWorkPeriodsAlerts = (state) => state.workPeriods.periodsAlerts;

/**
* Returns working periods' details.
*
Expand Down
Loading