From 732d47b8230e17078c058bec29eadda54b413909 Mon Sep 17 00:00:00 2001 From: jujulcrane Date: Sat, 14 Dec 2024 10:04:11 -0500 Subject: [PATCH 1/7] Created a "You need to save!" reminder on the Candidate Decider --- .../Candidate-Decider/CandidateDecider.tsx | 78 +++++++++++++++++-- 1 file changed, 70 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx index 42d0217e3..1a09bb310 100644 --- a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx +++ b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { Button, Dropdown, Checkbox } from 'semantic-ui-react'; +import { Button, Dropdown, Checkbox, Modal } from 'semantic-ui-react'; import CandidateDeciderAPI from '../../API/CandidateDeciderAPI'; import ResponsesPanel from './ResponsesPanel'; import LocalProgressPanel from './LocalProgressPanel'; @@ -17,6 +17,10 @@ type CandidateDeciderProps = { }; const CandidateDecider: React.FC = ({ uuid }) => { + const [isSaved, setIsSaved] = useState(true); + const [isOpen, setIsOpen] = useState(false); + const [isBypassingModal, setIsBypassingModal] = useState(false); + const [navigationDirection, setNavigationDirection] = useState<'next' | 'previous' | null>(null); const [currentCandidate, setCurrentCandidate] = useState(0); const [showOtherVotes, setShowOtherVotes] = useState(false); @@ -68,7 +72,13 @@ const CandidateDecider: React.FC = ({ uuid }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentCandidate, instance.candidates, reviews]); - const next = () => { + const next = (force = false) => { + if (!force && !isSaved && !isBypassingModal) { + setNavigationDirection('next'); + setIsOpen(true); + return; + } + setIsBypassingModal(false); if (currentCandidate === instance.candidates.length - 1) return; setCurrentCandidate((prev) => { const nextCandidate = prev + 1; @@ -77,7 +87,13 @@ const CandidateDecider: React.FC = ({ uuid }) => { }); }; - const previous = () => { + const previous = (force = false) => { + if (!force && !isBypassingModal) { + setNavigationDirection('previous'); + setIsOpen(true); + return; + } + setIsBypassingModal(false); if (currentCandidate === 0) return; setCurrentCandidate((prev) => { const prevCandidate = prev - 1; @@ -86,6 +102,15 @@ const CandidateDecider: React.FC = ({ uuid }) => { }); }; + const confirmNavigation = (direction: 'next' | 'previous') => { + setIsOpen(false); + setIsBypassingModal(true); + setTimeout(() => { + if (direction === 'next') next(true); + else previous(true); + }, 0); + } + const handleRatingAndCommentChange = (id: number, rating: Rating, comment: string) => { CandidateDeciderAPI.updateRatingAndComment(instance.uuid, id, rating, comment); if (userInfo) { @@ -106,13 +131,52 @@ const CandidateDecider: React.FC = ({ uuid }) => { uuid: '' } ]); + setIsSaved(true); } }; + useEffect(() => { + setIsSaved( + currentComment === defaultCurrentComment && + currentRating === defaultCurrentRating + ); + }, [currentComment, currentRating, defaultCurrentComment, defaultCurrentRating]); + return instance.candidates.length === 0 ? (
) : ( +
+ setIsOpen(false)} + size="small" + > + Don't Forget To Save! + +

You have unsaved changes. Do you want to save them before navigating?

+
+ + + + + +
= ({ uuid }) => { /> of {instance.candidates.length} - - @@ -206,7 +196,12 @@ const CandidateDecider: React.FC = ({ uuid }) => { /> of {instance.candidates.length} - + @@ -172,8 +175,14 @@ const CandidateDecider: React.FC = ({ uuid }) => { { - setCurrentCandidate(candidate); - populateReviewForCandidate(candidate); + if (!isSaved) { + setNavigationDirection('other'); + setNavigationDirectionCandidate(candidate); + setIsOpen(true); + } else { + setCurrentCandidate(candidate); + populateReviewForCandidate(candidate); + } }} currentCandidate={currentCandidate} /> @@ -190,8 +199,14 @@ const CandidateDecider: React.FC = ({ uuid }) => { text: candidate.id }))} onChange={(_, data) => { - setCurrentCandidate(data.value as number); - populateReviewForCandidate(data.value as number); + if (!isSaved) { + setNavigationDirection('other'); + setNavigationDirectionCandidate(data.value as number); + setIsOpen(true); + } else { + setCurrentCandidate(data.value as number); + populateReviewForCandidate(data.value as number); + } }} /> of {instance.candidates.length} From 3d4f847641e0b8b379aa24463da27419a7d6a00e Mon Sep 17 00:00:00 2001 From: jujulcrane Date: Sat, 14 Dec 2024 14:19:23 -0500 Subject: [PATCH 4/7] fixed format and lint --- .../components/Candidate-Decider/CandidateDecider.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx index 293c67e79..a25359fcf 100644 --- a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx +++ b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx @@ -19,8 +19,12 @@ type CandidateDeciderProps = { const CandidateDecider: React.FC = ({ uuid }) => { const [isOpen, setIsOpen] = useState(false); const [isBypassingModal, setIsBypassingModal] = useState(false); - const [navigationDirection, setNavigationDirection] = useState<'next' | 'previous' | 'other' | null>(null); - const [navigationDirectionCandidate, setNavigationDirectionCandidate] = useState(null); + const [navigationDirection, setNavigationDirection] = useState< + 'next' | 'previous' | 'other' | null + >(null); + const [navigationDirectionCandidate, setNavigationDirectionCandidate] = useState( + null + ); const [currentCandidate, setCurrentCandidate] = useState(0); const [showOtherVotes, setShowOtherVotes] = useState(false); @@ -50,7 +54,8 @@ const CandidateDecider: React.FC = ({ uuid }) => { const [defaultCurrentRating, setDefaultCurrentRating] = useState(); const [defaultCurrentComment, setDefaultCurrentComment] = useState(); - const isSaved = currentComment === defaultCurrentComment && currentRating === defaultCurrentRating; + const isSaved = + currentComment === defaultCurrentComment && currentRating === defaultCurrentRating; const populateReviewForCandidate = (candidate: number) => { const rating = getRating(candidate); From 1129501d9646e8253cbae2e3465c59cbf85bf3e2 Mon Sep 17 00:00:00 2001 From: jujulcrane Date: Wed, 18 Dec 2024 14:41:50 -0500 Subject: [PATCH 5/7] Update CandidateDecider.tsx --- .../Candidate-Decider/CandidateDecider.tsx | 49 +++++++++---------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx index a25359fcf..08153f37f 100644 --- a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx +++ b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx @@ -112,16 +112,14 @@ const CandidateDecider: React.FC = ({ uuid }) => { const confirmNavigation = (direction: 'next' | 'previous' | 'other') => { setIsOpen(false); setIsBypassingModal(true); - setTimeout(() => { - if (direction === 'next') next(true); - else if (direction === 'previous') previous(true); - else if (direction === 'other') { - if (navigationDirectionCandidate) { - setCurrentCandidate(navigationDirectionCandidate); - populateReviewForCandidate(navigationDirectionCandidate); - } + if (direction === 'next') next(true); + else if (direction === 'previous') previous(true); + else if (direction === 'other') { + if (navigationDirectionCandidate) { + setCurrentCandidate(navigationDirectionCandidate); + populateReviewForCandidate(navigationDirectionCandidate); } - }, 0); + } }; const handleRatingAndCommentChange = (id: number, rating: Rating, comment: string) => { @@ -147,6 +145,17 @@ const CandidateDecider: React.FC = ({ uuid }) => { } }; + const handleCandiateChange = (candidate: number) => { + if (!isSaved) { + setNavigationDirection('other'); + setNavigationDirectionCandidate(candidate); + setIsOpen(true); + } else { + setCurrentCandidate(candidate); + populateReviewForCandidate(candidate); + } + }; + return instance.candidates.length === 0 ? (
) : ( @@ -168,10 +177,10 @@ const CandidateDecider: React.FC = ({ uuid }) => { confirmNavigation(navigationDirection!); }} > - Save and {navigationDirection === 'previous' ? 'Go Back' : 'Proceed'} + Save @@ -180,14 +189,7 @@ const CandidateDecider: React.FC = ({ uuid }) => { { - if (!isSaved) { - setNavigationDirection('other'); - setNavigationDirectionCandidate(candidate); - setIsOpen(true); - } else { - setCurrentCandidate(candidate); - populateReviewForCandidate(candidate); - } + handleCandiateChange(candidate); }} currentCandidate={currentCandidate} /> @@ -204,14 +206,7 @@ const CandidateDecider: React.FC = ({ uuid }) => { text: candidate.id }))} onChange={(_, data) => { - if (!isSaved) { - setNavigationDirection('other'); - setNavigationDirectionCandidate(data.value as number); - setIsOpen(true); - } else { - setCurrentCandidate(data.value as number); - populateReviewForCandidate(data.value as number); - } + handleCandiateChange(data.value as number); }} /> of {instance.candidates.length} From d4c18e738b0760b4d827a38bc4ac2b847b00beb8 Mon Sep 17 00:00:00 2001 From: jujulcrane Date: Fri, 20 Dec 2024 16:47:06 -0500 Subject: [PATCH 6/7] Update CandidateDecider.tsx --- .../Candidate-Decider/CandidateDecider.tsx | 96 +++++++------------ 1 file changed, 35 insertions(+), 61 deletions(-) diff --git a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx index 08153f37f..46c2ae5f1 100644 --- a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx +++ b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx @@ -17,12 +17,8 @@ type CandidateDeciderProps = { }; const CandidateDecider: React.FC = ({ uuid }) => { - const [isOpen, setIsOpen] = useState(false); - const [isBypassingModal, setIsBypassingModal] = useState(false); - const [navigationDirection, setNavigationDirection] = useState< - 'next' | 'previous' | 'other' | null - >(null); - const [navigationDirectionCandidate, setNavigationDirectionCandidate] = useState( + const [isModalOpen, setIsModalOpen] = useState(false); + const [nextCandidate, setNextCandidate] = useState( null ); const [currentCandidate, setCurrentCandidate] = useState(0); @@ -79,49 +75,6 @@ const CandidateDecider: React.FC = ({ uuid }) => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [currentCandidate, instance.candidates, reviews]); - const next = (force = false) => { - if (!force && !isSaved && !isBypassingModal) { - setNavigationDirection('next'); - setIsOpen(true); - return; - } - setIsBypassingModal(false); - if (currentCandidate === instance.candidates.length - 1) return; - setCurrentCandidate((prev) => { - const nextCandidate = prev + 1; - populateReviewForCandidate(nextCandidate); - return nextCandidate; - }); - }; - - const previous = (force = false) => { - if (!force && !isBypassingModal) { - setNavigationDirection('previous'); - setIsOpen(true); - return; - } - setIsBypassingModal(false); - if (currentCandidate === 0) return; - setCurrentCandidate((prev) => { - const prevCandidate = prev - 1; - populateReviewForCandidate(prevCandidate); - return prevCandidate; - }); - }; - - const confirmNavigation = (direction: 'next' | 'previous' | 'other') => { - setIsOpen(false); - setIsBypassingModal(true); - if (direction === 'next') next(true); - else if (direction === 'previous') previous(true); - else if (direction === 'other') { - if (navigationDirectionCandidate) { - setCurrentCandidate(navigationDirectionCandidate); - populateReviewForCandidate(navigationDirectionCandidate); - } - } - }; - const handleRatingAndCommentChange = (id: number, rating: Rating, comment: string) => { CandidateDeciderAPI.updateRatingAndComment(instance.uuid, id, rating, comment); if (userInfo) { @@ -145,11 +98,9 @@ const CandidateDecider: React.FC = ({ uuid }) => { } }; - const handleCandiateChange = (candidate: number) => { + const handleCandidateChange = (candidate: number) => { if (!isSaved) { - setNavigationDirection('other'); - setNavigationDirectionCandidate(candidate); - setIsOpen(true); + setIsModalOpen(true); } else { setCurrentCandidate(candidate); populateReviewForCandidate(candidate); @@ -160,13 +111,13 @@ const CandidateDecider: React.FC = ({ uuid }) => {
) : (
- setIsOpen(false)} size="small"> + setIsModalOpen(false)} size="small"> Don't Forget To Save!

You have unsaved changes. Do you want to save them before navigating?

- + - @@ -189,7 +155,7 @@ const CandidateDecider: React.FC = ({ uuid }) => { { - handleCandiateChange(candidate); + handleCandidateChange(candidate); }} currentCandidate={currentCandidate} /> @@ -206,7 +172,7 @@ const CandidateDecider: React.FC = ({ uuid }) => { text: candidate.id }))} onChange={(_, data) => { - handleCandiateChange(data.value as number); + handleCandidateChange(data.value as number); }} /> of {instance.candidates.length} @@ -215,7 +181,11 @@ const CandidateDecider: React.FC = ({ uuid }) => { basic color="blue" disabled={currentCandidate === 0} - onClick={() => previous(isSaved)} + onClick={() => { + if (currentCandidate === 0) return; + setNextCandidate(currentCandidate - 1); + handleCandidateChange(currentCandidate - 1); + }} > PREVIOUS @@ -223,7 +193,11 @@ const CandidateDecider: React.FC = ({ uuid }) => { basic color="blue" disabled={currentCandidate === instance.candidates.length - 1} - onClick={() => next(isSaved)} + onClick={() => { + if (currentCandidate === instance.candidates.length - 1) return; + setNextCandidate(currentCandidate + 1); + handleCandidateChange(currentCandidate + 1); + }} > NEXT From ee07bdd096c12c12290eba02f242153115ed9226 Mon Sep 17 00:00:00 2001 From: jujulcrane Date: Wed, 25 Dec 2024 15:54:59 +0100 Subject: [PATCH 7/7] reducing code duplication --- .../Candidate-Decider/CandidateDecider.tsx | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx index 46c2ae5f1..1f08a8bcf 100644 --- a/frontend/src/components/Candidate-Decider/CandidateDecider.tsx +++ b/frontend/src/components/Candidate-Decider/CandidateDecider.tsx @@ -18,9 +18,7 @@ type CandidateDeciderProps = { const CandidateDecider: React.FC = ({ uuid }) => { const [isModalOpen, setIsModalOpen] = useState(false); - const [nextCandidate, setNextCandidate] = useState( - null - ); + const [nextCandidate, setNextCandidate] = useState(null); const [currentCandidate, setCurrentCandidate] = useState(0); const [showOtherVotes, setShowOtherVotes] = useState(false); @@ -99,7 +97,11 @@ const CandidateDecider: React.FC = ({ uuid }) => { }; const handleCandidateChange = (candidate: number) => { + if (candidate < 0 || candidate >= instance.candidates.length) { + return; + } if (!isSaved) { + setNextCandidate(candidate); setIsModalOpen(true); } else { setCurrentCandidate(candidate); @@ -107,6 +109,13 @@ const CandidateDecider: React.FC = ({ uuid }) => { } }; + const navigateToNextCandidate = (candidate: number) => { + setCurrentCandidate(candidate); + populateReviewForCandidate(candidate); + setNextCandidate(null); + setIsModalOpen(false); + }; + return instance.candidates.length === 0 ? (
) : ( @@ -126,11 +135,8 @@ const CandidateDecider: React.FC = ({ uuid }) => { currentComment ?? '' ); if (nextCandidate !== null) { - setCurrentCandidate(nextCandidate); - populateReviewForCandidate(nextCandidate); - setNextCandidate(null); + navigateToNextCandidate(nextCandidate); } - setIsModalOpen(false); }} > Save @@ -139,11 +145,8 @@ const CandidateDecider: React.FC = ({ uuid }) => { primary onClick={() => { if (nextCandidate !== null) { - setCurrentCandidate(nextCandidate); - populateReviewForCandidate(nextCandidate); - setNextCandidate(null); + navigateToNextCandidate(nextCandidate); } - setIsModalOpen(false); }} > Discard @@ -182,8 +185,6 @@ const CandidateDecider: React.FC = ({ uuid }) => { color="blue" disabled={currentCandidate === 0} onClick={() => { - if (currentCandidate === 0) return; - setNextCandidate(currentCandidate - 1); handleCandidateChange(currentCandidate - 1); }} > @@ -194,8 +195,6 @@ const CandidateDecider: React.FC = ({ uuid }) => { color="blue" disabled={currentCandidate === instance.candidates.length - 1} onClick={() => { - if (currentCandidate === instance.candidates.length - 1) return; - setNextCandidate(currentCandidate + 1); handleCandidateChange(currentCandidate + 1); }} >