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

Commit a1d3aab

Browse files
committed
Added tooltip with error messages for work period selection checkbox.
1 parent 99ac1d1 commit a1d3aab

File tree

12 files changed

+306
-114
lines changed

12 files changed

+306
-114
lines changed

src/components/Tooltip/index.jsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import compStyles from "./styles.module.scss";
1212
* @param {string} [props.className] class name to be added to root element
1313
* @param {any} props.content tooltip content
1414
* @param {number} [props.delay] postpone showing the tooltip after this delay
15+
* @param {boolean} [props.isDisabled] whether the tooltip is disabled
1516
* @param {import('@popperjs/core').Placement} [props.placement] tooltip's
1617
* preferred placement as defined in PopperJS documentation
1718
* @param {'absolute'|'fixed'} [props.strategy] tooltip positioning strategy
@@ -27,6 +28,7 @@ const Tooltip = ({
2728
className,
2829
content,
2930
delay = 150,
31+
isDisabled = false,
3032
placement = "top",
3133
strategy = "absolute",
3234
targetClassName,
@@ -83,16 +85,16 @@ const Tooltip = ({
8385
<div
8486
className={cn(compStyles.container, className)}
8587
ref={containerRef}
86-
onMouseEnter={onMouseEnter}
87-
onMouseLeave={onMouseLeave}
88+
onMouseEnter={isDisabled ? null : onMouseEnter}
89+
onMouseLeave={isDisabled ? null : onMouseLeave}
8890
>
8991
<span
9092
className={cn(compStyles.target, targetClassName)}
9193
ref={setReferenceElement}
9294
>
9395
{children}
9496
</span>
95-
{isTooltipShown && (
97+
{!isDisabled && isTooltipShown && (
9698
<div
9799
ref={setPopperElement}
98100
className={cn(compStyles.tooltip, tooltipClassName)}
@@ -116,6 +118,7 @@ Tooltip.propTypes = {
116118
className: PT.string,
117119
content: PT.node,
118120
delay: PT.number,
121+
isDisabled: PT.bool,
119122
placement: PT.string,
120123
strategy: PT.oneOf(["absolute", "fixed"]),
121124
targetClassName: PT.string,

src/components/Tooltip/styles.module.scss

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
z-index: 8;
1414
border-radius: 8px;
1515
padding: 10px 15px;
16+
line-height: 22px;
1617
box-shadow: 0px 5px 25px #c6c6c6;
1718
background: #fff;
1819

src/constants/workPeriods.js

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as API_SORT_BY from "./workPeriods/apiSortBy";
66
import * as SORT_BY from "./workPeriods/sortBy";
77
import * as SORT_ORDER from "./workPeriods/sortOrder";
88
import * as PAYMENT_STATUS from "./workPeriods/paymentStatus";
9+
import * as REASON_DISABLED from "./workPeriods/reasonDisabled";
910

1011
export {
1112
API_CHALLENGE_PAYMENT_STATUS,
@@ -14,6 +15,7 @@ export {
1415
SORT_BY,
1516
SORT_ORDER,
1617
PAYMENT_STATUS,
18+
REASON_DISABLED,
1719
};
1820

1921
// resource bookings API url
@@ -135,3 +137,10 @@ export const JOB_NAME_ERROR = "<Error loading job>";
135137
export const BILLING_ACCOUNTS_LOADING = "Loading...";
136138
export const BILLING_ACCOUNTS_NONE = "<No accounts available>";
137139
export const BILLING_ACCOUNTS_ERROR = "<Error loading accounts>";
140+
141+
export const REASON_DISABLED_MESSAGE_MAP = {
142+
[REASON_DISABLED.NO_BILLING_ACCOUNT]:
143+
"Billing Account is not set for the Resorce Booking",
144+
[REASON_DISABLED.NO_DAYS_TO_PAY_FOR]: "There are no days to pay for",
145+
[REASON_DISABLED.NO_MEMBER_RATE]: "Member Rate should be greater than 0",
146+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export const NO_BILLING_ACCOUNT = "NO_BILLING_ACCOUNT";
2+
export const NO_DAYS_TO_PAY_FOR = "NO_DAYS_TO_PAY_FOR";
3+
export const NO_MEMBER_RATE = "NO_MEMBER_RATE";

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

+52-10
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import PaymentStatus from "../PaymentStatus";
1212
import PaymentTotal from "../PaymentTotal";
1313
import PeriodWorkingDays from "../PeriodWorkingDays";
1414
import PeriodDetails from "../PeriodDetails";
15-
import { PAYMENT_STATUS } from "constants/workPeriods";
15+
import {
16+
PAYMENT_STATUS,
17+
REASON_DISABLED_MESSAGE_MAP,
18+
} from "constants/workPeriods";
1619
import {
1720
setWorkPeriodWorkingDays,
1821
toggleWorkingDaysUpdated,
@@ -37,6 +40,7 @@ import styles from "./styles.module.scss";
3740
* @param {Object} props.item object describing a working period
3841
* @param {Object} props.data changeable working period data such as working days
3942
* @param {Object} [props.details] object with working period details
43+
* @param {Array} [props.reasonsDisabled] array of REASON_DISABLED values.
4044
* @returns {JSX.Element}
4145
*/
4246
const PeriodItem = ({
@@ -46,6 +50,7 @@ const PeriodItem = ({
4650
item,
4751
data,
4852
details,
53+
reasonsDisabled,
4954
}) => {
5055
const dispatch = useDispatch();
5156

@@ -107,6 +112,15 @@ const PeriodItem = ({
107112
[item.projectId]
108113
);
109114

115+
const reasonsDisabledElement = useMemo(
116+
() => (
117+
<span className={styles.tooltipContent}>
118+
{formatReasonsDisabled(reasonsDisabled)}
119+
</span>
120+
),
121+
[reasonsDisabled]
122+
);
123+
110124
return (
111125
<>
112126
<tr
@@ -117,15 +131,22 @@ const PeriodItem = ({
117131
onClick={onToggleItemDetails}
118132
>
119133
<td className={styles.toggle}>
120-
<Checkbox
121-
size="small"
122-
isDisabled={isDisabled}
123-
checked={isSelected}
124-
name={`wp_chb_${item.id}`}
125-
onChange={onToggleItem}
126-
option={{ value: item.id }}
127-
stopClickPropagation={true}
128-
/>
134+
<Tooltip
135+
content={reasonsDisabledElement}
136+
isDisabled={!reasonsDisabled}
137+
strategy="fixed"
138+
targetClassName={styles.checkboxContainer}
139+
>
140+
<Checkbox
141+
size="small"
142+
isDisabled={isDisabled || !!reasonsDisabled}
143+
checked={isSelected}
144+
name={`wp_chb_${item.id}`}
145+
onChange={onToggleItem}
146+
option={{ value: item.id }}
147+
stopClickPropagation={true}
148+
/>
149+
</Tooltip>
129150
</td>
130151
<td className={styles.userHandle}>
131152
<Tooltip
@@ -235,6 +256,27 @@ PeriodItem.propTypes = {
235256
periods: PT.array.isRequired,
236257
periodsIsLoading: PT.bool.isRequired,
237258
}),
259+
reasonsDisabled: PT.arrayOf(PT.string),
238260
};
239261

240262
export default memo(PeriodItem);
263+
264+
/**
265+
* Returns a string produced by concatenation of all provided reasons some
266+
* working period is disabled.
267+
*
268+
* @param {Array} reasonIds array of REASON_DISABLED values
269+
* @returns {?Array}
270+
*/
271+
function formatReasonsDisabled(reasonIds) {
272+
if (!reasonIds) {
273+
return null;
274+
}
275+
const reasons = [];
276+
reasons.push(REASON_DISABLED_MESSAGE_MAP[reasonIds[0]]);
277+
for (let i = 1, len = reasonIds.length; i < len; i++) {
278+
reasons.push(<br />);
279+
reasons.push(REASON_DISABLED_MESSAGE_MAP[reasonIds[i]]);
280+
}
281+
return reasons;
282+
}

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

+5-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
getWorkPeriods,
1111
getWorkPeriodsData,
1212
getWorkPeriodsDetails,
13+
getWorkPeriodsDisabled,
1314
getWorkPeriodsFailed,
1415
getWorkPeriodsIsProcessingPayments,
1516
getWorkPeriodsSelected,
@@ -27,8 +28,9 @@ const PeriodList = ({ className }) => {
2728
const periods = useSelector(getWorkPeriods);
2829
const [periodsData] = useSelector(getWorkPeriodsData);
2930
const periodsDetails = useSelector(getWorkPeriodsDetails);
31+
const [periodsDisabledMap] = useSelector(getWorkPeriodsDisabled);
3032
const periodsFailed = useSelector(getWorkPeriodsFailed);
31-
const periodsSelected = useSelector(getWorkPeriodsSelected);
33+
const [periodsSelectedSet] = useSelector(getWorkPeriodsSelected);
3234
const isProcessingPayments = useSelector(getWorkPeriodsIsProcessingPayments);
3335

3436
return (
@@ -54,10 +56,11 @@ const PeriodList = ({ className }) => {
5456
key={period.id}
5557
isDisabled={isProcessingPayments}
5658
isFailed={period.id in periodsFailed}
57-
isSelected={period.id in periodsSelected}
59+
isSelected={periodsSelectedSet.has(period.id)}
5860
item={period}
5961
data={periodsData[period.id]}
6062
details={periodsDetails[period.id]}
63+
reasonsDisabled={periodsDisabledMap.get(period.id)}
6164
/>
6265
))}
6366
</tbody>

src/routes/WorkPeriods/components/PeriodsSelectionMessage/index.jsx

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
getWorkPeriodsIsSelectedAll,
77
getWorkPeriodsIsSelectedVisible,
88
getWorkPeriodsPageSize,
9+
getWorkPeriodsSelectedCount,
910
getWorkPeriodsTotalCount,
1011
} from "store/selectors/workPeriods";
1112
import { toggleWorkingPeriodsAll } from "store/actions/workPeriods";
@@ -21,6 +22,7 @@ import styles from "./styles.module.scss";
2122
const PeriodsSelectionMessage = ({ className }) => {
2223
const isSelectedAll = useSelector(getWorkPeriodsIsSelectedAll);
2324
const isSelectedVisible = useSelector(getWorkPeriodsIsSelectedVisible);
25+
const selectedCount = useSelector(getWorkPeriodsSelectedCount);
2426
const pageSize = useSelector(getWorkPeriodsPageSize);
2527
const totalCount = useSelector(getWorkPeriodsTotalCount);
2628
const dispatch = useDispatch();
@@ -35,6 +37,8 @@ const PeriodsSelectionMessage = ({ className }) => {
3537
<span className={styles.message}>
3638
{isSelectedAll
3739
? `All ${totalCount} Records are selected. `
40+
: selectedCount < pageSize
41+
? `${selectedCount} Records on this page are selected. `
3842
: `All ${pageSize} Records on this page are selected. `}
3943
<span
4044
className={styles.button}

0 commit comments

Comments
 (0)