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

Implemented the upper constraint for the working days #64

Merged
merged 1 commit into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions src/constants/workPeriods.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const WORK_PERIODS_API_URL = `${API.V5}/work-periods`;
export const TAAS_TEAM_API_URL = `${API.V5}/taas-teams`;

export const DATE_FORMAT_API = "YYYY-MM-DD";
export const DATE_FORMAT_ISO = "YYYY-MM-DD";
export const DATE_FORMAT_UI = "MMM DD, YYYY";

// Field names that are required to be retrieved for display, filtering and sorting.
Expand All @@ -52,6 +53,7 @@ export const API_REQUIRED_FIELDS = [
"workPeriods.daysPaid",
"workPeriods.payments.amount",
"workPeriods.payments.challengeId",
"workPeriods.payments.createdAt",
"workPeriods.payments.days",
"workPeriods.payments.id",
"workPeriods.payments.memberRate",
Expand Down
4 changes: 4 additions & 0 deletions src/routes/WorkPeriods/components/PeriodDetails/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ const PeriodDetails = ({
</span>
</div>
<PeriodsHistory
bookingStart={period.bookingStart}
bookingEnd={period.bookingEnd}
isDisabled={isDisabled}
periods={periodsVisible}
/>
Expand Down Expand Up @@ -177,6 +179,8 @@ PeriodDetails.propTypes = {
rbId: PT.string.isRequired,
jobId: PT.string.isRequired,
billingAccountId: PT.number.isRequired,
bookingStart: PT.string.isRequired,
bookingEnd: PT.string.isRequired,
}).isRequired,
};

Expand Down
20 changes: 13 additions & 7 deletions src/routes/WorkPeriods/components/PeriodItem/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ import {
updateWorkPeriodWorkingDays,
} from "store/thunks/workPeriods";
import { useUpdateEffect } from "utils/hooks";
import { formatUserHandleLink, formatWeeklyRate } from "utils/formatters";
import {
formatDate,
formatUserHandleLink,
formatWeeklyRate,
} from "utils/formatters";
import { stopPropagation } from "utils/misc";
import styles from "./styles.module.scss";
import PeriodAlerts from "../PeriodAlerts";
Expand Down Expand Up @@ -178,8 +182,8 @@ const PeriodItem = ({
<ProjectName projectId={item.projectId} />
</Tooltip>
</td>
<td className={styles.startDate}>{item.startDate}</td>
<td className={styles.endDate}>{item.endDate}</td>
<td className={styles.startDate}>{formatDate(item.bookingStart)}</td>
<td className={styles.endDate}>{formatDate(item.bookingEnd)}</td>
<td className={styles.alert}>
<PeriodAlerts alerts={alerts} />
</td>
Expand Down Expand Up @@ -208,12 +212,14 @@ const PeriodItem = ({
</td>
<td className={styles.daysWorked}>
<PeriodWorkingDays
updateHintTimeout={2000}
bookingStart={item.bookingStart}
bookingEnd={item.bookingEnd}
controlName={`wp_wrk_days_${item.id}`}
data={data}
isDisabled={isDisabled}
onWorkingDaysChange={onWorkingDaysChange}
onWorkingDaysUpdateHintTimeout={onWorkingDaysUpdateHintTimeout}
updateHintTimeout={2000}
/>
</td>
</tr>
Expand Down Expand Up @@ -266,14 +272,14 @@ PeriodItem.propTypes = {
projectId: PT.oneOfType([PT.number, PT.string]).isRequired,
userHandle: PT.string.isRequired,
teamName: PT.oneOfType([PT.number, PT.string]).isRequired,
startDate: PT.string.isRequired,
endDate: PT.string.isRequired,
bookingStart: PT.string.isRequired,
bookingEnd: PT.string.isRequired,
weeklyRate: PT.number,
}).isRequired,
alerts: PT.arrayOf(PT.string),
data: PT.shape({
daysWorked: PT.number.isRequired,
daysPaid: PT.number.isRequired,
daysWorked: PT.number.isRequired,
paymentErrorLast: PT.object,
payments: PT.array,
paymentStatus: PT.string.isRequired,
Expand Down
119 changes: 95 additions & 24 deletions src/routes/WorkPeriods/components/PeriodWorkingDays/index.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import React from "react";
import React, { useMemo } from "react";
import PT from "prop-types";
import cn from "classnames";
import IntegerField from "components/IntegerField";
import Tooltip from "components/Tooltip";
import IconCheckmarkCircled from "components/Icons/CheckmarkCircled";
import { formatDate } from "utils/formatters";
import { stopPropagation } from "utils/misc";
import styles from "./styles.module.scss";

/**
* Displays working days input field with an icon hinting about the update.
*
* @param {Object} props component properties
* @param {string} props.bookingStart resource booking start date
* @param {string} props.bookingEnd resource booking end date
* @param {string} [props.className] class name to be added to root element
* @param {string} props.controlName working days input control name
* @param {Object} props.data working period data object
Expand All @@ -22,42 +26,109 @@ import styles from "./styles.module.scss";
* @returns {JSX.Element}
*/
const PeriodWorkingDays = ({
bookingStart,
bookingEnd,
className,
controlName,
data,
data: { daysPaid, daysWorked, daysWorkedMax, daysWorkedIsUpdated },
isDisabled,
onWorkingDaysChange,
onWorkingDaysUpdateHintTimeout,
updateHintTimeout = 2000,
}) => (
<div className={cn(styles.container, className)}>
<span className={styles.iconPlaceholder}>
{data.daysWorkedIsUpdated && (
<IconCheckmarkCircled
className={styles.checkmarkIcon}
onTimeout={onWorkingDaysUpdateHintTimeout}
timeout={updateHintTimeout}
}) => {
const isBtnMinusDisabled = daysWorked > 0 && daysWorked <= daysPaid;
const isBtnPlusDisabled = daysWorked < 5 && daysWorked >= daysWorkedMax;
const decreaseDaysWorkedMessage = useMemo(
() => `Cannot decrease "Working Days" below the number of days already
paid for: ${daysPaid}`,
[daysPaid]
);
const increaseDaysWorkedMessage = useMemo(
() => `Cannot increase "Working Days" because the Resource Booking period
is between ${formatDate(bookingStart)} and ${formatDate(bookingEnd)}`,
[bookingStart, bookingEnd]
);

return (
<div className={cn(styles.container, className)}>
<span className={styles.iconPlaceholder}>
{daysWorkedIsUpdated && (
<IconCheckmarkCircled
className={styles.checkmarkIcon}
onTimeout={onWorkingDaysUpdateHintTimeout}
timeout={updateHintTimeout}
/>
)}
</span>
<div
className={styles.daysWorkedControl}
onClick={stopPropagation}
role="button"
tabIndex={0}
>
<input
disabled={isDisabled}
readOnly
className={styles.input}
name={controlName}
value={daysWorked}
/>
)}
</span>
<IntegerField
className={styles.daysWorkedControl}
isDisabled={isDisabled}
name={controlName}
onChange={onWorkingDaysChange}
maxValue={5}
minValue={data.daysPaid}
value={data.daysWorked}
/>
</div>
);
<Tooltip
className={styles.btnMinus}
targetClassName={cn(styles.tooltipTarget, {
[styles.notAllowed]: isBtnMinusDisabled,
})}
tooltipClassName={styles.tooltip}
content={decreaseDaysWorkedMessage}
isDisabled={!isBtnMinusDisabled || isDisabled}
strategy="fixed"
>
<button
className={styles.btnMinus}
disabled={isBtnMinusDisabled}
onClick={(event) => {
event.stopPropagation();
if (!isDisabled) {
onWorkingDaysChange(Math.max(daysWorked - 1, daysPaid));
}
}}
/>
</Tooltip>
<Tooltip
className={styles.btnPlus}
targetClassName={cn(styles.tooltipTarget, {
[styles.notAllowed]: isBtnPlusDisabled,
})}
tooltipClassName={styles.tooltip}
content={increaseDaysWorkedMessage}
isDisabled={!isBtnPlusDisabled || isDisabled}
strategy="fixed"
>
<button
className={styles.btnPlus}
disabled={isBtnPlusDisabled}
onClick={(event) => {
event.stopPropagation();
if (!isDisabled) {
onWorkingDaysChange(Math.min(daysWorked + 1, daysWorkedMax));
}
}}
/>
</Tooltip>
</div>
</div>
);
};

PeriodWorkingDays.propTypes = {
bookingStart: PT.string.isRequired,
bookingEnd: PT.string.isRequired,
className: PT.string,
controlName: PT.string.isRequired,
data: PT.shape({
daysPaid: PT.number.isRequired,
daysWorked: PT.number.isRequired,
daysWorkedMax: PT.number.isRequired,
daysWorkedIsUpdated: PT.bool.isRequired,
}).isRequired,
isDisabled: PT.bool.isRequired,
Expand Down
123 changes: 123 additions & 0 deletions src/routes/WorkPeriods/components/PeriodWorkingDays/styles.module.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@import "styles/variables";

.container {
display: flex;
align-items: baseline;
Expand All @@ -17,5 +19,126 @@
}

.daysWorkedControl {
position: relative;
display: inline-flex;
align-items: center;
border: 1px solid $control-border-color;
border-radius: 6px;
width: 100px;
overflow: hidden;
}

input.input {
flex: 1 1 0;
margin: 0;
border: none !important;
padding: 3px 0;
height: 28px;
line-height: 22px;
background: #fff;
outline: none !important;
box-shadow: none !important;
text-align: center;

&:disabled {
border-color: $control-disabled-border-color;
background-color: $control-disabled-bg-color;
color: $control-disabled-text-color;
cursor: not-allowed;

~ .btnMinus,
~ .btnPlus {
cursor: not-allowed;
}
}
}

.tooltip {
max-width: 400px;
}

.tooltipTarget {
display: block;
width: 100%;
height: 100%;

&.notAllowed {
cursor: not-allowed;
}
}

.btnMinus,
.btnPlus {
position: absolute;
top: 0;
bottom: 0;
margin: auto;
width: 30px;
height: 30px;

button {
display: block;
position: relative;
border: none;
padding: 0;
width: 100%;
height: 100%;
background: transparent;
outline: none !important;
cursor: pointer;

&:disabled {
background: #ddd;
opacity: 1;
}
}
}

.btnMinus {
left: 0;

button {
&::before {
content: "";
display: block;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
width: 8px;
height: 1px;
background: #7f7f7f;
}
}
}

.btnPlus {
right: 0;

button {
&::before,
&::after {
content: "";
display: block;
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
margin: auto;
background: #7f7f7f;
}

&::before {
width: 9px;
height: 1px;
}

&::after {
width: 1px;
height: 9px;
}
}
}
Loading