From ee82f65a7c6c7a379af14f4b248e8d595264f2e9 Mon Sep 17 00:00:00 2001 From: One Community Date: Mon, 4 Mar 2024 21:06:42 -0800 Subject: [PATCH] Revert "Revert "Abdel update bleu square scheduler"" --- src/actions/reasonsActions.js | 43 -- src/actions/timeOffRequestAction.js | 2 +- .../Dashboard/TimeOffRequestDetailModal.jsx | 101 +-- src/components/LeaderBoard/Leaderboard.jsx | 1 + .../TeamMemberTasks/TeamMemberTask.jsx | 67 +- src/components/TeamMemberTasks/style.css | 31 +- .../UserManagement/logTimeOffPopUp.jsx | 137 ++-- .../UserManagement/usermanagement.css | 2 +- .../UserProfile/BlueSquareLayout.jsx | 294 +++----- .../UserProfile/BlueSquares/BlueSquare.jsx | 70 +- .../ScheduleExplanationModal.jsx | 132 +++- .../ScheduleReasonModal.css | 34 +- .../ScheduleReasonModal.jsx | 667 +++++++++++++----- .../ScheduleReasonModalCard.jsx | 48 ++ src/components/UserProfile/UserProfile.jsx | 1 + .../UserProfileEdit/UserProfileEdit.scss | 246 ++++--- src/utils/URL.js | 17 - 17 files changed, 1096 insertions(+), 797 deletions(-) delete mode 100644 src/actions/reasonsActions.js create mode 100644 src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModalCard.jsx diff --git a/src/actions/reasonsActions.js b/src/actions/reasonsActions.js deleted file mode 100644 index dfd3f933d6..0000000000 --- a/src/actions/reasonsActions.js +++ /dev/null @@ -1,43 +0,0 @@ -import axios from 'axios'; -import { ENDPOINTS } from 'utils/URL'; - -export async function addReason(userId, reasonData) { - try { - const url = ENDPOINTS.CREATEREASON(userId); - const response = await axios.post(url, { userId: userId, reasonData: reasonData }); - return Promise.resolve(response) - } catch (error) { - return {message: error.response.data.message, errorCode: error.response.data.message, status: error.response.status} - } -} - -export async function getReasonByDate(userId, queryDate){ - try { - const url = ENDPOINTS.GETSINGLEREASONBYID(userId); - const response = await axios.get(url, {params: {queryDate: queryDate }}); - return Promise.resolve(response) - } catch (error) { - return {message: error.response.data.message, errorCode: error.response.data.message, status: error.response.status} - } -} - -export async function patchReason(userId, reasonData) { - try { - const url = ENDPOINTS.PATCHUSERREASONBYID(userId); - const response = await axios.patch(url, { userId: userId, reasonData: reasonData }); - return Promise.resolve(response) - } catch (error) { - return {message: error.response.data.message, errorCode: error.response.data.message, status: error.response.status} - } - } - -// gets all scheduled reasons - Sucheta -export async function getAllReasons(userId){ - try{ - const url = ENDPOINTS.GETALLUSERREASONS(userId); - const response = await axios.get(url); - return Promise.resolve(response) - }catch(error){ - return {message: error.response.data.message, errorCode:error.response.data.message, status: error.repsonse.status} - } -} \ No newline at end of file diff --git a/src/actions/timeOffRequestAction.js b/src/actions/timeOffRequestAction.js index 4d45003c01..f9338c631d 100644 --- a/src/actions/timeOffRequestAction.js +++ b/src/actions/timeOffRequestAction.js @@ -129,7 +129,7 @@ const isTimeOffRequestIncludeCurrentWeek = request => { const requestEndingDate = moment(endingDate); const currentWeekStart = moment().startOf('week').add(1, 'second'); - const currentWeekEnd = moment().endOf('week').subtract(1, 'second'); + const currentWeekEnd = moment().endOf('week').subtract(1, 'day').subtract(1, 'second'); // Check if the current week falls within the date range of the request if ( diff --git a/src/components/Dashboard/TimeOffRequestDetailModal.jsx b/src/components/Dashboard/TimeOffRequestDetailModal.jsx index 19a6ddfb5a..91ca547a19 100644 --- a/src/components/Dashboard/TimeOffRequestDetailModal.jsx +++ b/src/components/Dashboard/TimeOffRequestDetailModal.jsx @@ -1,4 +1,4 @@ -import { Modal, ModalHeader, ModalBody, Row, Col } from 'reactstrap'; +import { Modal, ModalHeader, ModalBody, Row, Col, Container } from 'reactstrap'; import moment from 'moment'; import 'moment-timezone'; import { useSelector, useDispatch } from 'react-redux'; @@ -11,54 +11,73 @@ const TimeOffRequestDetailModal = () => { dispatch(hideTimeOffRequestModal()); }; + const getWeekIntervals = data => { + const dateOfLeaveStr = moment(data.startingDate) + .tz('America/Los_Angeles') + .format() + .split('T')[0]; + const intervals = []; + let startDate = moment(dateOfLeaveStr); + for (let i = 0; i < data.duration; i++) { + const endDate = startDate.clone().endOf('week'); + intervals.push([startDate.format('MM-DD-YYYY'), endDate.format('MM-DD-YYYY')]); + startDate = startDate.add(1, 'week').startOf('week'); + } + return [intervals, startDate]; + }; + return (
detailModalClose()} returnFocusAfterClose={true}> detailModalClose()}>Time Off Details - - -

+ + + {data?.onVacation ? `${data?.name} Is Not Available this Week` : `${data?.name} Is Not Available Next Week`} -

- -
- - -

- {data?.onVacation - ? `${data?.name} Is on vacation:` - : `${data?.name} Is going on vacation:`} -

- -
- - -

From:

-

- {moment(data?.startingDate).format('YYYY-MM-DD')} -

- -
- {/*
- -
*/} - - -

To:

-

- {moment(data?.endingDate).format('YYYY-MM-DD')} -

- -
- - -

For The Following reason:

-

{data?.reason}

- -
+ + + + + {' '} + {`${data?.name} is going to be absent for the following`} + {getWeekIntervals(data)[0]?.length > 1 ? ` weeks:` : ` week:`} + + + + + {getWeekIntervals(data)[0].map((week, index) => ( +
  • + {`From `} + {week[0]} + {` To `} + {week[1]} +
  • + ))} + +
    + + Due to the reason of: + + + +
  • {data?.reason}
  • + +
    + + The return day is: + + + +
  • + {`On `} + {getWeekIntervals(data)[1].format('MM-DD-YYYY')} +
  • + +
    +
    diff --git a/src/components/LeaderBoard/Leaderboard.jsx b/src/components/LeaderBoard/Leaderboard.jsx index 9e24f0afe4..0f68764870 100644 --- a/src/components/LeaderBoard/Leaderboard.jsx +++ b/src/components/LeaderBoard/Leaderboard.jsx @@ -340,6 +340,7 @@ function LeaderBoard({ (userOnTimeOff[item.personId] || userGoingOnTimeOff[item.personId]) && (
    -
    - )} + ) : ( +
    + + + + {onTimeOff + ? `${user.name} Is Not Available this Week` + : `${user.name} Is Not Available Next Week`} + + +
    + ))} diff --git a/src/components/TeamMemberTasks/style.css b/src/components/TeamMemberTasks/style.css index 715837e582..9d25aaa4b0 100644 --- a/src/components/TeamMemberTasks/style.css +++ b/src/components/TeamMemberTasks/style.css @@ -259,26 +259,35 @@ margin: 10px; } -.time-off-detail-modal-title { - font-size: 1.1rem; +.time-off-detail-modal-section { margin-bottom: 10px; - font-weight: bold; } -.time-off-detail-modal-sub-heading { - font-size: 1rem; - font-weight: bold; +.compress-time-off-detail-button{ + color: white; + position: absolute; + right: 1px; + bottom: 1px; + border: none; + background-color: transparent; + border-radius: 5px; } -.time-off-detail-modal-sub-section { - font-size: 1rem; - margin-bottom: 10px; +.compress-time-off-detail-button:hover,.expand-time-off-detail-button:hover{ + background-color: rgba(0, 0, 25, 0.6); + } -.time-off-detail-modal-section { - margin-bottom: 10px; +.expand-time-off-detail-button{ + color: white; + position: absolute; + right: 1px; + bottom: 1px; + background-color: rgba(0, 0, 25, 0.7); + border-radius: 5px; } + @media only screen and (max-width: 768px) { .pc-component { display: none; diff --git a/src/components/UserManagement/logTimeOffPopUp.jsx b/src/components/UserManagement/logTimeOffPopUp.jsx index c1080cf778..a006463e49 100644 --- a/src/components/UserManagement/logTimeOffPopUp.jsx +++ b/src/components/UserManagement/logTimeOffPopUp.jsx @@ -1,4 +1,4 @@ -import React, { useState ,useEffect } from 'react'; +import React, { useState, useEffect } from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { addTimeOffRequestThunk, @@ -33,8 +33,10 @@ const LogTimeOffPopUp = React.memo(props => { const dispatch = useDispatch(); const allRequests = useSelector(state => state.timeOffRequests.requests); const today = moment().format('YYYY-MM-DD'); - const nextSundayStr = moment().isoWeekday(7).startOf('day') - const nextSunday = new Date(nextSundayStr.year(), nextSundayStr.month(), nextSundayStr.date()) + const nextSundayStr = moment() + .isoWeekday(7) + .startOf('day'); + const nextSunday = new Date(nextSundayStr.year(), nextSundayStr.month(), nextSundayStr.date()); const initialRequestData = { dateOfLeave: nextSunday, @@ -206,8 +208,10 @@ const LogTimeOffPopUp = React.memo(props => { }; // checks if date of leave is not before the start of current week const validateDateIsNotBeforeStartOfCurrentWeek = data => { - - const isBeforeToday = moment(getDateWithoutTimeZone(data.dateOfLeave)).isBefore(moment().startOf('week'), 'day'); + const isBeforeToday = moment(getDateWithoutTimeZone(data.dateOfLeave)).isBefore( + moment().startOf('week'), + 'day', + ); if (isBeforeToday) { setRequestDataErrors(prev => ({ ...prev, @@ -220,22 +224,28 @@ const LogTimeOffPopUp = React.memo(props => { }; // checks if the newly added request doesn't overlap with existing ones const checkIfRequestOverlapsWithOtherRequests = data => { - const dataStartingDate = moment(getDateWithoutTimeZone(data.dateOfLeave)).tz('America/Los_Angeles') - const dataEndingDate = moment(data.dateOfLeave).tz('America/Los_Angeles').add(Number(data.numberOfWeeks), 'week') + const dataStartingDate = moment(getDateWithoutTimeZone(data.dateOfLeave)).startOf('day'); + const dataEndingDate = moment(getDateWithoutTimeZone(data.dateOfLeave)) + .add(Number(data.numberOfWeeks), 'week') + .subtract(1, 'day') + .startOf('day'); + if (allRequests[props.user._id]?.length > 0) { const isAnyOverlapingRequests = allRequests[props.user._id].some(request => { - const requestStartingDate = moment(request.startingDate).tz('America/Los_Angeles'); - const requestEndingDate = moment(request.endingDate).tz('America/Los_Angeles') - const startingDateIsBetween = dataStartingDate.isBetween(requestStartingDate, requestEndingDate) - const endingDateIsBetween = dataEndingDate.isBetween(requestStartingDate, requestEndingDate) + const requestStartingDate = moment(request.startingDate.split('T')[0]).startOf('day'); + const requestEndingDate = moment(request.endingDate.split('T')[0]).startOf('day'); - if (startingDateIsBetween || endingDateIsBetween) { - return true + if ( + (dataStartingDate.isSameOrAfter(requestStartingDate) && + dataStartingDate.isSameOrBefore(requestEndingDate)) || + (dataEndingDate.isSameOrAfter(requestStartingDate) && + dataEndingDate.isSameOrBefore(requestEndingDate)) + ) { + return true; } - return false + return false; }); - - + if (isAnyOverlapingRequests) { setRequestDataErrors(prev => ({ ...prev, @@ -273,7 +283,7 @@ const LogTimeOffPopUp = React.memo(props => { if (!validateNumberOfWeeks(updateRequestData, true)) return; if (!validateReasonForLeave(updateRequestData, true)) return; if (!checkIfRequestOverlapsWithOtherRequests(updateRequestData)) return; - + const data = { reason: updateRequestData.reasonForLeave, startingDate: moment(updateRequestData.dateOfLeave) @@ -290,19 +300,19 @@ const LogTimeOffPopUp = React.memo(props => { dispatch(deleteTimeOffRequestThunk(id)); }; - const getDateWithoutTimeZone = date=>{ - const newDateObject = new Date(date); - const day = newDateObject.getDate(); - const month = newDateObject.getMonth() + 1; + const getDateWithoutTimeZone = date => { + const newDateObject = new Date(date); + const day = newDateObject.getDate(); + const month = newDateObject.getMonth() + 1; const year = newDateObject.getFullYear(); - return moment(`${month}-${day}-${year}`, 'MM-DD-YYYY').format('YYYY-MM-DD') - } + return moment(`${month}-${day}-${year}`, 'MM-DD-YYYY').format('YYYY-MM-DD'); + }; const sortRequests = (a, b) => { const momentA = moment(a.startingDate, 'YYYY-MM-DD'); const momentB = moment(b.startingDate, 'YYYY-MM-DD'); return momentA - momentB; -} + }; return ( @@ -316,12 +326,12 @@ const LogTimeOffPopUp = React.memo(props => { { + onChange={date => { setRequestData(prev => ({ ...prev, ['dateOfLeave']: date, - }))} - } + })); + }} filterDate={filterSunday} dateFormat="MM/dd/yyyy" placeholderText="Select a Sunday" @@ -383,41 +393,44 @@ const LogTimeOffPopUp = React.memo(props => { Time Off Requests - {allRequests[props.user._id].slice().sort(sortRequests).map(request => ( - - - - -
    Date of Leave:
    -

    {moment(request.startingDate).format('MM/DD/YYYY')}

    - - -
    Duration (weeks):
    -

    {request.duration}

    - -
    - - -
    Reason for Leave:
    -

    {request.reason}

    - -
    - - - - - - -
    -
    - ))} + {allRequests[props.user._id] + .slice() + .sort(sortRequests) + .map(request => ( + + + + +
    Date of Leave:
    +

    {moment(request.startingDate).format('MM/DD/YYYY')}

    + + +
    Duration (weeks):
    +

    {request.duration}

    + +
    + + +
    Reason for Leave:
    +

    {request.reason}

    + +
    + + + + + + +
    +
    + ))}
    Edit Time Off Request diff --git a/src/components/UserManagement/usermanagement.css b/src/components/UserManagement/usermanagement.css index 334617f8a5..128b393411 100644 --- a/src/components/UserManagement/usermanagement.css +++ b/src/components/UserManagement/usermanagement.css @@ -261,7 +261,7 @@ tr:hover { fill: #0d6efd; } .Logged-time-off-cards-container { - overflow: scroll; + overflow-y: auto; overflow-x: hidden; max-height: 215px; } diff --git a/src/components/UserProfile/BlueSquareLayout.jsx b/src/components/UserProfile/BlueSquareLayout.jsx index 49871d8813..8dfcd1ab86 100644 --- a/src/components/UserProfile/BlueSquareLayout.jsx +++ b/src/components/UserProfile/BlueSquareLayout.jsx @@ -1,175 +1,60 @@ -import React, { useCallback } from 'react'; +import React from 'react'; +import { useSelector, useDispatch } from 'react-redux'; import BlueSquare from './BlueSquares'; import ToggleSwitch from './UserProfileEdit/ToggleSwitch'; import './UserProfile.scss'; -import './UserProfileEdit/UserProfileEdit.scss'; import { Button } from 'react-bootstrap'; import ScheduleExplanationModal from './ScheduleExplanationModal/ScheduleExplanationModal'; import ScheduleReasonModal from './ScheduleReasonModal/ScheduleReasonModal'; -import { useState, useEffect } from 'react'; -import { useReducer } from 'react'; -import Spinner from 'react-bootstrap/Spinner'; -import { addReason, patchReason, getAllReasons } from 'actions/reasonsActions'; -import moment from 'moment-timezone'; +import { useState } from 'react'; +import hasPermission from '../../utils/permissions'; import { Modal } from 'react-bootstrap'; import { boxStyle } from 'styles'; +import './UserProfileEdit/UserProfileEdit.scss'; const BlueSquareLayout = props => { - const fetchingReducer = (state, action) => { - switch (action.type) { - case 'FETCHING_STARTED': - return { - error: false, - success: false, - isFetching: true, - fetchMessage: '', - errorCode: null, - }; - case 'ERROR': - return { - isFetching: false, - error: true, - success: false, - fetchMessage: action.payload.message, - errorCode: action.payload.errorCode, - }; - case 'SUCCESS': - return { ...state, isFetching: false, error: false, success: true, isSet: true }; - case 'FETCHING_FINISHED': - return { - error: false, - success: false, - isFetching: false, - fetchMessage: '', - errorCode: null, - isSet: action.payload.isSet, - }; - default: - return state; - } - }; + const dispatch = useDispatch(); + const allRequests = useSelector(state => state.timeOffRequests.requests); + const canManageTimeOffRequests = dispatch(hasPermission('manageTimeOffRequests')); - const { userProfile, handleUserProfile, handleBlueSquare, canEdit } = props; + const { userProfile, handleUserProfile, handleBlueSquare, canEdit, user } = props; const { privacySettings } = userProfile; const [show, setShow] = useState(false); - // =============================================================== - const [showExplanation, setShowExplanation]= useState(false); - const [allreasons, setAllReasons]= useState(""); - const [numberOfReasons, setNumberOfReasons] = useState(""); - const [isInfringementMoreThanFive, setIsInfringementMoreThanFive]= useState(false); - const [infringementsNum, setInfringementsNum] = useState(""); - const [addsReason, setAddsReason] = useState(false); // this flag will be set to true, each time a scheduled reason has been added - sucheta - // =============================================================== - const [reason, setReason] = useState(''); - const [date, setDate] = useState( - moment - .tz('America/Los_Angeles') - .endOf('week') - .toISOString() - .split('T')[0], - ); - const [IsReasonUpdated,setIsReasonUpdated] = useState(false); - const [fetchState, fetchDispatch] = useReducer(fetchingReducer, { - isFetching: false, - error: false, - success: false, - fetchMessage: '', - errorCode: null, - isSet: false, - }); + const [showExplanation, setShowExplanation] = useState(false); - const handleOpen = useCallback(() => { - setShow(true); - }, []); + const checkIfUserCanScheduleTimeOff = () => { + let scheduledVacation = 0; + allRequests[userProfile._id]?.forEach(element => { + scheduledVacation = scheduledVacation + Number(element.duration); + }); + const blueSquares = Number(userProfile.infringements?.length) || 0; - const handleClose = useCallback(() => { - setShow(false); - }, []); + const infringementAndTimeOff = scheduledVacation + blueSquares; + const hasRolePermission = user.role === 'Administrator' || user.role === 'Owner'; - - const handleSubmit = async () => { - if (fetchState.isSet && IsReasonUpdated) { //if reason already exists and if it is changed by the user - fetchDispatch({ type: 'FETCHING_STARTED' }); - const response = await patchReason(userProfile._id, { date: date, message: reason }); - if (response.status !== 200) { - fetchDispatch({ - type: 'ERROR', - payload: { message: response.message, errorCode: response.errorCode }, - }); - } else { - fetchDispatch({ type: 'SUCCESS' }); - } - setShow(true); - } else { //add/create reason - fetchDispatch({ type: 'FETCHING_STARTED' }); - const response = await addReason(userProfile._id, { date: date, message: reason }); - if (response.status !== 200) { - fetchDispatch({ - type: 'ERROR', - payload: { message: response.message, errorCode: response.errorCode }, - }); - } else { - fetchDispatch({ type: 'SUCCESS' }); - setAddsReason(true); - } + if (infringementAndTimeOff >= 5 && !hasRolePermission && !canManageTimeOffRequests) { + return false; } - setIsReasonUpdated(false); - setAddsReason(false); + return true; }; -// =============================================================== -// This handler is used for Explanation Modal, that open when Click to learn why is clicked -const openExplanationModal = useCallback(() => { - setShowExplanation(true); -}, []); -// This handler is used to close Info Modal, - -const closeExplanationModal = useCallback(() => { - setShowExplanation(false); -}, []); + const handleOpen = () => { + setShow(true); + }; - // checks for blueSquare scheduled reasons - useEffect(()=>{ - let isMounted = true; - const checkReasons = async ()=>{ - fetchDispatch({type: 'FETCHING_STARTED'}) - const response = await getAllReasons(userProfile._id); - if (response.status !== 200) { - fetchDispatch({ - type: 'ERROR', - payload: { message: response.message, errorCode: response.errorCode }, - }); - } else { - fetchDispatch({ type: 'SUCCESS' }); - if (isMounted){ - setAllReasons(response.data.reasons) - setNumberOfReasons(response.data.reasons.length); - } + const handleClose = () => { + setShow(false); + }; - } - } - checkReasons() - return () => isMounted = false; + // This handler is used for Explanation Modal, that open when Click to learn why is clicked + const openExplanationModal = () => { + setShowExplanation(true); + }; + // This handler is used to close Info Modal, - + const closeExplanationModal = () => { + setShowExplanation(false); + }; -}, [addsReason]); -// checks infringement counts -useEffect(()=>{ - const checkInfringementCount = ()=>{ - setInfringementsNum(userProfile.infringements.length) - if(userProfile.role === "Administrator" || userProfile.role === "Owner"){ - setIsInfringementMoreThanFive(false); - return - }else{ - if(infringementsNum >= 5){ - setIsInfringementMoreThanFive(true) - } - else{ - setIsInfringementMoreThanFive(false) - } - } - } - checkInfringementCount(); -}, [userProfile]) -// =============================================================== if (canEdit) { return (
    @@ -184,81 +69,66 @@ useEffect(()=>{ /> ) : null}
    - - {/* */} - + {/* Replaces Schedule Blue Square button when there are more than 5 blue squares or scheduled reasons - by Sucheta */}
    - { - ((isInfringementMoreThanFive || numberOfReasons >= 5 || (infringementsNum + numberOfReasons >= 5 )) && !(userProfile.role === "Administrator" || userProfile.role === "Owner") )? <> + {!checkIfUserCanScheduleTimeOff() ? ( + <> + {allRequests[userProfile._id]?.length > 0 && - : } + Schedule Blue Square Reason + + )}
    - {(infringementsNum >= 5 || numberOfReasons >= 5 || (infringementsNum + numberOfReasons >= 5 )) && showExplanation && ( - - - - )} + + + + + {show && ( )} diff --git a/src/components/UserProfile/BlueSquares/BlueSquare.jsx b/src/components/UserProfile/BlueSquares/BlueSquare.jsx index d16b27dae5..569490b756 100644 --- a/src/components/UserProfile/BlueSquares/BlueSquare.jsx +++ b/src/components/UserProfile/BlueSquares/BlueSquare.jsx @@ -3,17 +3,13 @@ import './BlueSquare.css'; import hasPermission from 'utils/permissions'; import { connect, useSelector } from 'react-redux'; import { formatCreatedDate, formatDate } from 'utils/formatDate'; -import { - formatDateFromDescriptionString, - formatTimeOffRequests, -} from 'utils/formatDateFromDescriptionString'; const BlueSquare = props => { const authRole = useSelector(state => state.auth.user.role); const isInfringementAuthorizer = props.hasPermission('infringementAuthorizer'); const canPutUserProfileImportantInfo = props.hasPermission('putUserProfileImportantInfo'); - const { blueSquares, handleBlueSquare, numberOfReasons, infringementsNum } = props; + const { blueSquares, handleBlueSquare} = props; return (
    @@ -48,51 +44,17 @@ const BlueSquare = props => { >
    {formatDate(blueSquare.date)}
    - {blueSquare.description !== undefined && ( -
    - {(() => { - const dateFormattedDescription = formatDateFromDescriptionString( - blueSquare.description, - ); - const formattedDescription = formatTimeOffRequests( - dateFormattedDescription, - ); - - if (formattedDescription.length > 0) { - return ( - <> - - {blueSquare.createdDate !== undefined - ? formatCreatedDate(BlueSquare.createdDate) + ':' - : null} - - - {formattedDescription[0]} -
    - Notice : - {`${formattedDescription[1]}`} -
    - - ); - } else { - return blueSquare.createdDate !== undefined - ? formatCreatedDate(BlueSquare.createdDate) + - ': ' + - dateFormattedDescription - : dateFormattedDescription; - } - })()} -
    - )} + {blueSquare.description !== undefined && +
    { + blueSquare.createdDate !== undefined ? formatCreatedDate(blueSquare.createdDate)+": "+ blueSquare.description : blueSquare.description + }
    + }
    )) : null} - {/* Check for userRole, infringements and scheduled reasons to render + button - Sucheta*/} - {authRole === 'Owner' || authRole === 'Administrator' ? ( + {isInfringementAuthorizer && (
    { handleBlueSquare(true, 'addBlueSquare', ''); @@ -103,24 +65,6 @@ const BlueSquare = props => { > +
    - ) : ( - isInfringementAuthorizer && - !( - infringementsNum >= 5 || - numberOfReasons >= 5 || - numberOfReasons + infringementsNum >= 5 - ) && ( -
    { - handleBlueSquare(true, 'addBlueSquare', ''); - }} - className="blueSquareButton" - color="primary" - data-testid="addBlueSquare" - > - + -
    - ) )}
    diff --git a/src/components/UserProfile/ScheduleExplanationModal/ScheduleExplanationModal.jsx b/src/components/UserProfile/ScheduleExplanationModal/ScheduleExplanationModal.jsx index d865381bb7..ce2be6c07e 100644 --- a/src/components/UserProfile/ScheduleExplanationModal/ScheduleExplanationModal.jsx +++ b/src/components/UserProfile/ScheduleExplanationModal/ScheduleExplanationModal.jsx @@ -1,43 +1,117 @@ -import Button from 'react-bootstrap/Button'; +import { Button, Container, Row, Col } from 'react-bootstrap'; import Modal from 'react-bootstrap/Modal'; import { boxStyle } from 'styles'; +import moment from 'moment-timezone'; -function SchedulerExplanationModal({infringementsNum,handleClose, infringements, reasons}) { +function SchedulerExplanationModal({ + infringementsNum, + handleClose, + infringements, + timeOffRequests, +}) { return ( -
    - + <> Please Refer To The Explanation - {/*

    You have {infringementsNum} blue squares already and 5 is the maximum allowed per year. Please contact your Administrator if you need to request time off

    */} -

    Including your time already requested off, you have used the equivalent of {infringementsNum} blue squares and {reasons.length} schedule time offs. 5 is the maximum allowed per year. Please remove a time-off request below or contact your Administrator if you need to request time off in addition to what is listed here: -

    - - {(infringements.length > 0) && <>
    INFRINGEMENTS
      {infringements.map((el,index)=>{ - return
    1. {el.description}
    2. - })}
    } - {reasons.length > 0 && <> -
    SCHEDULED REASONS
    -
      - {reasons.map((el,index)=>{ - return
    • {new Date(el.date).toLocaleDateString()} - {el.reason}
    • })} -
    - } - -

    Note: Blue squares expire after 1 calendar year from their issuance date.

    - + + + + Including your time already requested off, you have used the equivalent of{' '} + {infringementsNum} blue + squares and{' '} + {timeOffRequests.length}{' '} + schedule time offs. 5 is + the maximum allowed per year of employment. Please remove a time-off request below + or contact your Administrator if you need to request time off in addition to what is + listed here: + + + {infringements.length > 0 && ( + + + + +
    INFRINGEMENTS:
    + +
    + + +
      + {infringements.map((el, index) => { + return ( +
    1. +
        +
      • + {`Date: `} + {el.date} +
      • +
      • + {`Reason: `} + {el.description} +
      • +
      +
    2. + ); + })} +
    + +
    + +
    + )} + {timeOffRequests.length > 0 && ( + + + + +
    SCHEDULED TIME OFF:
    + +
    + + +
      + {timeOffRequests.map((el, index) => { + return ( +
    1. +
        +
      • + {`Date: `} + {moment(el.startingDate).format('MM-DD-YYYY')} +
      • +
      • + {`Duration: `} + {` ${el.duration} ${Number(el.duration) > 1 ? 'weeks' : 'week'}`} +
      • +
      • + {`Reason: `} + {el.reason} +
      • +
      +
    2. + ); + })} +
    + +
    + +
    + )} + + + Note: Blue squares expire after 1 calendar year from their issuance date. + + +
    - - + -
    -
    + ); } -export default SchedulerExplanationModal; \ No newline at end of file +export default SchedulerExplanationModal; diff --git a/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModal.css b/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModal.css index 6bf1bf0dd7..5c33a12b3b 100644 --- a/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModal.css +++ b/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModal.css @@ -1,8 +1,28 @@ .centered-container { - text-align: center; - } - - .centered-text { - display: inline-block; - margin-top: 10px; - } + text-align: center; +} + +.centered-text { + display: inline-block; + margin-top: 10px; +} + +#user-time-off-request-list::-webkit-scrollbar, +.user-time-off-scheduler-reason-input::-webkit-scrollbar { + width: 12px; +} + +#user-time-off-request-list::-webkit-scrollbar-track, +.user-time-off-scheduler-reason-input::-webkit-scrollbar-track { + border-radius: 4px; + background-color: #e7e7e7; + border: 1px solid #cacaca; + box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); +} + +#user-time-off-request-list::-webkit-scrollbar-thumb, +.user-time-off-scheduler-reason-input::-webkit-scrollbar-thumb { + border-radius: 8px; + background-color: #007bff; + box-shadow: inset 0 0 3px #e7e7e7; +} diff --git a/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModal.jsx b/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModal.jsx index ce86827276..99ab6c697d 100644 --- a/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModal.jsx +++ b/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModal.jsx @@ -1,12 +1,15 @@ import React from 'react'; import { Modal, Button } from 'react-bootstrap'; import { Container, Row, Col, Modal as NestedModal, ModalBody, ModalFooter } from 'reactstrap'; +import { useDispatch, useSelector } from 'react-redux'; import Form from 'react-bootstrap/Form'; import moment from 'moment-timezone'; -import Spinner from 'react-bootstrap/Spinner'; -import Alert from 'react-bootstrap/Alert'; -import { useEffect, useState } from 'react'; -import { getReasonByDate } from 'actions/reasonsActions'; +import { useState } from 'react'; +import ScheduleReasonModalCard from './ScheduleReasonModalCard'; +import { + addTimeOffRequestThunk, + deleteTimeOffRequestThunk, +} from '../../../actions/timeOffRequestAction'; import DatePicker from 'react-datepicker'; import 'react-datepicker/dist/react-datepicker.css'; import { boxStyle } from 'styles'; @@ -14,68 +17,160 @@ import './ScheduleReasonModal.css'; const ScheduleReasonModal = ({ handleClose, - show, - user, - reason, - setReason, - handleSubmit, - fetchState, - date, - setDate, - fetchMessage, - fetchDispatch, userId, - IsReasonUpdated, - setIsReasonUpdated, - numberOfReasons, - infringementsNum + infringements, + user, + canManageTimeOffRequests, + checkIfUserCanScheduleTimeOff, }) => { - useEffect(() => { - const initialFetching = async () => { - fetchDispatch({ type: 'FETCHING_STARTED' }); - const response = await getReasonByDate(userId, date); - // console.log(response); - if (response.status !== 200) { - fetchDispatch({ - type: 'ERROR', - payload: { message: response.message, errorCode: response.errorCode }, - }); - } else { - // console.log('reason: ', reason); - // console.log('date: ', date); - if (reason !== response.data.reason ) { - setReason(response.data.reason); - } - fetchDispatch({ type: 'FETCHING_FINISHED', payload: { isSet: response.data.isSet } }); - } - }; - initialFetching(); - }, [date]); - -// =============================================================== - // This useEffect will make sure to close the modal that allows for users to schedule reasons - Sucheta - useEffect(()=>{ - if(user.role === "Owner" || user.role === "Administrator"){ - return - }else{ - if (infringementsNum >= 5 || numberOfReasons >= 5 || (infringementsNum + numberOfReasons >= 5)){ - handleClose(); - } - } - },[numberOfReasons,infringementsNum]) -// =============================================================== + const dispatch = useDispatch(); + const allRequests = useSelector(state => state.timeOffRequests.requests); + + const nextSundayStr = moment() + .isoWeekday(7) + .startOf('day'); + const nextSunday = new Date(nextSundayStr.year(), nextSundayStr.month(), nextSundayStr.date()); + const initialRequestData = { + dateOfLeave: nextSunday, + numberOfWeeks: 1, + reasonForLeave: '', + }; + + const initialRequestDataErrors = { + dateOfLeaveError: '', + numberOfWeeksError: '', + reasonForLeaveError: '', + }; + + const initialConfirmationData = { + offTimeWeeks: [], + returnDate: '', + reasonForLeave: '', + }; + + const [requestData, setRequestData] = useState(initialRequestData); + const [requestDataErrors, setRequestDataErrors] = useState(initialRequestDataErrors); const [confirmationModal, setConfirmationModal] = useState(false); - const [offTimeWeeks, setOffTimeWeeks] = useState([]); - const [dateInputError, setDateInputError] = useState(''); + const [confirmationModalData, setConfirmationModalData] = useState(initialConfirmationData); + const [deleteConfirmationModal, setDeleteConfirmationModal] = useState(false); + const [allowedDurationModal, setAllowedDurationModal] = useState(false); + const [allowedDurationData, setAllowedDurationData] = useState({}); + const [requestTodelete, setRequestTodelete] = useState(''); + + const ContainerMaxHeight = checkIfUserCanScheduleTimeOff() ? '160px' : '600px'; + + const handleAddRequestDataChange = e => { + e.preventDefault(); + const key = e.target.name; + const value = e.target.value; + if (key === 'numberOfWeeks') { + if (checkIfUserIsAllowedToscheduleForTheDuration(value)) return; + } + setRequestData(prev => ({ + ...prev, + [key]: value, + })); + }; - const validateDateIsNotBeforeToday = returnDate => { - const isBeforeToday = moment(returnDate).isBefore(moment(), 'day'); + // checks if date value is not empty + const validateDateOfLeave = data => { + if (!data.dateOfLeave) { + setRequestDataErrors(prev => ({ + ...prev, + dateOfLeaveError: 'Date of leave can not be empty', + })); + return false; + } + return true; + }; + + // checks if date of leave is not before the start of current week + const validateDateIsNotBeforeStartOfCurrentWeek = data => { + const isBeforeToday = moment(getDateWithoutTimeZone(data.dateOfLeave)).isBefore( + moment().startOf('week'), + 'day', + ); if (isBeforeToday) { - setDateInputError('The selected return date must be after today'); + setRequestDataErrors(prev => ({ + ...prev, + dateOfLeaveError: 'Date of leave can not be before the start of current week', + })); + return false; + } + return true; + }; + + // checks if the newly added request doesn't overlap with existing ones + const checkIfRequestOverlapsWithOtherRequests = data => { + const dataStartingDate = moment(getDateWithoutTimeZone(data.dateOfLeave)).startOf('day'); + const dataEndingDate = moment(getDateWithoutTimeZone(data.dateOfLeave)) + .add(Number(data.numberOfWeeks), 'week') + .subtract(1, 'day') + .startOf('day'); + + if (allRequests[userId]?.length > 0) { + const isAnyOverlapingRequests = allRequests[userId].some(request => { + const requestStartingDate = moment(request.startingDate.split('T')[0]).startOf('day'); + const requestEndingDate = moment(request.endingDate.split('T')[0]).startOf('day'); + + if ( + (dataStartingDate.isSameOrAfter(requestStartingDate) && + dataStartingDate.isSameOrBefore(requestEndingDate)) || + (dataEndingDate.isSameOrAfter(requestStartingDate) && + dataEndingDate.isSameOrBefore(requestEndingDate)) + ) { + return true; + } + return false; + }); + + if (isAnyOverlapingRequests) { + setRequestDataErrors(prev => ({ + ...prev, + dateOfLeaveError: 'this request overlaps with other existing requests', + })); + return false; + } + } + return true; + }; + // checks if reason for leave is not empty and if it has over 10 words + const validateReasonForLeave = data => { + if (!data.reasonForLeave) { + setRequestDataErrors(prev => ({ + ...prev, + reasonForLeaveError: 'Reason for leave can not be empty', + })); + return false; + } + const words = data.reasonForLeave?.split(' '); + if (words.length < 10) { + setRequestDataErrors(prev => ({ + ...prev, + reasonForLeaveError: 'Reason for leave can not be less than 10 words', + })); + return false; + } + return true; + }; + // checks if duration is not empty,negative or 0 + const validateNumberOfWeeks = data => { + if (!data.numberOfWeeks) { + setRequestDataErrors(prev => ({ + ...prev, + numberOfWeeksError: 'Duration can not be empty', + })); + return false; + } + if (data.numberOfWeeks < 1) { + setRequestDataErrors(prev => ({ + ...prev, + numberOfWeeksError: 'Duration can not be less than 1 week', + })); return false; } - setDateInputError(''); + return true; }; @@ -83,156 +178,362 @@ const ScheduleReasonModal = ({ setConfirmationModal(prev => !prev); }; - const getWeekIntervals = endDate => { - const weekEnd = moment(endDate).subtract(1, 'day'); - const weekStart = moment(weekEnd).startOf('week'); - - const formattedInterval = [formatDate(weekStart), formatDate(weekEnd)]; + const toggleDeleteConfirmationModal = () => { + setDeleteConfirmationModal(prev => !prev); + }; - return formattedInterval; + const toggleDurationInfoModal = () => { + setAllowedDurationModal(prev => !prev); }; - const formatDate = date => { - return date.format('MM/DD/YYYY'); + const getWeekIntervals = data => { + const dateOfLeaveStr = getDateWithoutTimeZone(data.dateOfLeave); + const intervals = []; + let startDate = moment(dateOfLeaveStr); + for (let i = 0; i < data.numberOfWeeks; i++) { + const endDate = startDate.clone().endOf('week'); + intervals.push([startDate.format('MM-DD-YYYY'), endDate.format('MM-DD-YYYY')]); + startDate = startDate.add(1, 'week').startOf('week'); + } + + return { intervals, startDate }; }; - const filterSunday = (date) => { + const filterSunday = date => { const day = date.getDay(); return day === 0; }; const handleSaveReason = e => { e.preventDefault(); - if (!validateDateIsNotBeforeToday(date)) return; - const weeks = getWeekIntervals(date); - setOffTimeWeeks(weeks); + setRequestDataErrors(initialRequestDataErrors); + + if (!validateDateOfLeave(requestData)) return; + if (!validateDateIsNotBeforeStartOfCurrentWeek(requestData)) return; + if (!checkIfRequestOverlapsWithOtherRequests(requestData)) return; + if (!validateNumberOfWeeks(requestData)) return; + if (!validateReasonForLeave(requestData)) return; + + const { intervals, startDate } = getWeekIntervals(requestData); + setConfirmationModalData({ + offTimeWeeks: intervals, + returnDate: startDate.format('MM-DD-YYYY'), + reasonForLeave: requestData.reasonForLeave, + }); toggleConfirmationModal(); }; const handelConfirmReason = () => { - handleSubmit(); + const data = { + requestFor: userId, + reason: requestData.reasonForLeave, + startingDate: getDateWithoutTimeZone(requestData.dateOfLeave), + duration: requestData.numberOfWeeks, + }; + dispatch(addTimeOffRequestThunk(data)); + setRequestData(initialRequestData); toggleConfirmationModal(); }; + const getDateWithoutTimeZone = date => { + const newDateObject = new Date(date); + const day = newDateObject.getDate(); + const month = newDateObject.getMonth() + 1; + const year = newDateObject.getFullYear(); + return moment(`${month}-${day}-${year}`, 'MM-DD-YYYY').format('YYYY-MM-DD'); + }; + + const handleDeleteRequest = id => { + toggleDeleteConfirmationModal(); + setRequestTodelete(id); + }; + + const handelDeleteConfirmReason = () => { + dispatch(deleteTimeOffRequestThunk(requestTodelete)); + setRequestTodelete(''); + toggleDeleteConfirmationModal(); + }; + + const sortRequests = (a, b) => { + const momentA = moment(a.startingDate, 'YYYY-MM-DD'); + const momentB = moment(b.startingDate, 'YYYY-MM-DD'); + return momentA - momentB; + }; + + const checkIfUserIsAllowedToscheduleForTheDuration = data => { + if (!data) return false; + const blueSquares = Number(infringements?.length) || 0; + const numberOfWeeks = Number(data); + let scheduledVacation = 0; + + allRequests[userId]?.forEach(element => { + scheduledVacation = scheduledVacation + Number(element.duration); + }); + + const infringementsAndScheduledTimeOff = scheduledVacation + blueSquares; + const hasRolePermission = user.role === 'Administrator' || user.role === 'Owner'; + + if ( + infringementsAndScheduledTimeOff + numberOfWeeks > 5 && + !hasRolePermission && + !canManageTimeOffRequests + ) { + setAllowedDurationData({ + numberOfScheduledReasons: allRequests[userId]?.length || 0, + durationOfScheduledReasons: scheduledVacation, + blueSquares: blueSquares || 0, + }); + setAllowedDurationModal(true); + return true; + } + return false; + }; + + const durationExplanationText = data => { + + const { numberOfScheduledReasons, blueSquares, durationOfScheduledReasons } = data; + + const transitionWord = numberOfScheduledReasons > 0 && blueSquares > 0 ? ` and ` : ''; + const scheduledReasonsText = + numberOfScheduledReasons > 0 + ? `${numberOfScheduledReasons} scheduled ${ + numberOfScheduledReasons > 1 ? 'reasons' : 'reason' + } for a duration of ${durationOfScheduledReasons} ${ + durationOfScheduledReasons > 1 ? 'weeks' : 'week' + }` + : ''; + + const blueSquaresText = blueSquares > 0 ? ` ${blueSquares} blue ${ + blueSquares > 1 ? 'squares' : 'square' + }` : '' + + const allowedPeriodText = `. Therefore, you are only allowed to schedule a reason for no more than ${5 - + blueSquares - durationOfScheduledReasons} ${ + 5 - blueSquares - durationOfScheduledReasons > 1 + ? 'weeks' + : 'week' + }` + + const finalText = (scheduledReasonsText === '' && blueSquaresText === '') ? `You are only allowed to schedule reason for no more than 5 Weeks.` : `You have ${scheduledReasonsText} ${transitionWord} ${blueSquaresText}${allowedPeriodText}.`; + + return finalText + }; + return ( <> - - -
    Choose to Use a Blue Square
    -
    (function under development)
    -
    -
    -
    - - - - {/* Schedule a reason to be used on this weekend's blue square for {user.firstName} */} - Need to take a week off for an emergency or vacation? That's no problem. The system - will still issue you a blue square but scheduling here will note this reason on it so - it's clear you chose to use one (vs receiving one for missing something) and let us - know in advance. Blue squares are meant for situations like this and we allow 5 a - year. - - -

    - To schedule your time off, you need to CHOOSE THE SUNDAY OF THE WEEK YOU’LL RETURN. - This is the date needed so your reason ends up on the blue square that will be - auto-issued AT THE END OF THE WEEK YOU'LL BE GONE. -

    -
    - Choose the Sunday of the week you'll return: - { - const dateSelectedTz = moment(dateSelected) - .tz('America/Los_Angeles') - .endOf('week') - .toISOString() - .split('T')[0]; - setDate(dateSelectedTz); - }} - filterDate={filterSunday} - dateFormat="yyyy-MM-dd" - placeholderText="Select a Sunday" - id="dateOfLeave" - className="form-control" - wrapperClassName="w-100" - /> - {dateInputError} - - What is your reason for requesting this time off? - - { - setReason(e.target.value); - }} - disabled={fetchState.isFetching} - /> -
    - {!fetchState.isFetching && fetchState.error ? ( - {fetchMessage} - ) : !fetchState.isFetching && fetchState.success ? ( - Reason Scheduling Saved! - ) : null} -
    - - - - {/* Save button */} - + + + + + + + The time off will be scheduled for the following + {confirmationModalData.offTimeWeeks?.length > 1 ? ` weeks:` : ` week:`} + + + {confirmationModalData.offTimeWeeks?.length > 0 && ( + + + {confirmationModalData.offTimeWeeks.map((week, index) => ( +
  • + {`From `} + {week[0]} + {` To `} + {week[1]} +
  • + ))} + +
    + )} + + + Due to the reason of: + + + +
  • {confirmationModalData.reasonForLeave}
  • + +
    + + The return day is: + + + +
  • + {`On `} + {confirmationModalData.returnDate} +
  • + +
    + + + Please review the details of the request before confirming your selection. + Once confirmed, an email will be sent to you and your manager. + + +
    +
    + + + + +
    + + + + + {durationExplanationText(allowedDurationData)} + + + + + + + +
    +
    + + )} + {allRequests[userId]?.length > 0 && ( + <> + - {fetchState.isFetching ? : 'Save'} - - - - - - The blue square reason will be scheduled for the following week: - - {offTimeWeeks.length > 0 && ( + +
    Scheduled Time Off
    +
    +
    + + + {allRequests[userId] + .slice() + .sort(sortRequests) + .map(request => ( + + ))} + + + + + + + Are you sure you want to delete the scheduled time off? + + - -
  • - {`From `} - {offTimeWeeks[0]} - {` To `} - {offTimeWeeks[1]} -
  • + + Once you confirm, an email will be sent to you and your manager to notify them + of the update.
    - )} - - Please confirm your selection - -
    -
    - - - - -
    -
    - + +
    + + + + + + + + )} ); }; diff --git a/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModalCard.jsx b/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModalCard.jsx new file mode 100644 index 0000000000..c368b029a2 --- /dev/null +++ b/src/components/UserProfile/ScheduleReasonModal/ScheduleReasonModalCard.jsx @@ -0,0 +1,48 @@ +import React, { useState } from 'react'; +import { Card, CardBody, Row, Col, Button } from 'reactstrap'; +import moment from 'moment-timezone'; + +function ScheduleReasonModalCard({ request , handleDeleteRequest }) { + const [showFullText, setShowFullText] = useState(false); + const toggleShowText = () => { + setShowFullText(!showFullText); + }; + return ( + + + + +

    + Date Of Leave: + {moment(request.startingDate).format('MMM Do YYYY')} +

    + + +

    + Duration: + {request.duration}{Number(request.duration) > 1 ? ` weeks`: ` week`} +

    + + +
    + + +

    + Reason: + {request.reason} +

    + +
    + + + + + +
    +
    + ); +} + +export default ScheduleReasonModalCard; diff --git a/src/components/UserProfile/UserProfile.jsx b/src/components/UserProfile/UserProfile.jsx index 2227799cbf..341035ebbd 100644 --- a/src/components/UserProfile/UserProfile.jsx +++ b/src/components/UserProfile/UserProfile.jsx @@ -884,6 +884,7 @@ function UserProfile(props) { handleUserProfile={handleUserProfile} handleSaveError={props.handleSaveError} handleBlueSquare={handleBlueSquare} + user={props.auth.user} isUserSelf={isUserSelf} canEdit={canEdit} /> diff --git a/src/components/UserProfile/UserProfileEdit/UserProfileEdit.scss b/src/components/UserProfile/UserProfileEdit/UserProfileEdit.scss index 60d97c593b..a8b1572909 100644 --- a/src/components/UserProfile/UserProfileEdit/UserProfileEdit.scss +++ b/src/components/UserProfile/UserProfileEdit/UserProfileEdit.scss @@ -1,107 +1,139 @@ -form-control { - width: 0; -} - -.saveChangesWarning { - display: block; - position: fixed; - top: 7vh; - left: 0; - width: 100%; - color: white; - background-color: red; - border: 1px solid #a8a8a8; - text-align: center; - z-index: 9; - opacity: 70%; -} - -.profileEditTitleCenter { - display: flex; - justify-content: center; - width: 100%; - margin: 5px; - font-weight: bold; - text-align: center; - color: lightblue; -} - -.profileEditTitle { - color: lightblue; - display: flex; - margin: 5px; - font-weight: bold; -} - -.detailEditSection { - display: flex; - flex-direction: column; - justify-content: center; - text-align: center; - width: 300px; - margin-left: 5%; - margin-right: 5%; -} - -.inputSections { - display: flex; - flex-direction: column; - justify-content: center; - align-content: center; - text-align: center; - width: 300px; -} - -.profileViewButtonContainer { - position: absolute; - display: flex; - right: 1%; - justify-content: flex-end; -} - -.profileViewButton { - display: flex; - padding: 0.25em 0.4em; - font-size: 16px; - font-weight: 700; - line-height: 1; - border-radius: 20px; - background-color: dodgerblue; - color: white; - - &:hover { - text-decoration: none; - } -} - -.modLinkButton { - display: flex; - width: 25px; - height: 25px; - margin: 5px; - color: white; - border-radius: 25%; - align-items: center; - justify-content: center; - background-color: #6d757d; - border: none; -} -.teamsView { - display: flex; -} - -.blueSquareSection { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - color: orange; - font-weight: bold; - margin: 5px; -} - -.tab-pane { - .row { - padding: 15 px; - } -} +form-control { + width: 0; +} + +.saveChangesWarning { + display: block; + position: fixed; + top: 7vh; + left: 0; + width: 100%; + color: white; + background-color: red; + border: 1px solid #a8a8a8; + text-align: center; + z-index: 9; + opacity: 70%; +} + +.profileEditTitleCenter { + display: flex; + justify-content: center; + width: 100%; + margin: 5px; + font-weight: bold; + text-align: center; + color: lightblue; +} + +.profileEditTitle { + color: lightblue; + display: flex; + margin: 5px; + font-weight: bold; +} + +.detailEditSection { + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + width: 300px; + margin-left: 5%; + margin-right: 5%; +} + +.inputSections { + display: flex; + flex-direction: column; + justify-content: center; + align-content: center; + text-align: center; + width: 300px; +} + +.profileViewButtonContainer { + position: absolute; + display: flex; + right: 1%; + justify-content: flex-end; +} + +.profileViewButton { + display: flex; + padding: 0.25em 0.4em; + font-size: 16px; + font-weight: 700; + line-height: 1; + border-radius: 20px; + background-color: dodgerblue; + color: white; + + &:hover { + text-decoration: none; + } +} + +.modLinkButton { + display: flex; + width: 25px; + height: 25px; + margin: 5px; + color: white; + border-radius: 25%; + align-items: center; + justify-content: center; + background-color: #6d757d; + border: none; +} +.teamsView { + display: flex; +} + +.blueSquareSection { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + color: orange; + font-weight: bold; + margin: 5px; +} + +.Schedule-explanation-modal-list-marker::marker { + font-weight: bold; +} +.infringements-explanation-modal-row, +.scheduled-time-off-explanation-modal-row { + max-height: 200px; + overflow-y: auto; + box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3); + padding-top: 10px; + border-radius: 5px; +} + +.infringements-explanation-modal-row::-webkit-scrollbar, +.scheduled-time-off-explanation-modal-row::-webkit-scrollbar { + width: 12px; +} + +.infringements-explanation-modal-row::-webkit-scrollbar-track, +.scheduled-time-off-explanation-modal-row::-webkit-scrollbar-track { + border-radius: 4px; + background-color: #e7e7e7; + border: 1px solid #cacaca; + box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); +} + +.infringements-explanation-modal-row::-webkit-scrollbar-thumb, +.scheduled-time-off-explanation-modal-row::-webkit-scrollbar-thumb { + border-radius: 8px; + background-color: #007bff; + box-shadow: inset 0 0 3px #e7e7e7; +} + +.tab-pane { + .row { + padding: 15 px; + } +} diff --git a/src/utils/URL.js b/src/utils/URL.js index ce0197e408..2aba397da5 100644 --- a/src/utils/URL.js +++ b/src/utils/URL.js @@ -104,23 +104,6 @@ export const ENDPOINTS = { SETUP_NEW_USER_PROFILE: () => `${APIEndpoint}/ProfileInitialSetup`, ALL_MAP_LOCATIONS: () => `${APIEndpoint}/mapLocations`, - //reasons endpoints - CREATEREASON: () => { - return `${APIEndpoint}/reason/`; - }, - GETALLUSERREASONS: userId => { - return `${APIEndpoint}/reason/${userId}`; - }, - GETSINGLEREASONBYID: userId => { - return `${APIEndpoint}/reason/single/${userId}`; - }, - PATCHUSERREASONBYID: userId => { - return `${APIEndpoint}/reason/${userId}`; - }, - DELETEUSERREASONBYID: userId => { - return `${APIEndpoint}/reason/${userId}`; - }, - MOUSEOVERTEXT: () => `${APIEndpoint}/mouseoverText`, MOUSEOVERTEXT_BY_ID: mouseoverTextId => `${APIEndpoint}/mouseoverText/${mouseoverTextId}`, PERMISSION_CHANGE_LOGS: userId => `${APIEndpoint}/permissionChangeLogs/${userId}`,