Skip to content

Commit 3347d3e

Browse files
Merge pull request #4465 from OneCommunityGlobal/venkataramanan_add_40_hour_limit_to_add_lost_time
Venkataramanan 🔥 Add: 40 hour limit per entry to add lost time
2 parents 5f924ba + 5ad5379 commit 3347d3e

3 files changed

Lines changed: 139 additions & 57 deletions

File tree

src/components/Header/Header.jsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ export function Header(props) {
7979
const [displayUserId, setDisplayUserId] = useState(user.userid);
8080
const [popup, setPopup] = useState(false);
8181
const [isAuthUser, setIsAuthUser] = useState(true);
82-
const [isAckLoading, setIsAckLoading] = useState(false);
8382
const [ showPromotionsPopup, setShowPromotionsPopup ] = useState(false);
8483

8584
const ALLOWED_ROLES_TO_INTERACT = useMemo(() => ['Owner', 'Administrator'], []);

src/components/Reports/LostTime/AddLostTime.jsx

Lines changed: 67 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ import '../../Header/DarkMode.css'
1212
import { isEmpty, isEqual } from 'lodash';
1313
import { getUserProfile } from '~/actions/userProfile';
1414
import { postTimeEntry } from '~/actions/timeEntries';
15+
import { toast } from 'react-toastify';
1516

1617
function AddLostTime(props) {
1718

1819
const darkMode = useSelector((state) => state.theme.darkMode);
1920
const fontColor = getFontColor(darkMode);
2021
const boxStyling = getBoxStyling(darkMode);
22+
const MAX_HOURS_PER_ENTRY = 40;
2123

2224
const initialForm = {
2325
projectId: undefined,
@@ -234,51 +236,89 @@ function AddLostTime(props) {
234236
props.toggle();
235237
};
236238

237-
const validateInputs = () => {
238-
const result = {};
239-
240-
const date = moment(inputs.dateOfWork);
239+
const validateDate = (dateOfWork) => {
240+
const errors = {};
241+
const date = moment(dateOfWork);
241242
const today = moment(
242243
moment()
243244
.tz('America/Los_Angeles')
244245
.format('YYYY-MM-DD'),
245246
);
247+
246248
if (!date.isValid()) {
247-
result.dateOfWork = 'Invalid date';
248-
} else if (
249-
today.diff(date, 'days') < 0
250-
) {
251-
result.dateOfWork = 'Cannot add lost time for future dates.';
249+
errors.dateOfWork = 'Invalid date';
250+
} else if (today.diff(date, 'days') < 0) {
251+
errors.dateOfWork = 'Cannot add lost time for future dates.';
252252
}
253253

254-
if (inputs.hours === 0 && inputs.minutes === 0) {
255-
result.time = 'Time is required';
256-
} else {
257-
const hours = inputs.hours * 1;
258-
const minutes = inputs.minutes * 1;
259-
if (!Number.isInteger(hours) || !Number.isInteger(minutes)) {
260-
result.time = 'Hours and minutes should be integers';
261-
}
262-
if (hours < 0 || minutes < 0) {
263-
result.time = 'Time should be greater than 0';
264-
}
254+
return errors;
255+
};
256+
257+
const getTimeError = (hoursInput, minutesInput) => {
258+
const hours = Number(hoursInput);
259+
const minutes = Number(minutesInput);
260+
261+
if (Number.isNaN(hours) || Number.isNaN(minutes) || (hours === 0 && minutes === 0)) {
262+
return 'Time is required';
265263
}
264+
265+
if (!Number.isInteger(hours) || !Number.isInteger(minutes)) {
266+
return 'Hours and minutes should be integers';
267+
}
268+
269+
if (hours < 0 || minutes < 0) {
270+
return 'Time should be greater than 0';
271+
}
272+
273+
const totalMinutes = hours * 60 + minutes;
274+
const maxMinutes = MAX_HOURS_PER_ENTRY * 60;
275+
276+
if (totalMinutes > maxMinutes) {
277+
toast.error(
278+
"Hold up, workhorse! You’ve hit the 40-hour limit for a single entry. You can pop in a new time log for any additional hours.",
279+
{ autoClose: 5000 }
280+
);
281+
return `You can’t log more than ${MAX_HOURS_PER_ENTRY} hours in a single entry.`;
282+
}
283+
284+
return null;
285+
};
286+
287+
const validateEntryType = (entryType, inputs) => {
288+
const errors = {};
289+
266290
if (entryType === '') {
267-
result.events = 'Type is required';
291+
errors.events = 'Type is required';
292+
return errors;
268293
}
294+
269295
if (entryType === 'project' && inputs.projectId === undefined) {
270-
result.projectId = 'Project is required';
296+
errors.projectId = 'Project is required';
297+
} else if (entryType === 'person' && inputs.personId === undefined) {
298+
errors.personId = 'Person is required';
299+
} else if (entryType === 'team' && inputs.teamId === undefined) {
300+
errors.teamId = 'Team is required';
271301
}
272-
if (entryType === 'person' && inputs.personId === undefined) {
273-
result.personId = 'Person is required';
274-
}
275-
if (entryType === 'team' && inputs.teamId === undefined) {
276-
result.teamId = 'Team is required';
302+
303+
return errors;
304+
};
305+
306+
const validateInputs = () => {
307+
const result = {};
308+
309+
Object.assign(result, validateDate(inputs.dateOfWork));
310+
311+
const timeError = getTimeError(inputs.hours, inputs.minutes);
312+
if (timeError) {
313+
result.time = timeError;
277314
}
278315

316+
Object.assign(result, validateEntryType(entryType, inputs));
317+
279318
setErrors(result);
280319
return isEmpty(result);
281320
};
321+
282322

283323
const handleSubmit = async event => {
284324
if (event) event.preventDefault();

src/components/Reports/LostTime/EditHistoryModal.jsx

Lines changed: 72 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@ import { isEmpty } from 'lodash';
88
import { deleteTimeEntry, editTimeEntry } from '~/actions/timeEntries';
99
import './EditHistoryModal.css';
1010
import '../../Header/DarkMode.css'
11+
import { toast } from 'react-toastify';
1112

1213
function EditHistoryModal(props) {
1314
const {darkMode} = props;
1415
const fontColor = getFontColor(darkMode);
1516
const boxStyling = getBoxStyling(darkMode);
17+
const MAX_HOURS_PER_ENTRY = 40;
1618

1719
const initialForm = {
1820
projectId: props.entryType === 'project'? props.dataId: undefined,
@@ -110,50 +112,91 @@ function EditHistoryModal(props) {
110112
toggleEdit();
111113
};
112114

113-
const validateInputs = () => {
114-
const result = {};
115-
116-
const date = moment(inputs.dateOfWork);
115+
const validateDate = (dateOfWork) => {
116+
const errors = {};
117+
const date = moment(dateOfWork);
117118
const today = moment(
118119
moment()
119120
.tz('America/Los_Angeles')
120121
.format('YYYY-MM-DD'),
121122
);
123+
122124
if (!date.isValid()) {
123-
result.dateOfWork = 'Invalid date';
124-
} else if (
125-
today.diff(date, 'days') < 0
125+
errors.dateOfWork = 'Invalid date';
126+
} else if (today.diff(date, 'days') < 0) {
127+
errors.dateOfWork = 'Cannot add lost time for future dates.';
128+
}
129+
130+
return errors;
131+
};
132+
133+
const validateTime = (hoursInput, minutesInput) => {
134+
const errors = {};
135+
const hours = Number(hoursInput);
136+
const minutes = Number(minutesInput);
137+
138+
if (
139+
Number.isNaN(hours) ||
140+
Number.isNaN(minutes) ||
141+
(hours === 0 && minutes === 0)
126142
) {
127-
result.dateOfWork = 'Cannot add lost time for future dates.';
143+
errors.time = 'Time is required';
144+
return errors;
128145
}
129-
130-
if (inputs.hours === 0 && inputs.minutes === 0) {
131-
result.time = 'Time is required';
132-
} else {
133-
const hours = inputs.hours * 1;
134-
const minutes = inputs.minutes * 1;
135-
if (!Number.isInteger(hours) || !Number.isInteger(minutes)) {
136-
result.time = 'Hours and minutes should be integers';
137-
}
138-
if (hours < 0 || minutes < 0) {
139-
result.time = 'Time should be greater than 0';
140-
}
146+
147+
if (!Number.isInteger(hours) || !Number.isInteger(minutes)) {
148+
errors.time = 'Hours and minutes should be integers';
149+
return errors;
141150
}
142-
143-
if (props.entryType === 'project' && inputs.projectId === undefined) {
144-
result.projectId = 'Project is required';
151+
152+
if (hours < 0 || minutes < 0) {
153+
errors.time = 'Time should be greater than 0';
154+
return errors;
145155
}
146-
if (props.entryType === 'person' && inputs.personId === undefined) {
147-
result.personId = 'Person is required';
156+
157+
const totalMinutes = hours * 60 + minutes;
158+
const maxMinutes = MAX_HOURS_PER_ENTRY * 60;
159+
160+
if (totalMinutes > maxMinutes) {
161+
errors.time = `You can’t log more than ${MAX_HOURS_PER_ENTRY} hours in a single entry.`;
162+
toast.error(
163+
"Hold up, workhorse! You’ve hit the 40-hour limit for a single entry. You can pop in a new time log for any additional hours.",
164+
{ autoClose: 5000 }
165+
);
148166
}
149-
if (props.entryType === 'team' && inputs.teamId === undefined) {
150-
result.teamId = 'Team is required';
167+
168+
return errors;
169+
};
170+
171+
const validateEntryType = (inputs, entryType) => {
172+
const errors = {};
173+
174+
if (entryType === 'project' && inputs.projectId === undefined) {
175+
errors.projectId = 'Project is required';
176+
} else if (entryType === 'person' && inputs.personId === undefined) {
177+
errors.personId = 'Person is required';
178+
} else if (entryType === 'team' && inputs.teamId === undefined) {
179+
errors.teamId = 'Team is required';
151180
}
152-
181+
182+
return errors;
183+
};
184+
185+
const validateInputs = () => {
186+
const dateErrors = validateDate(inputs.dateOfWork);
187+
const timeErrors = validateTime(inputs.hours, inputs.minutes);
188+
const entryTypeErrors = validateEntryType(inputs, props.entryType);
189+
190+
const result = {
191+
...dateErrors,
192+
...timeErrors,
193+
...entryTypeErrors,
194+
};
195+
153196
setErrors(result);
154197
return isEmpty(result);
155198
};
156-
199+
157200
const handleSubmit = async event => {
158201

159202
if (event) event.preventDefault();

0 commit comments

Comments
 (0)