diff --git a/src/actions/warnings.js b/src/actions/warnings.js index 0a1c524945..8deee989d6 100644 --- a/src/actions/warnings.js +++ b/src/actions/warnings.js @@ -9,6 +9,7 @@ import { postNewWarning as postNewWarningAction, deleteWarningDescription as deleteWarningDescriptionAction, updateWarningDescription as updateWarningDescriptionAction, + reorderWarningDescriptions as reorderWarningDescriptionsAction, editWarningDescription as editWarningDescriptionAction, } from '../constants/warning'; @@ -163,3 +164,18 @@ export const updateWarningDescription = warningDescriptionId => { } }; }; + +export const reorderWarningDescriptions = warningDescriptions => { + const url = ENDPOINTS.REORDER_WARNING_DESCRIPTIONS(warningDescriptions); + + return async dispatch => { + try { + const res = await axios.put(url, { warningDescriptions }); + const response = await dispatch(reorderWarningDescriptionsAction(res.data)); + + return response.payload; + } catch (error) { + return { error: error.message }; + } + }; +}; diff --git a/src/components/Warnings/WarningIcon.jsx b/src/components/Warnings/WarningIcon.jsx index 001ad1153b..4af165be13 100644 --- a/src/components/Warnings/WarningIcon.jsx +++ b/src/components/Warnings/WarningIcon.jsx @@ -8,12 +8,8 @@ import moment from 'moment'; import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; import Popover from 'react-bootstrap/Popover'; import styles from './Warnings.module.css'; +import { useSelector } from 'react-redux'; -const colors = { - blue: 'blue', - red: 'red', - yellow: '#ffc107', -}; function WarningIcon({ userProfileModal, id, @@ -34,7 +30,7 @@ function WarningIcon({ // numberOfWarnings, // } = props; - const btnColor = color ? colors[color] : 'white'; + const darkMode = useSelector(state => state.theme.darkMode); // eslint-disable-next-line no-shadow const handleIssueWarning = id => { @@ -60,23 +56,29 @@ function WarningIcon({ }; const popover = ( - + Date Assigned - {dateAssigned} + {dateAssigned} ); + const warningColor = () => { + if (color === 'red') { + return styles.warningColorRed; + } else if (color === 'blue') { + return styles.warningColorBlue; + } else if (color === 'yellow') { + return styles.warningColorYellow; + } + return ''; + }; + const renderIcon = ( handleIssueWarning(id)} icon={faCircle} diff --git a/src/components/Warnings/Warnings.jsx b/src/components/Warnings/Warnings.jsx index 636aad1bf4..92c12d1847 100644 --- a/src/components/Warnings/Warnings.jsx +++ b/src/components/Warnings/Warnings.jsx @@ -150,20 +150,22 @@ export default function Warning({ personId, username, userRole, displayUser }) { const warnings = !toggle ? null - : usersWarnings.map(warning => ( -
-
-

{warning.title}

- + : usersWarnings + .map(warning => ( +
+
+

{warning.title}

+ +
-
- )); + )) + .sort((warn1, warn2) => warn1.order - warn2.order); return (
diff --git a/src/components/Warnings/Warnings.module.css b/src/components/Warnings/Warnings.module.css index ada93ad78d..3df44a5cc2 100644 --- a/src/components/Warnings/Warnings.module.css +++ b/src/components/Warnings/Warnings.module.css @@ -201,6 +201,10 @@ margin: 0.5em 0; } +.popover .modal__information.darkMode { + color: black !important; +} + .alert__container { margin-top: 0.5em; text-align: center; @@ -241,6 +245,39 @@ input[type='text'] { z-index: 1100 !important; } +.warning__tracker__modal_save__changes__btns { + width: 20%; +} + +.icon { + border: 1px solid black; + border-radius: 50%; + width: 10px; + height: 10px; + margin: 0 0.175em; + color: white; +} + +.icon.warningColorRed { + color: red !important; +} + +.icon.warningColorBlue { + color: blue !important; +} + +.icon.warningColorYellow { + color: #ffc107 !important; +} + +.icon path { + color: inherit !important; +} + +.popoverDarkMode h4 { + background-color: rgb(109 109 109) !important; +} + /* Media query for screens smaller than 767px */ @media (width <= 767px) { .warnings-container { diff --git a/src/components/Warnings/__test__/Warnings.test.jsx b/src/components/Warnings/__test__/Warnings.test.jsx index 6dafcae1c2..bd71492e40 100644 --- a/src/components/Warnings/__test__/Warnings.test.jsx +++ b/src/components/Warnings/__test__/Warnings.test.jsx @@ -1,11 +1,12 @@ // eslint-disable-next-line no-unused-vars import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; -import { Provider } from 'react-redux'; +import { Provider, useSelector } from 'react-redux'; import { configureStore } from 'redux-mock-store'; import thunk from 'redux-thunk'; import Warning from '../Warnings'; import * as warningActions from '../../../actions/warnings'; +// need to add handling for darkmode vi.mock('../../../actions/warnings', () => ({ getWarningsByUserId: vi.fn(() => () => Promise.resolve([])), @@ -13,6 +14,15 @@ vi.mock('../../../actions/warnings', () => ({ deleteWarningsById: vi.fn(() => () => Promise.resolve([])), })); +vi.mock('react-redux', async () => { + const actual = await vi.importActual('react-redux'); + return { + __esModule: true, + ...actual, + useSelector: vi.fn(), + }; +}); + const mockStore = configureStore([thunk]); describe('Warning Component', () => { @@ -60,6 +70,12 @@ describe('Warning Component', () => { Promise.resolve([{ title: 'Warning 1', warnings: [] }]), ); + useSelector.mockImplementation(selector => + selector({ + theme: { darkMode: false }, + }), + ); + render( diff --git a/src/components/Warnings/modals/WarningTrackerModal.jsx b/src/components/Warnings/modals/WarningTrackerModal.jsx index dd95d43f09..ca5fb1e93b 100644 --- a/src/components/Warnings/modals/WarningTrackerModal.jsx +++ b/src/components/Warnings/modals/WarningTrackerModal.jsx @@ -7,7 +7,7 @@ /* eslint-disable no-alert */ import { Modal, ModalHeader, ModalBody, ModalFooter, Button, Alert } from 'reactstrap'; import { useEffect, useState } from 'react'; -import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; import OverlayTrigger from 'react-bootstrap/OverlayTrigger'; @@ -19,6 +19,7 @@ import { updateWarningDescription, deleteWarningDescription, editWarningDescription, + reorderWarningDescriptions, } from '../../../actions/warnings'; import styles from '../Warnings.module.css'; @@ -47,6 +48,9 @@ function WarningTrackerModal({ const [warningWasEdited, setWarningWasEdited] = useState(false); const [isPermanent, setIsPermanent] = useState(false); const [error, setError] = useState(null); + const [dragIndex, setDragIndex] = useState(null); + const [warningsEdited, setWarningsEdited] = useState(false); + const darkMode = useSelector(state => state.theme.darkMode); const dispatch = useDispatch(); const rolesAllowedToTracking = ['Administrator', 'Owner']; @@ -83,20 +87,53 @@ function WarningTrackerModal({ const handleOverlayTrigger = title => { if (title === 'info') { return ( - - Information + + + Information + -

+

Pressing the "+" button will allow you to activate the warning tracker.

-

+

Pressing the "-" button will allow you to deactivate the warning tracker.

-

+

Pressing the "x" button will allow you to delete the warning tracker. This will also delete all assoicated warnings for every user (be careful doing this!).

-

+

Pressing the "Add New Warning Tracker" button will allow you to add a new warning to the list.

@@ -165,25 +202,42 @@ function WarningTrackerModal({ const handleCancelEdit = () => { fetchWarningDescriptions(); setWarningEdited(false); + setWarningsEdited(false); setWarningWasEdited(false); }; const handleSaveEditedWarning = () => { - dispatch(editWarningDescription(editedWarning)).then(res => { - if (res.error) { - setError(res.error); + if (warningEdited) { + dispatch(editWarningDescription(editedWarning)).then(res => { + if (res.error) { + setError(res.error); + setWarningEdited(false); + setEditedWarning(null); + fetchWarningDescriptions(); + return; + } setWarningEdited(false); setEditedWarning(null); + setError(null); + getUsersWarnings(); fetchWarningDescriptions(); - return; - } - setWarningEdited(false); - setEditedWarning(null); - setError(null); - getUsersWarnings(); - fetchWarningDescriptions(); - setWarningWasEdited(true); - }); + setWarningWasEdited(true); + }); + } else if (warningsEdited) { + dispatch(reorderWarningDescriptions(warningDescriptions)).then(res => { + if (res?.error) { + setError(res.error); + setWarningsEdited(false); + fetchWarningDescriptions(); + return; + } + setWarningsEdited(false); + setError(null); + getUsersWarnings(); + fetchWarningDescriptions(); + setWarningWasEdited(true); + }); + } }; // eslint-disable-next-line no-shadow @@ -246,6 +300,23 @@ function WarningTrackerModal({ ); } + const handleDragStart = index => { + setDragIndex(index); + }; + + const handleDragOver = (e, index) => { + e.preventDefault(); + if (dragIndex === index) return; + + const updatedWarningList = [...warningDescriptions]; + const [movedWarning] = updatedWarningList.splice(dragIndex, 1); + updatedWarningList.splice(index, 0, movedWarning); + + setDragIndex(index); + setWarningDescriptions(updatedWarningList); + setWarningsEdited(true); + }; + return ( // need to make .modal in modal.css z-index go to 1051 // or make .modal-backdrop z-index go to 1049 otherwise the important makes it nullify the warning tracker modal @@ -285,74 +356,89 @@ function WarningTrackerModal({ )} - {warningDescriptions.map((warning, index) => ( -
- reorder - {warning.activeWarning ? ( - - - - ) : ( - - + + ) : ( + - - - - )} + + + )} + + +