Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created a "You need to save!" Modal on the Candidate Decider #799

Merged
merged 7 commits into from
Dec 26, 2024

Conversation

jujulcrane
Copy link
Collaborator

Summary

Created a "You Need to Save!" modal if user tries to navigate away from a candidate without saving in the candidate decider. It allows them to either cancel, discard and proceed, or save and proceed.

Notion/Figma Link

https://www.notion.so/Candidate-Decider-Show-a-You-Need-to-Save-Modal-if-User-Tries-to-Navigate-Away-from-Candidate-W-1440ad723ce1806caae2c09118cacceb

Test Plan

Screen.Recording.2024-12-14.at.10.05.12.AM.mov

@jujulcrane jujulcrane requested a review from a team as a code owner December 14, 2024 15:10
@dti-github-bot
Copy link
Member

dti-github-bot commented Dec 14, 2024

[diff-counting] Significant lines: 85.

}
};

useEffect(() => {
setIsSaved(currentComment === defaultCurrentComment && currentRating === defaultCurrentRating);
Copy link
Collaborator

Choose a reason for hiding this comment

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

instead of storing this value in a state, what if we just had a variable that stored it? currentComment, currentRating, defaultCurrentComment, defaultCurrentRating are all state variables so they'd trigger a re-render anyways when they're changed.

In general it isn't the best pattern to store state that's derived from other states, since it requires you to keep them in sync, and can get lead to buggy behavior if they ever became out of sync

@andrew032011
Copy link
Collaborator

Can we also handle the two other methods of navigation? (Candidate ID dropdown and search bar)

<p>You have unsaved changes. Do you want to save them before navigating?</p>
</Modal.Content>
<Modal.Actions>
<Button onClick={() => setIsOpen(false)}>Clancel</Button>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
<Button onClick={() => setIsOpen(false)}>Clancel</Button>
<Button onClick={() => setIsOpen(false)}>Cancel</Button>

<div className={styles.applicationContainer}>
<div className={styles.searchBar}>
<SearchBar
instance={instance}
setCurrentCandidate={(candidate) => {
setCurrentCandidate(candidate);
populateReviewForCandidate(candidate);
if (!isSaved) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

can you abstract this function and the below into a helper function? for cleanliness ideally we can put all the handling for this into a single function, as opposed to spreading the logic around.

confirmNavigation(navigationDirection!);
}}
>
Save and {navigationDirection === 'previous' ? 'Go Back' : 'Proceed'}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm worried that this language is confusing "Go Back" might be interpreted as going back to the previous state (someone might think this is the same as "Discard")

maybe we don't need to show the navigation direction? Just "Save", "Discard", and "Cancel" seem good with me

const confirmNavigation = (direction: 'next' | 'previous' | 'other') => {
setIsOpen(false);
setIsBypassingModal(true);
setTimeout(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

hm, wondering what the point of setTimeout with 0 milliseconds is?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Oh oops I forgot to get rid of that - I was trying to fix a problem I was running into where it was making the user click save twice before it worked but I ended up fixing it a different way

Copy link
Collaborator

@andrew032011 andrew032011 left a comment

Choose a reason for hiding this comment

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

It's really coming together! Just some comments to help reduce code duplication

@@ -17,6 +17,14 @@ type CandidateDeciderProps = {
};

const CandidateDecider: React.FC<CandidateDeciderProps> = ({ uuid }) => {
const [isOpen, setIsOpen] = useState(false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

rename to isModalOpen

@@ -17,6 +17,14 @@ type CandidateDeciderProps = {
};

const CandidateDecider: React.FC<CandidateDeciderProps> = ({ uuid }) => {
const [isOpen, setIsOpen] = useState(false);
const [isBypassingModal, setIsBypassingModal] = useState(false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm pretty sure we can get rid of this isBypassingModal entirely. It feels like a combination of force and isSaved is enough.

@@ -109,17 +145,51 @@ const CandidateDecider: React.FC<CandidateDeciderProps> = ({ uuid }) => {
}
};

const handleCandiateChange = (candidate: number) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const handleCandiateChange = (candidate: number) => {
const handleCandidateChange = (candidate: number) => {

@@ -68,7 +79,13 @@ const CandidateDecider: React.FC<CandidateDeciderProps> = ({ uuid }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentCandidate, instance.candidates, reviews]);

const next = () => {
const next = (force = false) => {
if (!force && !isSaved && !isBypassingModal) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Now that we're not using navigationDirection anymore for the text, it feels like prev, next, and handleCandidateChange can all be condensed into just one function. Can you try deleting prev and next and just using handleCandiateChange? (you might have to check if candidate number is within 0...instance.candidates.length - 1 but otherwise the looks the same to me)

and then we can get rid of navigationDirection !

const [navigationDirection, setNavigationDirection] = useState<
'next' | 'previous' | 'other' | null
>(null);
const [navigationDirectionCandidate, setNavigationDirectionCandidate] = useState<number | null>(
Copy link
Collaborator

Choose a reason for hiding this comment

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

rename? "navigationDirection" doesn't really apply anymore if we're not tracking navigation direction

currentRating ?? 0,
currentComment ?? ''
);
if (nextCandidate !== null) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about a function navigateToNextCandidate for this and the code that's being duplicated below?

<div className={styles.applicationContainer}>
<div className={styles.searchBar}>
<SearchBar
instance={instance}
setCurrentCandidate={(candidate) => {
setCurrentCandidate(candidate);
populateReviewForCandidate(candidate);
handleCandidateChange(candidate);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Actually you can just pass in handleCandidateChange here
i.e.

(candidate) => { handleCandidateChange(candidate); }

is the same as

handleCandidateChange

disabled={currentCandidate === 0}
onClick={() => {
if (currentCandidate === 0) return;
setNextCandidate(currentCandidate - 1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

How about let's move setNextCandidate into handleCandidateChange? And move the bound checks there as well (==0 and == instance.candidates.length - 1)?

primary
onClick={() => {
if (nextCandidate !== null) {
setCurrentCandidate(nextCandidate);
Copy link
Collaborator

Choose a reason for hiding this comment

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

See above, move this into a separate function navigateToNextCandidate to reduce code duplication

@jujulcrane jujulcrane merged commit c4d5c04 into main Dec 26, 2024
17 checks passed
@jujulcrane jujulcrane deleted the juju-you-need-to-save branch December 26, 2024 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants