Skip to content
43 changes: 33 additions & 10 deletions src/components/Header/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ import BellNotification from './BellNotification';
import { getUserProfile } from '../../actions/userProfile';
import PermissionWatcher from '../Auth/PermissionWatcher';
import DisplayBox from '../PRPromotions/DisplayBox';
import PropTypes from 'prop-types';

export function Header(props) {
const location = useLocation();
Expand Down Expand Up @@ -104,7 +105,8 @@ export function Header(props) {
props.hasPermission('postUserProfile', !isAuthUser && canInteractWithViewingUser) ||
props.hasPermission('deleteUserProfile', !isAuthUser && canInteractWithViewingUser) ||
props.hasPermission('changeUserStatus', !isAuthUser && canInteractWithViewingUser) ||
props.hasPermission('getUserProfiles', !isAuthUser && canInteractWithViewingUser);
props.hasPermission('getUserProfiles', !isAuthUser && canInteractWithViewingUser) ||
props.hasPermission('setFinalDay', !isAuthUser && canInteractWithViewingUser);

// Badges
const canAccessBadgeManagement =
Expand Down Expand Up @@ -594,14 +596,14 @@ export function Header(props) {
props.userProfile.email,
props.userProfile.email,
) && (
<DropdownItem
tag={Link}
to={`/updatepassword/${displayUserId}`}
className={fontColor}
>
{UPDATE_PASSWORD}
</DropdownItem>
)}
<DropdownItem
tag={Link}
to={`/updatepassword/${displayUserId}`}
className={fontColor}
>
{UPDATE_PASSWORD}
</DropdownItem>
)}
<DropdownItem className={fontColor}>
<DarkModeButton />
</DropdownItem>
Expand Down Expand Up @@ -680,7 +682,28 @@ const mapStateToProps = state => ({
notification: state.notification,
darkMode: state.theme.darkMode,
});

Header.propTypes = {
hasPermission: PropTypes.func.isRequired,
auth: PropTypes.shape({
isAuthenticated: PropTypes.bool,
user: PropTypes.shape({
userid: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
role: PropTypes.string
}),
firstName: PropTypes.string,
profilePic: PropTypes.string
}),
getHeaderData: PropTypes.func,
getAllRoles: PropTypes.func,
getWeeklySummaries: PropTypes.func,
role: PropTypes.shape({
roles: PropTypes.array
}),
notification: PropTypes.object,
userProfile: PropTypes.object,
darkMode: PropTypes.bool,
taskEditSuggestionCount: PropTypes.number,
};
export default connect(mapStateToProps, {
getHeaderData,
getAllRoles,
Expand Down
9 changes: 4 additions & 5 deletions src/components/PermissionsManagement/PermissionsConst.jsx

This comment was marked as resolved.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From PR#3245. The route permissions also need to be updated as in that PR.

      {
        label: 'Set Final Day',
        key: 'setFinalDay',
        description: 'Gives the user permission to set the final working day.',
      },

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the permission Set Final Day for User is already defined in PermissionsConst.jsx

Copy link
Contributor

@nathanah nathanah Nov 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The key for that is 'setUserFinalDay' instead of 'setFinalDay' as you're checking. So the key needs to be updated either in your hasPermission() calls or in PermissionsConst and anywhere that actually uses 'setUserFinalDay'.

This comment was marked as resolved.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// recursive function that returns the permission keys given an array of permission objects (from permissionLabels below)
//// recursive function that returns the permission keys given an array of permission objects (from permissionLabels below)
const getAllSubpermissionKeys = permissions => {
const keys = [];
permissions.forEach(permission => {
Expand Down Expand Up @@ -192,10 +192,9 @@ export const permissionLabels = [
'Gives the user permission to use the "Pause" button to pause user activity on their profile page.',
},
{
label: 'Set Final Day for User',
key: 'setUserFinalDay',
description:
'Gives the user permission to use the "Set Final Day" button to set a final working day for a user on their profile page.',
label: 'Set Final Day',
key: 'setFinalDay',
description: 'Gives the user permission to use the set the final working day.',
},
{
label: 'Tracking Management',
Expand Down
43 changes: 38 additions & 5 deletions src/components/UserManagement/UserTableData.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,21 @@ import { Tooltip, UncontrolledTooltip } from 'reactstrap';
import { connect, useSelector, useDispatch} from 'react-redux';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { toast } from 'react-toastify';
// import { Link, useHistory } from 'react-router-dom';
import PropTypes from 'prop-types';
import { useHistory, Link } from 'react-router-dom';
import { faUser, faUsers, faShieldAlt, faBriefcase, faUserTie, faCrown, faChalkboardTeacher, faBug, faGlobe, faStar, faCopy } from '@fortawesome/free-solid-svg-icons';
import { updateUserInfomation } from '../../actions/userManagement';
import { getAllRoles } from '../../actions/role';
import ResetPasswordButton from './ResetPasswordButton';
// import { DELETE, PAUSE, RESUME, SET_FINAL_DAY, CANCEL } from '../../languages/en/ui';
import { DELETE, PAUSE, RESUME } from '../../languages/en/ui';
// import { UserStatus, FinalDay } from '../../utils/enums';
import { UserStatus } from '../../utils/enums';
import ActiveCell from './ActiveCell';
import TimeDifference from './TimeDifference';
import { boxStyle } from '../../styles';
import { formatDateLocal, formatDateUtcYYYYMMDD } from '../../utils/formatDate';
import hasPermission, {cantUpdateDevAdminDetails } from '../../utils/permissions';
import SetUpFinalDayButton from './SetUpFinalDayButton';

/**
* The body row of the user table
*/
Expand Down Expand Up @@ -62,6 +61,7 @@ const UserTableDataComponent = (props) => {
const resetPasswordStatus = props.hasPermission('updatePassword');
//const updatePasswordStatus = props.hasPermission('updatePassword');
const canChangeUserStatus = props.hasPermission('changeUserStatus');
const canSetFinalDay = props.hasPermission('setFinalDay');
const canSeeReports = props.hasPermission('getReports');
const toggleDeleteTooltip = () => setTooltipDelete(!tooltipDeleteOpen);
const togglePauseTooltip = () => setTooltipPause(!tooltipPauseOpen);
Expand Down Expand Up @@ -473,7 +473,7 @@ const UserTableDataComponent = (props) => {
<td>
{!isCurrentUser && (
<>
{!canChangeUserStatus ? (
{canSetFinalDay ? (
<Tooltip
placement="bottom"
isOpen={tooltipFinalDayOpen}
Expand All @@ -485,13 +485,15 @@ const UserTableDataComponent = (props) => {
) : (
''
)}
<SetUpFinalDayButton
<SetUpFinalDayButton
userProfile={props.user}
darkMode={darkMode}
onFinalDaySave={updatedUser => {
// Update the user object in the parent state
props.onUserUpdate(updatedUser);
}}
id={`btn-final-day-${props.user._id}`}
disabled={!canSetFinalDay}
/>
</>
)}
Expand Down Expand Up @@ -591,6 +593,10 @@ const UserTableDataComponent = (props) => {
</tr>
);
};
// UserTableData.propTypes = {
// hasPermission: PropTypes.func, // or PropTypes.bool depending on what it is
// user: PropTypes.object, // if you access user or user._id
// };

const UserTableData = React.memo(UserTableDataComponent);
UserTableData.displayName = 'UserTableData';
Expand All @@ -599,5 +605,32 @@ const mapStateToProps = state => ({
auth: state.auth,
authEmail: state.auth.user.email,
});
UserTableDataComponent.propTypes = {
hasPermission: PropTypes.func.isRequired, // must be a function
user: PropTypes.shape({
_id: PropTypes.string.isRequired,
firstName: PropTypes.string,
lastName: PropTypes.string,
role: PropTypes.string,
jobTitle: PropTypes.string,
email: PropTypes.string,
weeklycommittedHours: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
startDate: PropTypes.string,
endDate: PropTypes.string,
infringementCount: PropTypes.number,
isActive: PropTypes.bool,
reactivationDate: PropTypes.string,
}).isRequired,
index: PropTypes.number,
isActive: PropTypes.bool,
resetLoading: PropTypes.bool,
authEmail: PropTypes.string,
auth: PropTypes.object,
onPauseResumeClick: PropTypes.func,
onDeleteClick: PropTypes.func,
onLogTimeOffClick: PropTypes.func,
onUserUpdate: PropTypes.func,
timeOffRequests: PropTypes.array,
};

export default connect(mapStateToProps, { hasPermission })(UserTableData);
13 changes: 13 additions & 0 deletions src/components/UserManagement/UserTableHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { toast } from 'react-toastify';
import { getAllUserProfile } from '../../actions/userManagement';
import { ENDPOINTS } from '~/utils/URL';
import userTableDataPermissions from '../../utils/userTableDataPermissions';
import PropTypes from 'prop-types';
import {
ACTIVE,
FIRST_NAME,
Expand Down Expand Up @@ -317,6 +318,18 @@ const UserTableHeaderComponent = ({ authRole, roleSearchText, darkMode, editUser
);
};

UserTableHeaderComponent.propTypes = {
authRole: PropTypes.string.isRequired,
roleSearchText: PropTypes.string,
darkMode: PropTypes.bool,
editUser: PropTypes.object,
enableEditUserInfo: PropTypes.func.isRequired,
disableEditUserInfo: PropTypes.func.isRequired,
isMobile: PropTypes.bool,
mobileFontSize: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),

};

const UserTableHeader = React.memo(UserTableHeaderComponent);
UserTableHeader.displayName = 'UserTableHeader';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import moment from 'moment';
import PhoneInput from 'react-phone-input-2';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCopy } from '@fortawesome/free-solid-svg-icons';
// import 'react-phone-input-2/lib/style.css';

//// import 'react-phone-input-2/lib/style.css';
import PauseAndResumeButton from '~/components/UserManagement/PauseAndResumeButton';
import TimeZoneDropDown from '../TimeZoneDropDown';
import { connect , useDispatch } from 'react-redux';
Expand Down
Loading