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 4 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
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 {FC} from 'react'

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 @@ const ProfileLine: FC<ProfileLineInput> = ({label, value}) => {

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

const {data} = useQuery({
queryKey: ['personal', 'profiles', 'myprofile'],
Expand All @@ -34,13 +37,25 @@ export const ProfileDetail: FC = () => {
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')
}}
>
zmeniť heslo
</Button>
</Stack>
</Stack>
)
}
124 changes: 124 additions & 0 deletions src/components/Profile/ProfileForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
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} from '../SchoolSubForm/SchoolSubForm'

export type ProfileFormValues = {
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 | ''
}

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,
})

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> = async (data) => {
submitFormData(data)
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

asi raz pridame daky eslint rule co trochu zjednoti/da bacha na async funkcie... tu deklarujeme fuknciu ako async ale nevolame v nej await, co nejakemu rulu vadi, ale v podstate je to tu v pohode


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>
)
}
95 changes: 6 additions & 89 deletions src/components/RegisterForm/RegisterForm.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
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 {SelectOption} from '@/components/FormItems/FormSelect/FormSelect'
import {IGeneralPostResponse} from '@/types/api/general'
import {ISchool} from '@/types/api/personal'
import {useSeminarInfo} from '@/utils/useSeminarInfo'

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

type RegisterFormValues = {
export type RegisterFormValues = {
email?: string
password1?: string
password2?: string
Expand Down Expand Up @@ -50,7 +48,6 @@

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 scrollToTop = () => {
window.scrollTo({
Expand All @@ -59,58 +56,8 @@
})
}

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 Down Expand Up @@ -189,37 +136,7 @@
/>
<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} />

Check failure on line 139 in src/components/RegisterForm/RegisterForm.tsx

View workflow job for this annotation

GitHub Actions / branch-test

Type 'Control<RegisterFormValues, any>' is not assignable to type 'Control<RegisterFormValues | ProfileFormValues, unknown>'.

Check failure on line 139 in src/components/RegisterForm/RegisterForm.tsx

View workflow job for this annotation

GitHub Actions / branch-test

Type 'UseFormWatch<RegisterFormValues>' is not assignable to type 'UseFormWatch<RegisterFormValues | ProfileFormValues>'.

Check failure on line 139 in src/components/RegisterForm/RegisterForm.tsx

View workflow job for this annotation

GitHub Actions / branch-test

Type 'UseFormSetValue<RegisterFormValues>' is not assignable to type 'UseFormSetValue<RegisterFormValues | ProfileFormValues>'.
<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