Skip to content

Commit

Permalink
Merge pull request #1030 from OneCommunityGlobal/yufei_make_intangibl…
Browse files Browse the repository at this point in the history
…e_time_number_having_mouseover_explanation

Yufei - make intangible time numbers having a mouseover explanation editable by the owner
  • Loading branch information
one-community authored Aug 4, 2023
2 parents 40099cd + 3d41c6b commit 56cc802
Show file tree
Hide file tree
Showing 12 changed files with 239 additions and 15 deletions.
19 changes: 16 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"redux-persist": "^5.10.0",
"redux-thunk": "^2.3.0",
"tinymce": "^5.10.7",
"uuid": "^9.0.0",
"websocket-extensions": ">=0.1.4"
},
"scripts": {
Expand Down
48 changes: 48 additions & 0 deletions src/actions/mouseoverTextAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import axios from 'axios';
import { ENDPOINTS } from '../utils/URL';

import * as types from './../constants/mouseoverTextConstants';

export const getMouseoverTextAction = payload => {
return {
type: types.GET_MOUSEOVER_TEXT,
payload,
};
}

export const getMouseoverText = () => async dispatch => {
try {
const { data } = await axios.get(ENDPOINTS.MOUSEOVERTEXT());
// console.log('Fetched mouseoverText data:', data); // Verify the fetched data
return dispatch(getMouseoverTextAction(data));
} catch (error) {
console.log('Error fetching mouseoverText:', error);
}
};

export const createMouseoverTextAction = payload => {
return {
type: types.CREATE_MOUSEOVER_TEXT,
payload,
};
}

export const createMouseoverText = mouseoverText => async dispatch => {
await axios.post(ENDPOINTS.MOUSEOVERTEXT(), mouseoverText)
return dispatch(createMouseoverTextAction(mouseoverText))
}

export const updateMouseoverTextAction = payload => {
return {
type: types.UPDATE_MOUSEOVER_TEXT,
payload,
};
}

export const updateMouseoverText = (mouseoverTextId, mouseoverText) => {
return async dispatch => {
await axios.put(ENDPOINTS.MOUSEOVERTEXT_BY_ID(mouseoverTextId), mouseoverText)
.then(dispatch(updateMouseoverTextAction(mouseoverText)));
}
}

11 changes: 10 additions & 1 deletion src/components/LeaderBoard/LeaderBoard.container.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { connect } from 'react-redux';
import Leaderboard from './Leaderboard';
import { getcolor, getprogress, getProgressValue } from '../../utils/effortColors';
import { get, round, maxBy } from 'lodash';
import { getMouseoverText } from '../../actions/mouseoverTextAction';


const mapStateToProps = state => {
let leaderBoardData = get(state, 'leaderBoardData', []);
Expand Down Expand Up @@ -52,13 +54,20 @@ const mapStateToProps = state => {
orgData.barcolor = getcolor(orgTangibleColorTime);
orgData.barprogress = getprogress(orgTangibleColorTime);




return {
isAuthenticated: get(state, 'auth.isAuthenticated', false),
leaderBoardData: leaderBoardData,
loggedInUser: get(state, 'auth.user', {}),
organizationData: orgData,
timeEntries: get(state, 'timeEntries', {}),
isVisible: user.role === 'Volunteer' || user.isVisible,
totalTimeMouseoverText: state?.mouseoverText?.[0]?.mouseoverText,
totalTimeMouseoverTextId: state?.mouseoverText?.[0]?._id,
};
};
export default connect(mapStateToProps, { getLeaderboardData, getOrgData })(Leaderboard);


export default connect(mapStateToProps, { getLeaderboardData, getOrgData, getMouseoverText })(Leaderboard);
6 changes: 4 additions & 2 deletions src/components/LeaderBoard/Leaderboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
@media (max-width: 544px) {
.leaderboard {
font-size: 0.7rem;
} /*1rem = 16px*/
}

/*1rem = 16px*/

.my-custom-scrollbar {
height: 300px;
Expand All @@ -28,4 +30,4 @@
.boldClass {
color: #007bff;
font-weight: bold;
}
}
39 changes: 32 additions & 7 deletions src/components/LeaderBoard/Leaderboard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
assignStarDotColors,
showStar,
} from 'utils/leaderboardPermissions';
import MouseoverTextTotalTimeEditButton from 'components/mouseoverText/MouseoverTextTotalTimeEditButton';

function useDeepEffect(effectFunc, deps) {
const isFirst = useRef(true);
Expand All @@ -30,15 +31,29 @@ function useDeepEffect(effectFunc, deps) {
const LeaderBoard = ({
getLeaderboardData,
getOrgData,
getMouseoverText,
leaderBoardData,
loggedInUser,
organizationData,
timeEntries,
isVisible,
asUser,
totalTimeMouseoverText,
}) => {
const userId = asUser ? asUser : loggedInUser.userId;
const isAdmin = ['Owner', 'Administrator', 'Core Team'].includes(loggedInUser.role);
const isOwner = ['Owner'].includes(loggedInUser.role);

const [mouseoverTextValue, setMouseoverTextValue] = useState(totalTimeMouseoverText);

useEffect(() => {
getMouseoverText();
setMouseoverTextValue(totalTimeMouseoverText);
}, [totalTimeMouseoverText]);

const handleMouseoverTextUpdate = (text) => {
setMouseoverTextValue(text);
};

useDeepEffect(() => {
getLeaderboardData(userId);
Expand All @@ -58,7 +73,7 @@ const LeaderBoard = ({
}
}
}
} catch {}
} catch { }
}, [leaderBoardData]);

const [isOpen, setOpen] = useState(false);
Expand Down Expand Up @@ -204,9 +219,18 @@ const LeaderBoard = ({
<span className="d-none d-sm-block">Tangible Time</span>
</th>
<th>Progress</th>
<th>
<span className="d-sm-none">Tot. Time</span>
<span className="d-none d-sm-block">Total Time</span>
<th style={{ textAlign: 'right' }}>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ textAlign: 'left' }}>
<span className="d-sm-none">Tot. Time</span>
<span className="d-none d-sm-inline-block" title={mouseoverTextValue}>Total Time </span>
</div>
{isOwner && (
<MouseoverTextTotalTimeEditButton
onUpdate={handleMouseoverTextUpdate}
/>
)}
</div>
</th>
</tr>
</thead>
Expand Down Expand Up @@ -259,7 +283,7 @@ const LeaderBoard = ({
{/* <Link to={`/dashboard/${item.personId}`}> */}
<div onClick={() => dashboardToggle(item)}>
{hasLeaderboardPermissions(loggedInUser.role) &&
showStar(item.tangibletime, item.weeklycommittedHours) ? (
showStar(item.tangibletime, item.weeklycommittedHours) ? (
<i
className="fa fa-star"
title={`Weekly Committed: ${item.weeklycommittedHours} hours`}
Expand Down Expand Up @@ -325,7 +349,8 @@ const LeaderBoard = ({
</td>
<td className="align-middle">
<span
title="Total time"
title={mouseoverTextValue}
id="Total time"
className={item.totalintangibletime_hrs > 0 ? 'boldClass' : null}
>
{item.totaltime}
Expand All @@ -340,4 +365,4 @@ const LeaderBoard = ({
);
};

export default LeaderBoard;
export default LeaderBoard;
2 changes: 1 addition & 1 deletion src/components/LeaderBoard/Leaderboard.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('Leaderboard page structure', () => {
expect(
leaderBoardItems.containsMatchingElement(
<td>
<span title="Total time">{lbData[i].totaltime}</span>
<span id="Total time">{lbData[i].totaltime}</span>
</td>,
),
).toBeTruthy();
Expand Down
98 changes: 98 additions & 0 deletions src/components/mouseoverText/MouseoverTextTotalTimeEditButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useState, useEffect } from 'react';
import { ENDPOINTS } from '../../utils/URL';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter, Label, Input } from 'reactstrap';
import { toast } from 'react-toastify';
import { connect } from 'react-redux';
import { createMouseoverText, updateMouseoverText } from '../../actions/mouseoverTextAction';


function MouseoverTextTotalTimeEditButton({
createMouseoverText,
updateMouseoverText,
onUpdate,
mouseoverTextId,
}) {
const [modalOpen, setModalOpen] = useState(false);
const [newText, setNewText] = useState('');


const handleUpdateText = () => {
setModalOpen(true);
};

const handleSaveText = () => {
const mouseoverTextFormat = {
newMouseoverText: newText,
};
if (newText) {
updateMouseoverText(mouseoverTextId, mouseoverTextFormat);
toast.success('Mouseover Text updated!');
} else {
createMouseoverText(mouseoverTextFormat);
toast.success('Mouseover Text created!');
}
setModalOpen(false);
onUpdate(newText);
};

const handleCancelSave = () => {
setModalOpen(false);
};


return (
<div>
<div>
<i
className="fa fa-pencil-square-o"
aria-hidden="true"
style={{ marginLeft: '5px', cursor: 'pointer' }}
onClick={handleUpdateText}
></i>
<Modal isOpen={modalOpen} toggle={() => setModalOpen(!modalOpen)}>
<ModalHeader toggle={() => setModalOpen(!modalOpen)}>Edit Mouseover Text</ModalHeader>
<ModalBody>
<Label for="newText">New Text</Label>
<Input
type="text"
name="newText"
id="newText"
value={newText}
onChange={(e) => setNewText(e.target.value)}
/>
</ModalBody>
<ModalFooter>
<Button color="primary" onClick={handleSaveText}>
Save
</Button>{' '}
<Button color="secondary" onClick={handleCancelSave}>
Cancel
</Button>
</ModalFooter>
</Modal>
</div>
</div>
);
}




const mapStateToProps = (state) => {
return {
mouseoverText: state?.mouseoverText?.[0]?.mouseoverText,
mouseoverTextId: state?.mouseoverText?.[0]?._id,
};
};

const mapDispatchToProps = dispatch => ({
createMouseoverText: mouseoverText => dispatch(createMouseoverText(mouseoverText)),
updateMouseoverText: (mouseoverTextId, mouseoverText) =>
dispatch(updateMouseoverText(mouseoverTextId, mouseoverText)),
});

export default connect(mapStateToProps, mapDispatchToProps)(MouseoverTextTotalTimeEditButton);




3 changes: 3 additions & 0 deletions src/constants/mouseoverTextConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const CREATE_MOUSEOVER_TEXT = 'CREATE_MOUSEOVER_TEXT';
export const GET_MOUSEOVER_TEXT = 'GET_MOUSEOVER_TEXT';
export const UPDATE_MOUSEOVER_TEXT = 'UPDATE_MOUSEOVER_TEXT';
4 changes: 3 additions & 1 deletion src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { timeZoneAPIReducer } from './timezoneApiReducer';
import { roleReducer } from './roleReducer';
import { ownerMessageReducer } from './ownerMessageReducer';
import { ownerStandardMessageReducer } from './ownerStandardMessageReducer';
import { mouseoverTextReducer } from './mouseoverTextReducer';

export default combineReducers({
auth: authReducer,
Expand Down Expand Up @@ -56,5 +57,6 @@ export default combineReducers({
taskEditSuggestions: taskEditSuggestionsReducer,
role: roleReducer,
ownerMessage: ownerMessageReducer,
ownerStandardMessage: ownerStandardMessageReducer
ownerStandardMessage: ownerStandardMessageReducer,
mouseoverText: mouseoverTextReducer,
});
20 changes: 20 additions & 0 deletions src/reducers/mouseoverTextReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as types from './../constants/mouseoverTextConstants';

const initialState = null;

export const mouseoverTextReducer = (state= initialState, action) => {
switch(action.type) {
case types.GET_MOUSEOVER_TEXT:
const data = action.payload;
return data;

case types.CREATE_MOUSEOVER_TEXT:
return state;

case types.UPDATE_MOUSEOVER_TEXT:
return state;

default:
return state;
}
}
3 changes: 3 additions & 0 deletions src/utils/URL.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ export const ENDPOINTS = {
OWNERSTANDARDMESSAGE: () => `${APIEndpoint}/ownerStandardMessage`,
OWNERSTANDARDMESSAGE_BY_ID: ownerStandardMessageId =>
`${APIEndpoint}/ownerStandardMessage/${ownerStandardMessageId}`,

MOUSEOVERTEXT: () => `${APIEndpoint}/mouseoverText`,
MOUSEOVERTEXT_BY_ID: mouseoverTextId => `${APIEndpoint}/mouseoverText/${mouseoverTextId}`,
};

export const ApiEndpoint = APIEndpoint;

0 comments on commit 56cc802

Please sign in to comment.