From 4c7f127ce3437cde8104350b83878de0268e0d6b Mon Sep 17 00:00:00 2001 From: Viliam Geffert <60324080+vgeffer@users.noreply.github.com> Date: Fri, 22 Nov 2024 20:06:00 +0100 Subject: [PATCH] =?UTF-8?q?413=20tla=C4=8Didlo=20gpdr=20pri=20registr?= =?UTF-8?q?=C3=A1ci=C3=AD=20opravi=C5=A5=20(#448)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * GDPR now opens on new page * Added useNavigationTrap and modal to reg form * Changed style and texts of the modal * Fixed lint errors * Fixed typo * Blocking now occurs on closing and refreshing page * larger link for GDPR in Registration ("tu" -> "najdete tu") * Changed dialog to custom component * use `disableSpacing` on DialogActions, add comment --------- Co-authored-by: rtrembecky --- src/components/Dialog/Dialog.tsx | 14 +- src/components/RegisterForm/RegisterForm.tsx | 159 ++++++++++++------- src/utils/useNavigationTrap.ts | 52 ++++++ 3 files changed, 161 insertions(+), 64 deletions(-) create mode 100644 src/utils/useNavigationTrap.ts diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index d3a01d8b..cd4a818e 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -5,7 +5,7 @@ import {CloseButton} from '../CloseButton/CloseButton' type DialogProps = { open: boolean - close: () => void + close?: () => void title?: ReactNode contentText?: string children?: ReactNode @@ -16,9 +16,11 @@ type DialogProps = { export const Dialog: FC = ({open, close, title, contentText, children, actions}) => { return ( - - - + {close && ( + + + + )} {title && {title}} {(contentText || children) && ( @@ -26,7 +28,9 @@ export const Dialog: FC = ({open, close, title, contentText, childr {children} )} - {actions && {actions}} + {/* MUI aplikuje nejaky default spacing, ale u nas je to kontraproduktivne, lebo globalne v teme + mame `flexDirection:'row-reverse'`. preto `disableSpacing` */} + {actions && {actions}} ) } diff --git a/src/components/RegisterForm/RegisterForm.tsx b/src/components/RegisterForm/RegisterForm.tsx index 25765332..362c78c1 100644 --- a/src/components/RegisterForm/RegisterForm.tsx +++ b/src/components/RegisterForm/RegisterForm.tsx @@ -2,15 +2,17 @@ import {Stack, Typography} from '@mui/material' import {useMutation} from '@tanstack/react-query' import axios, {AxiosError} from 'axios' import {useRouter} from 'next/router' -import {FC} from 'react' -import {SubmitHandler, useForm} from 'react-hook-form' +import {FC, useState} from 'react' +import {SubmitHandler, useForm, useFormState} from 'react-hook-form' import {FormInput} from '@/components/FormItems/FormInput/FormInput' import {IGeneralPostResponse} from '@/types/api/general' import {useSeminarInfo} from '@/utils/useSeminarInfo' +import {useNavigationTrap} from '../../utils/useNavigationTrap' import {Button} from '../Clickable/Button' import {Link} from '../Clickable/Link' +import {Dialog} from '../Dialog/Dialog' import {SchoolSubForm, SchoolSubFormValues} from '../SchoolSubForm/SchoolSubForm' interface RegisterFormValues extends SchoolSubFormValues { @@ -48,6 +50,8 @@ export const RegisterForm: FC = () => { values: defaultValues, }) + const {isDirty} = useFormState({control: control}) + const scrollToTop = () => { window.scrollTo({ top: 0, @@ -102,64 +106,101 @@ export const RegisterForm: FC = () => { return '* Zadaj telefónne číslo vo formáte +421 123 456 789 alebo +421123456789.' }, } + + const [dialogOpen, setDialogOpen] = useState(false) + + const {continueNavigation} = useNavigationTrap({ + shouldBlockNavigation: isDirty, + onNavigate: () => { + setDialogOpen(true) + }, + }) + return ( -
- - - - { - if (val !== getValues().password1) return '* Zadané heslá sa nezhodujú.' - }, - }} - /> - - - - - - - * takto označené polia sú povinné - - - Vyplnením a odoslaním registrácie beriem na vedomie, že moje osobné údaje budú spracované v súlade so zákonom - o ochrane osobných údajov. Bližšie informácie nájdete tu. - - - + <> + + + + + } + /> + + + + + + { + if (val !== getValues().password1) return '* Zadané heslá sa nezhodujú.' + }, + }} + /> + + + + + + + * takto označené polia sú povinné + + + Vyplnením a odoslaním registrácie beriem na vedomie, že moje osobné údaje budú spracované v súlade so + zákonom o ochrane osobných údajov. Bližšie informácie + + nájdete tu + + . + + + + - - + + ) } diff --git a/src/utils/useNavigationTrap.ts b/src/utils/useNavigationTrap.ts new file mode 100644 index 00000000..0cc1a5ce --- /dev/null +++ b/src/utils/useNavigationTrap.ts @@ -0,0 +1,52 @@ +import {useRouter} from 'next/router' +import {useCallback, useEffect, useRef} from 'react' + +interface NavigationTrapProps { + shouldBlockNavigation: boolean + onNavigate: () => void +} + +export const useNavigationTrap = ({shouldBlockNavigation, onNavigate}: NavigationTrapProps) => { + const router = useRouter() + + const currentPath = router.asPath + const nextPath = useRef('') + const navConfirmed = useRef(false) + + const killNavigation = useCallback(() => { + router.events.emit('routeChangeError', '', '', {shallow: false}) + + // eslint-disable-next-line no-throw-literal + throw 'Canceling navigation due to unsaved changes in the page' + }, [router]) + + useEffect(() => { + const pageNavigate = (path: string) => { + if (navConfirmed.current) return + if (shouldBlockNavigation && path !== currentPath) { + nextPath.current = path + onNavigate() + killNavigation() + } + } + + const pageExit = (e: BeforeUnloadEvent) => { + e.preventDefault() + } + + router.events.on('routeChangeStart', pageNavigate) + window.addEventListener('beforeunload', pageExit) + + return () => { + router.events.off('routeChangeStart', pageNavigate) + window.removeEventListener('beforeunload', pageExit) + } + }, [shouldBlockNavigation, currentPath, killNavigation, onNavigate, router.events]) + + const continueNavigation = () => { + navConfirmed.current = true + router.push(nextPath.current) + } + + return {nextPath, continueNavigation} +}