Skip to content

Commit

Permalink
413 tlačidlo gpdr pri registrácií opraviť (#448)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
vgeffer and rtrembecky authored Nov 22, 2024
1 parent 1485862 commit 4c7f127
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 64 deletions.
14 changes: 9 additions & 5 deletions src/components/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {CloseButton} from '../CloseButton/CloseButton'

type DialogProps = {
open: boolean
close: () => void
close?: () => void
title?: ReactNode
contentText?: string
children?: ReactNode
Expand All @@ -16,17 +16,21 @@ type DialogProps = {
export const Dialog: FC<DialogProps> = ({open, close, title, contentText, children, actions}) => {
return (
<MuiDialog open={open} onClose={close}>
<Box position="absolute" top={16} right={16}>
<CloseButton size={30} onClick={close} invertColors />
</Box>
{close && (
<Box position="absolute" top={16} right={16}>
<CloseButton size={30} onClick={close} invertColors />
</Box>
)}
{title && <DialogTitle variant="h2">{title}</DialogTitle>}
{(contentText || children) && (
<DialogContent>
{contentText && <DialogContentText>{contentText}</DialogContentText>}
{children}
</DialogContent>
)}
{actions && <DialogActions>{actions}</DialogActions>}
{/* MUI aplikuje nejaky default spacing, ale u nas je to kontraproduktivne, lebo globalne v teme
mame `flexDirection:'row-reverse'`. preto `disableSpacing` */}
{actions && <DialogActions disableSpacing>{actions}</DialogActions>}
</MuiDialog>
)
}
159 changes: 100 additions & 59 deletions src/components/RegisterForm/RegisterForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -48,6 +50,8 @@ export const RegisterForm: FC = () => {
values: defaultValues,
})

const {isDirty} = useFormState<RegisterFormValues>({control: control})

const scrollToTop = () => {
window.scrollTo({
top: 0,
Expand Down Expand Up @@ -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 (
<form onSubmit={handleSubmit(onSubmit)}>
<Stack gap={2}>
<FormInput
control={control}
name="email"
label="e-mail*"
rules={{
...requiredRule,
pattern: {
value: /^[\w%+.-]+@[\d.a-z-]+\.[a-z]{2,}$/iu,
message: '* Vlož správnu emailovú adresu.',
},
}}
/>
<FormInput
control={control}
name="password1"
label="heslo*"
type="password"
rules={{
...requiredRule,
minLength: {
value: 8,
message: '* Toto heslo je príliš krátke. Musí obsahovať aspoň 8 znakov.',
},
}}
/>
<FormInput
control={control}
name="password2"
label="potvrdenie hesla*"
type="password"
rules={{
...requiredRule,
validate: (val) => {
if (val !== getValues().password1) return '* Zadané heslá sa nezhodujú.'
},
}}
/>
<FormInput control={control} name="first_name" label="krstné meno*" rules={requiredRule} />
<FormInput control={control} name="last_name" label="priezvisko*" rules={requiredRule} />
<SchoolSubForm control={control} watch={watch} setValue={setValue} gap={2} />
<FormInput control={control} name="phone" label="telefónne číslo" rules={phoneRule} />
<FormInput control={control} name="parent_phone" label="telefónne číslo na rodiča" rules={phoneRule} />
<Typography variant="body2" fontWeight={800}>
* takto označené polia sú povinné
</Typography>
<Typography variant="body2">
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 <Link href={`./gdpr`}>tu</Link>.
</Typography>
<Stack direction="row" justifyContent="flex-end" mt={3}>
<Button variant="button2" type="submit" onClick={scrollToTop}>
Registrovať
</Button>
<>
<Dialog
open={dialogOpen}
title="Neuložené zmeny"
contentText="Máš neuložené zmeny. Naozaj chceš opustiť stránku?"
actions={
<>
<Button variant="button2" onClick={continueNavigation}>
Opustiť stránku
</Button>
<Button
variant="button2"
onClick={() => {
setDialogOpen(false)
}}
>
Zostať na stránke
</Button>
</>
}
/>

<form onSubmit={handleSubmit(onSubmit)}>
<Stack gap={2}>
<FormInput
control={control}
name="email"
label="e-mail*"
rules={{
...requiredRule,
pattern: {
value: /^[\w%+.-]+@[\d.a-z-]+\.[a-z]{2,}$/iu,
message: '* Vlož správnu emailovú adresu.',
},
}}
/>
<FormInput
control={control}
name="password1"
label="heslo*"
type="password"
rules={{
...requiredRule,
minLength: {
value: 8,
message: '* Toto heslo je príliš krátke. Musí obsahovať aspoň 8 znakov.',
},
}}
/>
<FormInput
control={control}
name="password2"
label="potvrdenie hesla*"
type="password"
rules={{
...requiredRule,
validate: (val) => {
if (val !== getValues().password1) return '* Zadané heslá sa nezhodujú.'
},
}}
/>
<FormInput control={control} name="first_name" label="krstné meno*" rules={requiredRule} />
<FormInput control={control} name="last_name" label="priezvisko*" rules={requiredRule} />
<SchoolSubForm control={control} watch={watch} setValue={setValue} gap={2} />
<FormInput control={control} name="phone" label="telefónne číslo" rules={phoneRule} />
<FormInput control={control} name="parent_phone" label="telefónne číslo na rodiča" rules={phoneRule} />
<Typography variant="body2" fontWeight={800}>
* takto označené polia sú povinné
</Typography>
<Typography variant="body2">
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
<Link href={`./gdpr`} target="_blank">
nájdete tu
</Link>
.
</Typography>
<Stack direction="row" justifyContent="flex-end" mt={3}>
<Button variant="button2" type="submit" onClick={scrollToTop}>
Registrovať
</Button>
</Stack>
</Stack>
</Stack>
</form>
</form>
</>
)
}
52 changes: 52 additions & 0 deletions src/utils/useNavigationTrap.ts
Original file line number Diff line number Diff line change
@@ -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}
}

0 comments on commit 4c7f127

Please sign in to comment.