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

Profil - Uprava udajov #160

Merged
merged 6 commits into from
Nov 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"typescript": "^5.0.4",
"unstated-next": "^1.1.0"
"unstated-next": "^1.1.0",
"usehooks-ts": "^2.9.1"
},
"devDependencies": {
"@svgr/webpack": "^8.0.1",
Expand Down
4 changes: 2 additions & 2 deletions src/components/PageLayout/LoginForm/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export const LoginForm: FC<ILoginForm> = ({closeOverlay}) => {
const {login} = AuthContainer.useContainer()
const {handleSubmit, control} = useForm<LoginFormValues>({defaultValues})

const onSubmit: SubmitHandler<LoginFormValues> = async (data) => {
await login({data, onSuccess: closeOverlay})
const onSubmit: SubmitHandler<LoginFormValues> = (data) => {
login({data, onSuccess: closeOverlay})
}

const requiredRule = {required: '* Toto pole nemôže byť prázdne.'}
Expand Down
29 changes: 22 additions & 7 deletions src/components/Profile/ProfileDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@

import {Profile} from '@/types/api/personal'
import {AuthContainer} from '@/utils/AuthContainer'
import {useSeminarInfo} from '@/utils/useSeminarInfo'

import {Button, Link} from '../Clickable/Clickable'
import styles from './ProfileDetail.module.scss'

type ProfileLineInput = {
Expand All @@ -25,6 +27,7 @@

export const ProfileDetail: FC = () => {
const {isAuthed} = AuthContainer.useContainer()
const {seminar} = useSeminarInfo()

const {data} = useQuery({
queryKey: ['personal', 'profiles', 'myprofile'],
Expand All @@ -34,13 +37,25 @@
const profile = data?.data

return (
<Stack spacing={2}>
<ProfileLine label={'meno'} value={profile?.first_name + ' ' + profile?.last_name} />
<ProfileLine label={'e-mail'} value={profile?.email} />
<ProfileLine label={'škola'} value={profile?.school.verbose_name} />
<ProfileLine label={'ročník'} value={profile?.grade_name} />
<ProfileLine label={'tel. č.'} value={profile?.phone || '-'} />
<ProfileLine label={'tel. č. na rodiča'} value={profile?.parent_phone || '-'} />
<Stack>
<Stack spacing={2}>
<ProfileLine label={'meno'} value={profile?.first_name + ' ' + profile?.last_name} />
<ProfileLine label={'e-mail'} value={profile?.email} />
<ProfileLine label={'škola'} value={profile?.school.verbose_name} />
<ProfileLine label={'ročník'} value={profile?.grade_name} />
<ProfileLine label={'tel. č.'} value={profile?.phone || '-'} />
<ProfileLine label={'tel. č. na rodiča'} value={profile?.parent_phone || '-'} />
</Stack>
<Stack direction={'row'} mt={3} spacing={2}>
<Link href={`/${seminar}/profil/uprava`}>upraviť údaje</Link>
<Button
onClick={() => {
console.log('TODO: modal so zmenou hesla')

Check warning on line 53 in src/components/Profile/ProfileDetail.tsx

View workflow job for this annotation

GitHub Actions / branch-test

Unexpected console statement
}}
>
zmeniť heslo
</Button>
</Stack>
</Stack>
)
}
121 changes: 121 additions & 0 deletions src/components/Profile/ProfileForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import {useMutation, useQuery} from '@tanstack/react-query'
import axios from 'axios'
import {useRouter} from 'next/router'
import {FC} from 'react'
import {SubmitHandler, useForm} from 'react-hook-form'

import styles from '@/components/FormItems/Form.module.scss'
import {FormInput} from '@/components/FormItems/FormInput/FormInput'
import {SelectOption} from '@/components/FormItems/FormSelect/FormSelect'
import {IGeneralPostResponse} from '@/types/api/general'
import {Profile} from '@/types/api/personal'
import {AuthContainer} from '@/utils/AuthContainer'
import {useSeminarInfo} from '@/utils/useSeminarInfo'

import {Button} from '../Clickable/Clickable'
import {SchoolSubForm, SchoolSubFormValues} from '../SchoolSubForm/SchoolSubForm'

interface ProfileFormValues extends SchoolSubFormValues {
first_name?: string
last_name?: string
phone?: string
parent_phone?: string
}

const defaultValues: ProfileFormValues = {
first_name: '',
last_name: '',
phone: '',
parent_phone: '',
new_school_description: '',
without_school: false,
school: null,
school_not_found: false,
grade: '',
}

export const ProfileForm: FC = () => {
const {isAuthed} = AuthContainer.useContainer()

const {data} = useQuery({
queryKey: ['personal', 'profiles', 'myprofile'],
queryFn: () => axios.get<Profile>(`/api/personal/profiles/myprofile`),
enabled: isAuthed,
})
const profile = data?.data
const profileValues: ProfileFormValues = {
first_name: profile?.first_name,
last_name: profile?.last_name,
phone: profile?.phone ?? '',
parent_phone: profile?.parent_phone ?? '',
new_school_description: '',
without_school: profile?.school_id === 1,
school: ({id: profile?.school.code, label: profile?.school.verbose_name} as SelectOption) ?? null,
school_not_found: profile?.school_id === 0,
grade: profile?.grade ?? '',
}

const {handleSubmit, control, watch, setValue} = useForm<ProfileFormValues>({
defaultValues,
values: profileValues,
})

watch(['first_name'])

const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
})
}

const router = useRouter()

const {seminar} = useSeminarInfo()

const transformFormData = (data: ProfileFormValues) => ({
profile: {
first_name: data.first_name,
last_name: data.last_name,
school: data.school?.id,
phone: data.phone,
parent_phone: data.parent_phone,
grade: data.grade,
},
new_school_description: data.new_school_description || '',
})

const {mutate: submitFormData} = useMutation({
mutationFn: (data: ProfileFormValues) => {
return axios.put<IGeneralPostResponse>(`/api/user/user`, transformFormData(data))
},
onSuccess: () => router.push(`/${seminar}/profil`),
})

const onSubmit: SubmitHandler<ProfileFormValues> = (data) => {
submitFormData(data)
}

const requiredRule = {required: '* Toto pole nemôže byť prázdne.'}
const phoneRule = {
validate: (val?: string) => {
if (val && !/^(\+\d{10,12})$/u.test(val.replaceAll(/\s+/gu, '')))
return '* Zadaj telefónne číslo vo formáte validnom formáte +421 123 456 789 alebo +421123456789.'
},
}
return (
<div>
<form className={styles.form} onSubmit={handleSubmit(onSubmit)}>
<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} />
<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} />
<p style={{fontWeight: 'bold'}}>* takto označéné polia sú povinné</p>
<Button type="submit" onClick={scrollToTop}>
Uložiť údaje
</Button>
</form>
</div>
)
}
108 changes: 11 additions & 97 deletions src/components/RegisterForm/RegisterForm.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,27 @@
import {useMutation, useQuery} from '@tanstack/react-query'
import {useMutation} from '@tanstack/react-query'
import axios from 'axios'
import {useRouter} from 'next/router'
import {FC, useEffect, useRef} from 'react'
import {FC} from 'react'
import {SubmitHandler, useForm} from 'react-hook-form'

import styles from '@/components/FormItems/Form.module.scss'
import {FormAutocomplete} from '@/components/FormItems/FormAutocomplete/FormAutocomplete'
import {FormCheckbox} from '@/components/FormItems/FormCheckbox/FormCheckbox'
import {FormInput} from '@/components/FormItems/FormInput/FormInput'
import {FormSelect, SelectOption} from '@/components/FormItems/FormSelect/FormSelect'
import {IGrade} from '@/types/api/competition'
import {IGeneralPostResponse} from '@/types/api/general'
import {ISchool} from '@/types/api/personal'
import {useSeminarInfo} from '@/utils/useSeminarInfo'

import {Button} from '../Clickable/Clickable'
import {SchoolSubForm, SchoolSubFormValues} from '../SchoolSubForm/SchoolSubForm'

type RegisterFormValues = {
interface RegisterFormValues extends SchoolSubFormValues {
email?: string
password1?: string
password2?: string
first_name?: string
last_name?: string
phone?: string
parent_phone?: string
new_school_description?: string
without_school: boolean
school?: SelectOption | null
school_not_found: boolean
grade: number | ''
gdpr: boolean
gdpr?: boolean
}

const defaultValues: RegisterFormValues = {
Expand All @@ -49,8 +41,10 @@ const defaultValues: RegisterFormValues = {
}

export const RegisterForm: FC = () => {
const {handleSubmit, control, watch, setValue, getValues} = useForm<RegisterFormValues>({defaultValues})
const [school_not_found, without_school] = watch(['school_not_found', 'without_school'])
const {handleSubmit, control, watch, setValue, getValues} = useForm<RegisterFormValues>({
defaultValues,
values: defaultValues,
})

const scrollToTop = () => {
window.scrollTo({
Expand All @@ -59,58 +53,8 @@ export const RegisterForm: FC = () => {
})
}

const otherSchoolItem = useRef<SelectOption>()
const withoutSchoolItem = useRef<SelectOption>()

const router = useRouter()

// načítanie ročníkov z BE, ktorými vyplníme FormSelect s ročníkmi
const {data: gradesData} = useQuery({
queryKey: ['competition', 'grade'],
queryFn: () => axios.get<IGrade[]>(`/api/competition/grade`),
})
const grades = gradesData?.data ?? []
const gradeItems: SelectOption[] = grades.map(({id, name}) => ({id, label: name}))

// načítanie škôl z BE, ktorými vyplníme FormAutocomplete so školami
const {data: schoolsData} = useQuery({
queryKey: ['personal', 'schools'],
queryFn: () => axios.get<ISchool[]>(`/api/personal/schools`),
})
const schools = schoolsData?.data ?? []
const allSchoolItems: SelectOption[] = schools.map(({code, city, name, street}) => ({
id: code,
label: city ? `${name} ${street}, ${city}` : name,
}))
const emptySchoolItems = allSchoolItems.filter(({id}) => [0, 1].includes(id))
otherSchoolItem.current = emptySchoolItems.find(({id}) => id === 0)
withoutSchoolItem.current = emptySchoolItems.find(({id}) => id === 1)
const schoolItems = allSchoolItems.filter(({id}) => ![0, 1].includes(id))

// predvyplnenie/zmazania hodnôt pri zakliknutí checkboxu pre užívateľa po škole
useEffect(() => {
if (without_school) {
setValue('school', withoutSchoolItem.current)
setValue('grade', 13)
setValue('school_not_found', false)
} else {
setValue('school', null)
setValue('grade', '')
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [without_school])

// predvyplnenie/zmazania hodnôt pri zakliknutí checkboxu pre neznámu školu
useEffect(() => {
if (school_not_found) {
setValue('school', otherSchoolItem.current)
} else if (!without_school) {
setValue('school', null)
setValue('grade', '')
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [school_not_found])

const {seminar} = useSeminarInfo()

const transformFormData = (data: RegisterFormValues) => ({
Expand All @@ -136,7 +80,7 @@ export const RegisterForm: FC = () => {
onSuccess: () => router.push(`${router.asPath}/../verifikacia`),
})

const onSubmit: SubmitHandler<RegisterFormValues> = async (data) => {
const onSubmit: SubmitHandler<RegisterFormValues> = (data) => {
submitFormData(data)
}

Expand Down Expand Up @@ -189,37 +133,7 @@ export const RegisterForm: FC = () => {
/>
<FormInput control={control} name="first_name" label="krstné meno*" rules={requiredRule} />
<FormInput control={control} name="last_name" label="priezvisko*" rules={requiredRule} />
<FormCheckbox control={control} name="without_school" label="nie som študent základnej ani strednej školy." />
<FormAutocomplete
control={control}
name="school"
label="škola*"
options={school_not_found || without_school ? emptySchoolItems : schoolItems}
disabled={!schoolItems.length || school_not_found || without_school}
rules={requiredRule}
/>
<FormCheckbox
control={control}
name="school_not_found"
label="moja škola sa v zozname nenachádza."
disabled={without_school}
/>
{school_not_found && (
<FormInput
control={control}
name="new_school_description"
label="povedz nám, na akú školu chodíš, aby sme ti ju mohli pridať*"
rules={school_not_found ? requiredRule : {}}
/>
)}
<FormSelect
control={control}
name="grade"
label="ročník*"
options={gradeItems.filter(({id}) => id !== 13 || without_school)}
disabled={!gradeItems.length || without_school}
rules={requiredRule}
/>
<SchoolSubForm control={control} watch={watch} setValue={setValue} />
<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} />
<FormCheckbox
Expand Down
Loading
Loading