Ott 463 user account fields (#177)

* Ott 463 user account fields 1 (#175)

* fix(ott-463): made some changes in combobox

* fix(ott-463): fixed input and search icon

* fix(ott-463): made some minor changes

* Ott 463 user account fields 2 (#176)

* fix(ott-463): added fields

* fix(ott-463): bug fix

* fix(ott-463): moved hooks from register to common hooks folder

* fix(ott-463): deleted mutation
keep-around/af30b88d367751c9e05a735e4a0467a96238ef47
Armen 5 years ago committed by GitHub
parent a89571e5ad
commit affac606b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      src/config/form.tsx
  2. 2
      src/features/Combobox/components/Arrow/index.tsx
  3. 64
      src/features/Combobox/hooks/index.tsx
  4. 4
      src/features/Combobox/hooks/useKeyboardScroll.tsx
  5. 4
      src/features/Combobox/index.tsx
  6. 3
      src/features/Combobox/styled.tsx
  7. 1
      src/features/Combobox/types.tsx
  8. 4
      src/features/Common/Input/index.tsx
  9. 8
      src/features/Common/Input/styled.tsx
  10. 6
      src/features/Login/hooks.tsx
  11. 2
      src/features/Login/index.tsx
  12. 14
      src/features/Register/components/RegistrationStep/hooks/useSubmitHandler.tsx
  13. 6
      src/features/Register/components/RegistrationStep/index.tsx
  14. 1
      src/features/Search/styled.tsx
  15. 73
      src/features/UserAccount/hooks.tsx
  16. 165
      src/features/UserAccount/index.tsx
  17. 8
      src/features/UserAccount/styled.tsx
  18. 11
      src/features/UserAccount/useValidateForm.tsx
  19. 0
      src/helpers/formatPhoneCode/__tests__/index.tsx
  20. 0
      src/helpers/formatPhoneCode/index.tsx
  21. 0
      src/helpers/isValidEmail/__tests__/index.tsx
  22. 0
      src/helpers/isValidEmail/index.tsx
  23. 0
      src/helpers/isValidPassword/__tests__/index.tsx
  24. 0
      src/helpers/isValidPassword/index.tsx
  25. 0
      src/helpers/isValidPhone/__tests__/index.tsx
  26. 0
      src/helpers/isValidPhone/index.tsx
  27. 21
      src/hooks/useCities.tsx
  28. 76
      src/hooks/useForm.tsx
  29. 8
      src/hooks/useValidateForm.tsx
  30. 6
      src/requests/saveUserInfo.tsx

@ -1,15 +1,17 @@
export const formIds = { export const formIds = {
address1: 'address1', address1: 'address_line1',
address2: 'address2', address2: 'address_line2',
city: 'city', city: 'city',
cityId: 'cityId',
country: 'country', country: 'country',
countryId: 'countryId',
email: 'email', email: 'email',
firstname: 'firstname', firstname: 'firstname',
formError: 'formError', formError: 'formError',
lastname: 'lastname', lastname: 'lastname',
password: 'password', password: 'password',
phone: 'phone', phone: 'phone',
postalCode: 'postalCode', postalCode: 'postal_code',
region: 'region', region: 'region',
} }

@ -9,7 +9,7 @@ type Props = {
const Wrapper = styled.div<{ isExpanded: boolean }>` const Wrapper = styled.div<{ isExpanded: boolean }>`
position: absolute; position: absolute;
right: 16px; right: 22px;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
width: 20px; width: 20px;

@ -1,4 +1,4 @@
import type { ChangeEvent } from 'react' import type { ChangeEvent, KeyboardEvent } from 'react'
import { import {
useState, useState,
useCallback, useCallback,
@ -10,8 +10,9 @@ import toLower from 'lodash/toLower'
import find from 'lodash/find' import find from 'lodash/find'
import trim from 'lodash/trim' import trim from 'lodash/trim'
import size from 'lodash/size' import size from 'lodash/size'
import isEmpty from 'lodash/isEmpty'
import { useToggle, useEventListener } from 'hooks' import { useToggle } from 'hooks'
import type { Props, Option } from '../types' import type { Props, Option } from '../types'
import { matchSort } from '../helpers' import { matchSort } from '../helpers'
@ -40,7 +41,10 @@ export const useCombobox = <T extends Option>(props: Props<T>) => {
const inputFieldRef = useRef<HTMLInputElement>(null) const inputFieldRef = useRef<HTMLInputElement>(null)
const [index, setIndex] = useState(0) const [index, setIndex] = useState(0)
const { onKeyDown, popoverRef } = useKeyboardScroll() const {
onKeyDownScroll,
popoverRef,
} = useKeyboardScroll()
const { const {
onQueryChange, onQueryChange,
@ -83,25 +87,19 @@ export const useCombobox = <T extends Option>(props: Props<T>) => {
const onOutsideClick = (event: MouseEvent) => { const onOutsideClick = (event: MouseEvent) => {
if (event.target !== inputFieldRef.current as HTMLInputElement) { if (event.target !== inputFieldRef.current as HTMLInputElement) {
close() close()
if (results[0]?.name.includes(query) && query) {
onOptionSelect(results[0].name)
} else {
onOptionSelect(query)
}
} }
} }
const optionSelectListener = useCallback((event: KeyboardEvent) => { const onInputBlur = () => {
if (event.key === 'ArrowUp') { if (isEmpty(results)) {
setIndex(index - 1) onOptionSelect('')
} else if (event.key === 'ArrowDown') {
setIndex(index + 1)
} else if (event.key === 'Enter') {
onSelect?.(results[index])
close()
inputFieldRef.current?.blur()
} }
}, [ }
close,
index,
results,
onSelect,
])
useEffect(() => { useEffect(() => {
const lastElementIndex = size(results) - 1 const lastElementIndex = size(results) - 1
@ -113,28 +111,34 @@ export const useCombobox = <T extends Option>(props: Props<T>) => {
results, results,
]) ])
useEventListener({ const onKeyDown = useCallback((event: KeyboardEvent<HTMLInputElement>) => {
callback: optionSelectListener, onKeyDownScroll(event)
event: 'keydown',
})
const escPressListener = useCallback((event: KeyboardEvent) => {
if (event.key === 'Escape') { if (event.key === 'Escape') {
close() close()
inputFieldRef.current?.blur() inputFieldRef.current?.blur()
} else if (event.key === 'ArrowUp') {
setIndex(index - 1)
} else if (event.key === 'ArrowDown') {
setIndex(index + 1)
} else if (event.key === 'Enter') {
onSelect?.(results[index])
close()
inputFieldRef.current?.blur()
} }
}, [close]) }, [
close,
useEventListener({ index,
callback: escPressListener, results,
event: 'keydown', onSelect,
}) onKeyDownScroll,
])
return { return {
close, close,
index, index,
inputFieldRef, inputFieldRef,
isOpen, isOpen,
onInputBlur,
onKeyDown, onKeyDown,
onOptionSelect, onOptionSelect,
onOutsideClick, onOutsideClick,

@ -3,7 +3,7 @@ import { useRef } from 'react'
export const useKeyboardScroll = () => { export const useKeyboardScroll = () => {
const popoverRef = useRef<HTMLUListElement>(null) const popoverRef = useRef<HTMLUListElement>(null)
const onKeyDown = (event: KeyboardEvent<HTMLInputElement>) => { const onKeyDownScroll = (event: KeyboardEvent<HTMLInputElement>) => {
const container = popoverRef.current const container = popoverRef.current
if (event.isDefaultPrevented() || !container) return if (event.isDefaultPrevented() || !container) return
@ -24,5 +24,5 @@ export const useKeyboardScroll = () => {
}) })
} }
return { onKeyDown, popoverRef } return { onKeyDownScroll, popoverRef }
} }

@ -30,6 +30,7 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
label, label,
labelLexic, labelLexic,
labelWidth, labelWidth,
maxLength,
title, title,
withArrow, withArrow,
} = props } = props
@ -38,6 +39,7 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
index, index,
inputFieldRef, inputFieldRef,
isOpen, isOpen,
onInputBlur,
onKeyDown, onKeyDown,
onOptionSelect, onOptionSelect,
onOutsideClick, onOutsideClick,
@ -59,6 +61,8 @@ export const Combobox = <T extends Option>(props: Props<T>) => {
{labelLexic ? <T9n t={labelLexic} /> : label} {labelLexic ? <T9n t={labelLexic} /> : label}
</LabelTitle> </LabelTitle>
<InputStyled <InputStyled
maxLength={maxLength}
onBlur={onInputBlur}
onFocus={open} onFocus={open}
ref={inputFieldRef} ref={inputFieldRef}
autoComplete='off' autoComplete='off'

@ -14,9 +14,10 @@ export const PopOver = styled.ul`
max-height: 400px; max-height: 400px;
width: 100%; width: 100%;
top: 55px; top: 55px;
left: 0px; left: -1px;
overflow: auto; overflow: auto;
z-index: 2; z-index: 2;
background: rgb(102, 102, 102);
${customScrollbar} ${customScrollbar}
${customStylesMixin} ${customStylesMixin}
` `

@ -22,6 +22,7 @@ export type Props<T> = Pick<InputHTMLAttributes<HTMLInputElement>, (
label?: string, label?: string,
labelLexic?: string, labelLexic?: string,
labelWidth?: number, labelWidth?: number,
maxLength?: number,
onChange?: (event: ChangeEvent<HTMLInputElement>) => void, onChange?: (event: ChangeEvent<HTMLInputElement>) => void,
onSelect?: (option: T | null) => void, onSelect?: (option: T | null) => void,
options: Array<T>, options: Array<T>,

@ -11,12 +11,14 @@ import {
Label, Label,
LabelTitle, LabelTitle,
Error, Error,
Icon,
Column, Column,
} from './styled' } from './styled'
type Props = { type Props = {
autoComplete?: string, autoComplete?: string,
defaultValue?: string, defaultValue?: string,
editIcon?: boolean,
inputWidth?: number, inputWidth?: number,
label?: string, label?: string,
labelLexic?: string, labelLexic?: string,
@ -35,6 +37,7 @@ type Props = {
export const Input = ({ export const Input = ({
autoComplete = '', autoComplete = '',
defaultValue, defaultValue,
editIcon = false,
error, error,
inputWidth, inputWidth,
label, label,
@ -81,6 +84,7 @@ export const Input = ({
placeholder={translate(labelLexic || '')} placeholder={translate(labelLexic || '')}
/> />
</Label> </Label>
{editIcon && <Icon image='edit-icon' />}
</InputWrapper> </InputWrapper>
<Error t={error || ''} /> <Error t={error || ''} />
</Column> </Column>

@ -174,3 +174,11 @@ export const Error = styled(T9n)<ErrorProps>`
margin-top: 0; margin-top: 0;
} }
` `
export const Icon = styled.div<{ image: string }>`
width: 15px;
height: 25px;
background-image: url(/images/${({ image }) => `${image}.svg`});
background-size: 100%;
background-repeat: no-repeat;
`

@ -3,10 +3,12 @@ import type { FormEvent, FocusEvent } from 'react'
import trim from 'lodash/trim' import trim from 'lodash/trim'
import isEmpty from 'lodash/isEmpty' import isEmpty from 'lodash/isEmpty'
import { formIds } from 'config/form'
import { isValidEmail } from 'helpers/isValidEmail'
import { useAuthStore } from 'features/AuthStore' import { useAuthStore } from 'features/AuthStore'
import { useForm } from 'features/FormStore' import { useForm } from 'features/FormStore'
import { formIds } from 'features/Register/components/RegistrationStep/config'
import { isValidEmail } from 'features/Register/helpers/isValidEmail'
export const useLoginForm = () => { export const useLoginForm = () => {
const { const {

@ -1,12 +1,12 @@
import React from 'react' import React from 'react'
import { PAGES } from 'config' import { PAGES } from 'config'
import { formIds } from 'config/form'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { Logo } from 'features/Logo' import { Logo } from 'features/Logo'
import { Input, ButtonSolid } from 'features/Common' import { Input, ButtonSolid } from 'features/Common'
import { Error } from 'features/Common/Input/styled' import { Error } from 'features/Common/Input/styled'
import { formIds } from 'features/Register/components/RegistrationStep/config'
import { FormStore } from 'features/FormStore' import { FormStore } from 'features/FormStore'
import { useLoginForm } from './hooks' import { useLoginForm } from './hooks'

@ -4,12 +4,13 @@ import trim from 'lodash/trim'
import type { City } from 'requests' import type { City } from 'requests'
import { formIds } from 'config/form'
import { useAuthStore } from 'features/AuthStore' import { useAuthStore } from 'features/AuthStore'
import { useForm } from 'features/FormStore' import { useForm } from 'features/FormStore'
import type { SelectedCountry } from 'hooks/useCountries' import type { SelectedCountry } from 'hooks/useCountries'
import { formIds } from '../config' import { useValidateForm } from 'hooks/useValidateForm'
import { useValidateForm } from './useValidateForm'
type Args = { type Args = {
selectedCity: City | null, selectedCity: City | null,
@ -26,11 +27,6 @@ export const useSubmitHandler = ({
const readTrimmedValue = (fieldName: string) => trim(readFormValue(fieldName)) const readTrimmedValue = (fieldName: string) => trim(readFormValue(fieldName))
const getCityParams = () => {
if (selectedCity) return { cityId: selectedCity.id }
return { city: readTrimmedValue(formIds.city) }
}
const handleSubmit = async (event: FormEvent<HTMLFormElement>) => { const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault() event.preventDefault()
@ -44,11 +40,13 @@ export const useSubmitHandler = ({
const region = readTrimmedValue(formIds.region) const region = readTrimmedValue(formIds.region)
const address1 = readTrimmedValue(formIds.address1) const address1 = readTrimmedValue(formIds.address1)
const address2 = readTrimmedValue(formIds.address2) const address2 = readTrimmedValue(formIds.address2)
const city = readTrimmedValue(formIds.city)
register({ register({
address1, address1,
address2, address2,
...getCityParams(), city,
cityId: selectedCity?.id,
countryId: selectedCountry.id, countryId: selectedCountry.id,
email, email,
firstname, firstname,

@ -1,5 +1,7 @@
import React from 'react' import React from 'react'
import { formIds } from 'config/form'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { Combobox } from 'features/Combobox' import { Combobox } from 'features/Combobox'
import { Input, ButtonSolid } from 'features/Common' import { Input, ButtonSolid } from 'features/Common'
@ -11,8 +13,7 @@ import {
} from 'features/Login/styled' } from 'features/Login/styled'
import { FormStore } from 'features/FormStore' import { FormStore } from 'features/FormStore'
import { formIds } from './config' import { useRegistrationForm } from 'hooks/useForm'
import { useRegistrationForm } from './hooks/useForm'
const labelWidth = 116 const labelWidth = 116
@ -123,7 +124,6 @@ const Registration = () => {
onChange={updateFormValue(formIds.phone)} onChange={updateFormValue(formIds.phone)}
onBlur={onPhoneBlur} onBlur={onPhoneBlur}
/> />
<ButtonsBlock> <ButtonsBlock>
<ButtonSolid type='submit'> <ButtonSolid type='submit'>
<T9n t='next' /> <T9n t='next' />

@ -107,6 +107,7 @@ export const Form = styled.form<{isMatch: boolean}>`
::before { ::before {
content: ''; content: '';
display: block; display: block;
position: absolute;
width: 25px; width: 25px;
height: 25px; height: 25px;
background-image: url(/images/search.svg); background-image: url(/images/search.svg);

@ -6,40 +6,38 @@ import {
import forEach from 'lodash/forEach' import forEach from 'lodash/forEach'
import trim from 'lodash/trim' import trim from 'lodash/trim'
import isString from 'lodash/isString'
import type { SelectedCountry } from 'hooks/useCountries' import { formIds } from 'config/form'
import { useCountries } from 'hooks/useCountries'
import type { UserInfo } from 'requests/saveUserInfo'
import { getUserInfo } from 'requests/getUserInfo' import { getUserInfo } from 'requests/getUserInfo'
import { saveUserInfo } from 'requests/saveUserInfo' import { saveUserInfo } from 'requests/saveUserInfo'
import type { UserInfo } from 'requests/saveUserInfo'
import { useForm } from 'features/FormStore' import type { SelectedCountry } from 'hooks/useCountries'
import { useLexicsStore } from 'features/LexicsStore' import { useRegistrationForm } from 'hooks/useForm'
import { useValidateForm } from './useValidateForm' import { useValidateForm } from './useValidateForm'
import { formIds } from './config'
type Name = 'name_rus' | 'name_eng'
export const useUserInfo = () => { export const useUserInfo = () => {
const {
readFormError,
readFormValue,
updateFormError,
updateFormValue,
} = useForm()
const { suffix } = useLexicsStore()
const validateForm = useValidateForm() const validateForm = useValidateForm()
const saveButton = useRef<HTMLButtonElement>(null) const saveButton = useRef<HTMLButtonElement>(null)
const { const {
cities,
countries, countries,
setSelectedCountry, getCities,
} = useCountries() onCitySelect,
onCountrySelect,
onPhoneBlur,
onRegionOrCityChange,
readFormError,
readFormValue,
resetCities,
resetSelectedCity,
selectedCity,
updateFormError,
updateFormValue,
} = useRegistrationForm()
const readTrimmedValue = useCallback( const readTrimmedValue = useCallback(
(fieldName: string) => trim(readFormValue(fieldName)), (fieldName: string) => trim(readFormValue(fieldName)),
@ -54,17 +52,16 @@ export const useUserInfo = () => {
const password = readTrimmedValue(formIds.password) const password = readTrimmedValue(formIds.password)
const postalCode = Number(readTrimmedValue(formIds.postalCode)) const postalCode = Number(readTrimmedValue(formIds.postalCode))
const region = readTrimmedValue(formIds.region) const region = readTrimmedValue(formIds.region)
const address_line1 = readTrimmedValue(formIds.address_line1) const address_line1 = readTrimmedValue(formIds.address1)
const address_line2 = readTrimmedValue(formIds.address_line2) const address_line2 = readTrimmedValue(formIds.address2)
const city = readTrimmedValue(formIds.city) const city = readTrimmedValue(formIds.city)
const city_id = Number(readTrimmedValue(formIds.city_id))
const countryId = Number(readTrimmedValue(formIds.countryId)) const countryId = Number(readTrimmedValue(formIds.countryId))
saveUserInfo({ saveUserInfo({
address_line1, address_line1,
address_line2, address_line2,
city, city,
city_id, cityId: selectedCity?.id || null,
countryId, countryId,
firstname, firstname,
lastname, lastname,
@ -77,40 +74,36 @@ export const useUserInfo = () => {
}, [ }, [
readTrimmedValue, readTrimmedValue,
validateForm, validateForm,
]) selectedCity,
const onCountrySelect = useCallback((country: SelectedCountry) => {
setSelectedCountry(country)
const countryName = country ? country[`name_${suffix}` as Name] : ''
updateFormValue(formIds.country)(countryName)
// TO DO пока не решили сбрасываем ли город и адрес
// updateFormValue(formIds.region)('')
updateFormValue(formIds.countryId)(`${country?.id}`)
}, [
setSelectedCountry,
updateFormValue,
suffix,
]) ])
useEffect(() => { useEffect(() => {
getUserInfo().then((res: UserInfo) => { getUserInfo().then((res: UserInfo) => {
forEach(res, (value: string | number | SelectedCountry, key: string) => { forEach(res, (value: string | number | SelectedCountry, key: string) => {
if (value && typeof value === 'object') { if (value && typeof value === 'object') {
onCountrySelect(value) onCountrySelect(value, false)
} else if (isString(value)) { } else if (value) {
updateFormValue(key)(value) updateFormValue(key)(String(value))
} }
}) })
}) })
}, [updateFormValue, onCountrySelect]) }, [updateFormValue, onCountrySelect])
return { return {
cities,
countries, countries,
getCities,
handleSubmit, handleSubmit,
onCitySelect,
onCountrySelect, onCountrySelect,
onPhoneBlur,
onRegionOrCityChange,
readFormError, readFormError,
readFormValue, readFormValue,
resetCities,
resetSelectedCity,
saveButton, saveButton,
selectedCity,
updateFormError, updateFormError,
updateFormValue, updateFormValue,
} }

@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import { userAccountLexics } from 'config/lexics/userAccount' import { userAccountLexics } from 'config/lexics/userAccount'
import { formIds } from 'config/form'
import { Background } from 'features/Background' import { Background } from 'features/Background'
import { Combobox } from 'features/Combobox' import { Combobox } from 'features/Combobox'
@ -9,11 +10,11 @@ import { Form } from 'features/Login/styled'
import { T9n } from 'features/T9n' import { T9n } from 'features/T9n'
import { Error } from 'features/Common/Input/styled' import { Error } from 'features/Common/Input/styled'
import { useLexicsStore, useLexicsConfig } from 'features/LexicsStore' import { useLexicsStore, useLexicsConfig } from 'features/LexicsStore'
import { CardNumber } from './CardNumber' import { CardNumber } from './CardNumber'
import { UserAccountButton } from './UserAccountButton' import { UserAccountButton } from './UserAccountButton'
import { PageTitle } from './PageTitle' import { PageTitle } from './PageTitle'
import { UserAccountSubscription } from './UserAccountSubscription' import { UserAccountSubscription } from './UserAccountSubscription'
import { formIds } from './config'
import { TextNoBorder } from './TextNoBorder' import { TextNoBorder } from './TextNoBorder'
import { useUserInfo } from './hooks' import { useUserInfo } from './hooks'
@ -27,7 +28,7 @@ import {
ButtonWrapper, ButtonWrapper,
} from './styled' } from './styled'
const labelWidth = 78 const labelWidth = 110
export const UserAccount = () => { export const UserAccount = () => {
useLexicsConfig(userAccountLexics) useLexicsConfig(userAccountLexics)
@ -35,9 +36,13 @@ export const UserAccount = () => {
const { translate } = useLexicsStore() const { translate } = useLexicsStore()
const { const {
cities,
countries, countries,
handleSubmit, handleSubmit,
onCitySelect,
onCountrySelect, onCountrySelect,
onPhoneBlur,
onRegionOrCityChange,
readFormError, readFormError,
readFormValue, readFormValue,
saveButton, saveButton,
@ -61,6 +66,8 @@ export const UserAccount = () => {
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={updateFormValue(formIds.firstname)} onChange={updateFormValue(formIds.firstname)}
error={readFormError(formIds.firstname)} error={readFormError(formIds.firstname)}
editIcon
maxLength={500}
/> />
<Input <Input
value={readFormValue(formIds.lastname)} value={readFormValue(formIds.lastname)}
@ -68,126 +75,94 @@ export const UserAccount = () => {
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={updateFormValue(formIds.lastname)} onChange={updateFormValue(formIds.lastname)}
error={readFormError(formIds.lastname)} error={readFormError(formIds.lastname)}
editIcon
maxLength={500}
/> />
<Input <Input
onBlur={onPhoneBlur}
value={readFormValue(formIds.phone)} value={readFormValue(formIds.phone)}
labelLexic='phone' labelLexic='phone'
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={updateFormValue(formIds.phone)} onChange={updateFormValue(formIds.phone)}
error={readFormError(formIds.phone)} error={readFormError(formIds.phone)}
editIcon
maxLength={100}
/>
<Input
value={readFormValue(formIds.password)}
error={readFormError(formIds.password)}
type='password'
labelLexic='form_password'
labelWidth={labelWidth}
onChange={updateFormValue(formIds.password)}
editIcon
maxLength={500}
/> />
<Combobox <Combobox
value={readFormValue(formIds.country)}
error={readFormError(formIds.country)}
labelLexic='form_country' labelLexic='form_country'
options={countries}
labelWidth={labelWidth} labelWidth={labelWidth}
withArrow
onChange={updateFormValue(formIds.country)} onChange={updateFormValue(formIds.country)}
value={readFormValue(formIds.country)} options={countries}
error={readFormError(formIds.country)}
onSelect={onCountrySelect} onSelect={onCountrySelect}
withArrow
/> />
<ButtonWrapper>
<OutlinedButton
type='button'
onClick={handleSubmit}
ref={saveButton}
>
<T9n t='save_changes' />
</OutlinedButton>
<Error t={readFormError(formIds.formError) || ''} />
</ButtonWrapper>
</Form>
</FormWrapper>
<FormWrapper>
<Form>
<UserAccountBlockTitle>
<T9n t='payment' />
</UserAccountBlockTitle>
<CardNumber visa label='1234 1234 1234 1234' />
<CardNumber checked label='1234 1234 1234 1234' />
<CardNumber label='1234 1234 1234 1234' />
<UserAccountButton text={translate('add_card')} />
</Form>
</FormWrapper>
<FormWrapper>
<Form>
<UserAccountBlockTitle>
<T9n t='subscriptions' />
</UserAccountBlockTitle>
<UserAccountSubscription
amount={1999}
checked
inputType='radio'
packageName='Базовая'
packageAction={translate('change')}
/>
<UserAccountSubscription
amount={1999}
checked
inputType='checkbox'
label='Российская Премьер-Лига'
/>
<UserAccountSubscription
amount={499}
checked
inputType='checkbox'
label='Primera División'
/>
<UserAccountSubscription
amount={299}
checked
inputType='checkbox'
label='Manchester United'
/>
<UserAccountButton text={translate('select_subscription')} />
<TextNoBorder
text={`${translate('next_debit')} 31.02.2020`}
amount={4796}
/>
</Form>
</FormWrapper>
</UserAccountFormWrapper>
</UserAccountWrapper>
</Background>
<Background>
<UserAccountWrapper>
<PageTitle titleText={translate('user_account')} />
<UserAccountFormWrapper>
<FormWrapper>
<Form>
<UserAccountBlockTitle>
<T9n t='main' />
</UserAccountBlockTitle>
<Input <Input
labelLexic='name' value={readFormValue(formIds.postalCode)}
error={readFormError(formIds.postalCode)}
labelLexic='form_postal_code'
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={updateFormValue(formIds.postalCode)}
editIcon
/> />
<Input <Input
labelLexic='lastname' value={readFormValue(formIds.region)}
error={readFormError(formIds.region)}
labelLexic='form_region'
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={onRegionOrCityChange(formIds.region)}
editIcon
maxLength={500}
/> />
<Input <Combobox
labelLexic='phone' value={readFormValue(formIds.city)}
error={readFormError(formIds.city)}
labelLexic='form_city'
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={onRegionOrCityChange(formIds.city)}
options={cities}
onSelect={onCitySelect}
maxLength={500}
/> />
<Input <Input
type='email' value={readFormValue(formIds.address1)}
labelLexic='mail' error={readFormError(formIds.address1)}
labelLexic='form_address1'
labelWidth={labelWidth} labelWidth={labelWidth}
onChange={updateFormValue(formIds.address1)}
editIcon
maxLength={500}
/> />
<Combobox <Input
labelLexic='form_country' value={readFormValue(formIds.address2)}
options={countries} error={readFormError(formIds.address2)}
labelLexic='form_address2'
labelWidth={labelWidth} labelWidth={labelWidth}
withArrow onChange={updateFormValue(formIds.address2)}
onChange={updateFormValue(formIds.country)} editIcon
value={readFormValue(formIds.country)} maxLength={500}
error={readFormError(formIds.country)}
onSelect={onCountrySelect}
/> />
<OutlinedButton> <ButtonWrapper>
<T9n t='save_changes' /> <OutlinedButton
</OutlinedButton> type='button'
onClick={handleSubmit}
ref={saveButton}
>
<T9n t='save_changes' />
</OutlinedButton>
<Error t={readFormError(formIds.formError) || ''} />
</ButtonWrapper>
</Form> </Form>
</FormWrapper> </FormWrapper>
<FormWrapper> <FormWrapper>

@ -8,9 +8,11 @@ export const OutlinedButton = styled.button`
width: 288px; width: 288px;
margin-top: 20px; margin-top: 20px;
align-self: flex-start; align-self: flex-start;
color: #005EDD; color: white;
border-color: #005EDD; font-weight: bold;
border-color: #0033CC;
background-color: #0033CC;
&:hover { &:hover {
cursor: pointer; cursor: pointer;
} }

@ -2,13 +2,14 @@ import trim from 'lodash/trim'
import reduce from 'lodash/reduce' import reduce from 'lodash/reduce'
import { useForm } from 'features/FormStore' import { useForm } from 'features/FormStore'
import { isValidPhone } from 'features/Register/helpers/isValidPhone' import { isValidPhone } from 'helpers/isValidPhone'
import { isValidPassword } from 'helpers/isValidPassword'
import { import {
formIds, formIds,
requiredFields, requiredFields,
simpleValidationFields, simpleValidationFields,
} from './config' } from 'config/form'
export const useValidateForm = () => { export const useValidateForm = () => {
const { const {
@ -37,6 +38,7 @@ export const useValidateForm = () => {
const validateForm = () => { const validateForm = () => {
let hasError = false let hasError = false
const phone = readTrimmedValue(formIds.phone) const phone = readTrimmedValue(formIds.phone)
const password = readTrimmedValue(formIds.password)
if (allFieldsEmpty(requiredFields)) { if (allFieldsEmpty(requiredFields)) {
updateFormError(formIds.formError, 'error_fill_out_required_fields') updateFormError(formIds.formError, 'error_fill_out_required_fields')
@ -46,6 +48,11 @@ export const useValidateForm = () => {
hasError = setErrorOnEmptyFields(simpleValidationFields, 'error_fill_out_this_field') hasError = setErrorOnEmptyFields(simpleValidationFields, 'error_fill_out_this_field')
if (!isValidPassword(password)) {
updateFormError(formIds.password, 'error_simple_password')
hasError = true
}
if (!isValidPhone(phone)) { if (!isValidPhone(phone)) {
updateFormError(formIds.phone, 'error_invalid_phone_format') updateFormError(formIds.phone, 'error_invalid_phone_format')
hasError = true hasError = true

@ -7,23 +7,24 @@ import debounce from 'lodash/debounce'
import type { Cities, City } from 'requests' import type { Cities, City } from 'requests'
import { getCountryCities } from 'requests' import { getCountryCities } from 'requests'
import { useForm } from 'features/FormStore'
import { formIds } from '../config' import { formIds } from 'config/form'
import { useForm } from 'features/FormStore'
const useCitiesList = () => { const useCitiesList = () => {
const [cities, setCities] = useState<Cities>([]) const [cities, setCities] = useState<Cities>([])
const getCities = (city: string, selectedCountryId: number) => { const getCities = useCallback((city: string, selectedCountryId: number) => {
getCountryCities(city, selectedCountryId).then(setCities) getCountryCities(city, selectedCountryId).then(setCities)
} }, [])
const getCitiesDebounced = useCallback( const getCitiesDebounced = useCallback(
debounce(getCities, 300), debounce(getCities, 500),
[], [],
) )
const resetCities = () => setCities([]) const resetCities = useCallback(() => setCities([]), [])
return { return {
cities, cities,
@ -44,19 +45,19 @@ export const useCities = () => {
resetCities, resetCities,
} = useCitiesList() } = useCitiesList()
const onCitySelect = (newCity: City | null) => { const onCitySelect = useCallback((newCity: City | null) => {
if (newCity) { if (newCity) {
setCityQuery(newCity.name) setCityQuery(newCity.name)
setSelectedCity(newCity) setSelectedCity(newCity)
} else { } else {
setSelectedCity(null) setSelectedCity(null)
} }
} }, [setCityQuery])
const resetSelectedCity = () => { const resetSelectedCity = useCallback(() => {
setCityQuery('') setCityQuery('')
setSelectedCity(null) setSelectedCity(null)
} }, [setCityQuery])
return { return {
cities, cities,

@ -1,18 +1,26 @@
import type { ChangeEvent, FocusEvent } from 'react' import type { ChangeEvent, FocusEvent } from 'react'
import { useEffect } from 'react' import {
useEffect,
useCallback,
} from 'react'
import trim from 'lodash/trim' import trim from 'lodash/trim'
import { useForm } from 'features/FormStore' import { isValidEmail } from 'helpers/isValidEmail'
import { isValidEmail } from 'features/Register/helpers/isValidEmail' import { isValidPhone } from 'helpers/isValidPhone'
import { isValidPhone } from 'features/Register/helpers/isValidPhone' import { formatPhoneCode } from 'helpers/formatPhoneCode'
import { formatPhoneCode } from 'features/Register/helpers/formatPhoneCode'
import type { SelectedCountry } from 'hooks/useCountries' import type { SelectedCountry } from 'hooks/useCountries'
import { useCountries } from 'hooks/useCountries' import { useCountries } from 'hooks/useCountries'
import { formIds } from '../config' import { useCities } from 'hooks/useCities'
import { useCities } from './useCities'
import { useSubmitHandler } from './useSubmitHandler' import { formIds } from 'config/form'
import { useForm } from 'features/FormStore'
import { useSubmitHandler } from 'features/Register/components/RegistrationStep/hooks/useSubmitHandler'
import { useLexicsStore } from 'features/LexicsStore'
type Name = 'name_rus' | 'name_eng'
export const useRegistrationForm = () => { export const useRegistrationForm = () => {
const { const {
@ -41,33 +49,50 @@ export const useRegistrationForm = () => {
selectedCity, selectedCity,
selectedCountry, selectedCountry,
}) })
const { suffix } = useLexicsStore()
const onEmailBlur = ({ target }: FocusEvent<HTMLInputElement>) => { const onEmailBlur = useCallback(({ target }: FocusEvent<HTMLInputElement>) => {
const email = trim(target.value) const email = trim(target.value)
if (email && !isValidEmail(email)) { if (email && !isValidEmail(email)) {
updateFormError(formIds.email, 'error_invalid_email_format') updateFormError(formIds.email, 'error_invalid_email_format')
} }
} }, [updateFormError])
const onPhoneBlur = ({ target }: ChangeEvent<HTMLInputElement>) => { const onPhoneBlur = useCallback(({ target }: ChangeEvent<HTMLInputElement>) => {
const phone = target.value const phone = target.value
if (phone && !isValidPhone(phone)) { if (phone && !isValidPhone(phone)) {
updateFormError(formIds.phone, 'error_invalid_phone_format') updateFormError(formIds.phone, 'error_invalid_phone_format')
} }
} }, [updateFormError])
const onCountrySelect = (country: SelectedCountry) => { const onCountrySelect = useCallback((
country: SelectedCountry,
updatePhoneCode: boolean = true,
) => {
setSelectedCountry(country) setSelectedCountry(country)
updateFormValue(formIds.country)(country?.name || '') const countryName = country ? country[`name_${suffix}` as Name] : ''
updateFormValue(formIds.country)(countryName)
updateFormValue(formIds.cityId)('')
updateFormValue(formIds.countryId)(`${country?.id}`)
resetCities() resetCities()
resetSelectedCity() resetSelectedCity()
const code = country?.phone_code
? formatPhoneCode(country.phone_code)
: ''
updateFormValue(formIds.phone)(code)
}
const onRegionOrCityChange = (fieldName: string) => ( if (updatePhoneCode) {
const code = country?.phone_code
? formatPhoneCode(country.phone_code)
: ''
updateFormValue(formIds.phone)(code)
}
}, [
resetCities,
resetSelectedCity,
setSelectedCountry,
updateFormValue,
suffix,
])
const onRegionOrCityChange = useCallback((fieldName: string) => (
({ target }: ChangeEvent<HTMLInputElement>) => { ({ target }: ChangeEvent<HTMLInputElement>) => {
if (selectedCountry) { if (selectedCountry) {
updateFormValue(fieldName)(target.value) updateFormValue(fieldName)(target.value)
@ -75,7 +100,11 @@ export const useRegistrationForm = () => {
updateFormError(formIds.country, 'error_select_country_first') updateFormError(formIds.country, 'error_select_country_first')
} }
} }
) ), [
selectedCountry,
updateFormError,
updateFormValue,
])
const trimmedCity = trim(readFormValue(formIds.city)) const trimmedCity = trim(readFormValue(formIds.city))
useEffect(() => { useEffect(() => {
@ -91,6 +120,7 @@ export const useRegistrationForm = () => {
return { return {
cities, cities,
countries, countries,
getCities,
handleSubmit, handleSubmit,
onCitySelect, onCitySelect,
onCountrySelect, onCountrySelect,
@ -99,6 +129,10 @@ export const useRegistrationForm = () => {
onRegionOrCityChange, onRegionOrCityChange,
readFormError, readFormError,
readFormValue, readFormValue,
resetCities,
resetSelectedCity,
selectedCity,
updateFormError,
updateFormValue, updateFormValue,
} }
} }

@ -2,15 +2,15 @@ import trim from 'lodash/trim'
import reduce from 'lodash/reduce' import reduce from 'lodash/reduce'
import { useForm } from 'features/FormStore' import { useForm } from 'features/FormStore'
import { isValidEmail } from 'features/Register/helpers/isValidEmail' import { isValidEmail } from 'helpers/isValidEmail'
import { isValidPassword } from 'features/Register/helpers/isValidPassword' import { isValidPassword } from 'helpers/isValidPassword'
import { isValidPhone } from 'features/Register/helpers/isValidPhone' import { isValidPhone } from 'helpers/isValidPhone'
import { import {
formIds, formIds,
requiredFields, requiredFields,
simpleValidationFields, simpleValidationFields,
} from '../config' } from 'config/form'
export const useValidateForm = () => { export const useValidateForm = () => {
const { const {

@ -10,7 +10,7 @@ export type UserInfo = {
address_line1: string, address_line1: string,
address_line2: string, address_line2: string,
city: string, city: string,
city_id: number, cityId: number | null,
countryId: number, countryId: number,
firstname: string, firstname: string,
lastname: string, lastname: string,
@ -34,7 +34,7 @@ export const saveUserInfo = async ({
address_line1, address_line1,
address_line2, address_line2,
city, city,
city_id, cityId,
countryId, countryId,
firstname, firstname,
lastname, lastname,
@ -49,7 +49,7 @@ export const saveUserInfo = async ({
_p_address_line1: address_line1, _p_address_line1: address_line1,
_p_address_line2: address_line2, _p_address_line2: address_line2,
_p_city: city, _p_city: city,
_p_city_id: city_id, _p_city_id: cityId,
_p_country_id: countryId, _p_country_id: countryId,
_p_firstname: firstname, _p_firstname: firstname,
_p_lastname: lastname, _p_lastname: lastname,

Loading…
Cancel
Save