mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 02:09:08 +03:00
refactor: profile page
This commit is contained in:
parent
f3dc0f14c5
commit
4069ac5b45
@ -1,10 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { Switch, SwitchProps, useColorMode } from '@chakra-ui/react';
|
|
||||||
|
|
||||||
const SwitchDarkThemeButton: React.FC<SwitchProps> = props => {
|
|
||||||
const { colorMode, toggleColorMode } = useColorMode();
|
|
||||||
|
|
||||||
return <Switch onChange={toggleColorMode} size='lg' isChecked={colorMode === 'dark'} {...props} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export { SwitchDarkThemeButton };
|
|
||||||
196
src/components/UserProfile/Settings/Fields/index.tsx
Normal file
196
src/components/UserProfile/Settings/Fields/index.tsx
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
FormControl,
|
||||||
|
FormHelperText,
|
||||||
|
FormLabel,
|
||||||
|
Input,
|
||||||
|
InputGroup,
|
||||||
|
InputRightElement,
|
||||||
|
InputProps,
|
||||||
|
Button,
|
||||||
|
Textarea,
|
||||||
|
Text,
|
||||||
|
TextareaProps,
|
||||||
|
Select,
|
||||||
|
SelectProps,
|
||||||
|
Switch,
|
||||||
|
SwitchProps,
|
||||||
|
ButtonProps,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { SaveButton } from '@educt/components/Buttons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useColorMode } from '@chakra-ui/react';
|
||||||
|
import { useSettingsContext } from '../context';
|
||||||
|
import { useHistory } from 'react-router-dom';
|
||||||
|
|
||||||
|
const InputWithButton: React.FC<
|
||||||
|
InputProps & { buttonText?: string | undefined; buttonHandler?: () => void }
|
||||||
|
> = props => {
|
||||||
|
const { buttonText, buttonHandler, ...inputProps } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InputGroup size='md'>
|
||||||
|
<Input {...inputProps} />
|
||||||
|
<InputRightElement width='4.5rem' pr='0.5rem'>
|
||||||
|
<Button onClick={buttonHandler} h='1.75rem' size='sm'>
|
||||||
|
{buttonText || 'Click'}
|
||||||
|
</Button>
|
||||||
|
</InputRightElement>
|
||||||
|
</InputGroup>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FullnameInput: React.FC<InputProps> = props => (
|
||||||
|
<FormControl isReadOnly>
|
||||||
|
<FormLabel>Fullname</FormLabel>
|
||||||
|
<Input {...props} />
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const EmailInput: React.FC<InputProps> = props => {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const onChangeEmail = () => history.push(`/profile/change-email`);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl isReadOnly>
|
||||||
|
<FormLabel>Email</FormLabel>
|
||||||
|
<InputWithButton buttonHandler={onChangeEmail} buttonText='Change' {...props} />
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AboutInput: React.FC<TextareaProps> = props => {
|
||||||
|
const {
|
||||||
|
form: {
|
||||||
|
register,
|
||||||
|
formState: { errors },
|
||||||
|
},
|
||||||
|
} = useSettingsContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>About</FormLabel>
|
||||||
|
<Textarea minH='130px' maxH='500px' isInvalid={!!errors.about} {...register('about')} {...props} />
|
||||||
|
<Text as='small' color='red.500'>
|
||||||
|
{errors.about?.message}
|
||||||
|
</Text>
|
||||||
|
<FormHelperText>Write description for your profile.</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PhoneInput: React.FC<InputProps> = props => {
|
||||||
|
const {
|
||||||
|
form: {
|
||||||
|
register,
|
||||||
|
formState: { errors },
|
||||||
|
},
|
||||||
|
} = useSettingsContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>Phone</FormLabel>
|
||||||
|
<Input
|
||||||
|
placeholder='+1 (234) 555-1234'
|
||||||
|
isInvalid={!!errors.phone_number}
|
||||||
|
{...register('phone_number')}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
<Text as='small' color='red.500'>
|
||||||
|
{errors.phone_number?.message}
|
||||||
|
</Text>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TwitterInput: React.FC<InputProps> = props => {
|
||||||
|
const {
|
||||||
|
form: {
|
||||||
|
register,
|
||||||
|
formState: { errors },
|
||||||
|
},
|
||||||
|
} = useSettingsContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>Twitter</FormLabel>
|
||||||
|
<Input placeholder='@username' isInvalid={!!errors.twitter_id} {...register('twitter_id')} {...props} />
|
||||||
|
<Text as='small' color='red.500'>
|
||||||
|
{errors.twitter_id?.message}
|
||||||
|
</Text>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TelegramInput: React.FC<InputProps> = props => {
|
||||||
|
const {
|
||||||
|
form: {
|
||||||
|
register,
|
||||||
|
formState: { errors },
|
||||||
|
},
|
||||||
|
} = useSettingsContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>Telegram</FormLabel>
|
||||||
|
<Input placeholder='@username' isInvalid={!!errors.telegram_id} {...register('telegram_id')} {...props} />
|
||||||
|
<Text as='small' color='red.500'>
|
||||||
|
{errors.telegram_id?.message}
|
||||||
|
</Text>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PasswordInput: React.FC<InputProps> = props => {
|
||||||
|
const history = useHistory();
|
||||||
|
|
||||||
|
const onChangePassword = () => history.push(`/profile/change-password`);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormControl isReadOnly>
|
||||||
|
<FormLabel>Password</FormLabel>
|
||||||
|
<InputWithButton buttonHandler={onChangePassword} buttonText='Change' {...props} />
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LanguageSelect: React.FC<SelectProps> = props => {
|
||||||
|
return (
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>Display Language</FormLabel>
|
||||||
|
<Select {...props}>
|
||||||
|
<option value='en'>English</option>
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ThemeSwitcher: React.FC<SwitchProps> = props => {
|
||||||
|
const { colorMode, toggleColorMode } = useColorMode();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Switch onChange={toggleColorMode} size='lg' isChecked={colorMode === 'dark'} {...props} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SaveSettingsButton: React.FC<ButtonProps> = props => {
|
||||||
|
const {
|
||||||
|
form: {
|
||||||
|
formState: { isDirty },
|
||||||
|
},
|
||||||
|
isLoading,
|
||||||
|
} = useSettingsContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<SaveButton type='submit' isLoading={isLoading} isDisabled={!isDirty} {...props} />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -1,31 +1,82 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, BoxProps, Heading as ChakraHeading, Divider, Stack, StackDivider } from '@chakra-ui/react';
|
import {
|
||||||
|
Box,
|
||||||
|
BoxProps,
|
||||||
|
Heading as ChakraHeading,
|
||||||
|
Divider,
|
||||||
|
InputProps,
|
||||||
|
TextareaProps,
|
||||||
|
Stack,
|
||||||
|
StackDivider,
|
||||||
|
SelectProps,
|
||||||
|
ButtonProps,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
import { Title, ITitleProps } from './Title';
|
import { Title, ITitleProps } from './Title';
|
||||||
import { Entry } from './Entry';
|
import { Entry } from './Entry';
|
||||||
import { EntryContent } from './EntryContent';
|
import { EntryContent } from './EntryContent';
|
||||||
|
import { SettingsContextProvider } from './SettingsContextProvider';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Form input fields
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
FullnameInput,
|
||||||
|
EmailInput,
|
||||||
|
AboutInput,
|
||||||
|
PhoneInput,
|
||||||
|
TwitterInput,
|
||||||
|
TelegramInput,
|
||||||
|
PasswordInput,
|
||||||
|
LanguageSelect,
|
||||||
|
ThemeSwitcher,
|
||||||
|
SaveSettingsButton,
|
||||||
|
} from './Fields';
|
||||||
|
import { SwitchProps } from 'react-router-dom';
|
||||||
|
|
||||||
export interface ISettingsComposition {
|
export interface ISettingsComposition {
|
||||||
Title: React.FC<ITitleProps & BoxProps>;
|
Title: React.FC<ITitleProps & BoxProps>;
|
||||||
Entry: React.FC;
|
Entry: React.FC;
|
||||||
EntryContent: React.FC;
|
EntryContent: React.FC;
|
||||||
|
FullnameInput: React.FC<InputProps>;
|
||||||
|
EmailInput: React.FC<InputProps>;
|
||||||
|
AboutInput: React.FC<TextareaProps>;
|
||||||
|
PhoneInput: React.FC<InputProps>;
|
||||||
|
TwitterInput: React.FC<InputProps>;
|
||||||
|
TelegramInput: React.FC<InputProps>;
|
||||||
|
PasswordInput: React.FC<InputProps>;
|
||||||
|
LanguageSelect: React.FC<SelectProps>;
|
||||||
|
ThemeSwitcher: React.FC<SwitchProps>;
|
||||||
|
SaveSettingsButton: React.FC<ButtonProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Settings: React.FC<BoxProps> & ISettingsComposition = props => {
|
const Settings: React.FC<BoxProps> & ISettingsComposition = props => {
|
||||||
return (
|
return (
|
||||||
<Box maxW={{ base: 'full', lg: '750px' }} {...props}>
|
<SettingsContextProvider>
|
||||||
<ChakraHeading as='h2' mt='12' fontSize={'2xl'} fontWeight={'bold'}>
|
<Box maxW={{ base: 'full', lg: '750px' }} {...props}>
|
||||||
Account Settings
|
<ChakraHeading as='h2' mt='12' fontSize={'2xl'} fontWeight={'bold'}>
|
||||||
</ChakraHeading>
|
Account Settings
|
||||||
<Divider my='5' />
|
</ChakraHeading>
|
||||||
<Stack divider={<StackDivider />} spacing={'5'}>
|
<Divider my='5' />
|
||||||
{props.children}
|
<Stack divider={<StackDivider />} spacing={'5'}>
|
||||||
</Stack>
|
{props.children}
|
||||||
</Box>
|
</Stack>
|
||||||
|
</Box>
|
||||||
|
</SettingsContextProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Settings.Title = Title;
|
Settings.Title = Title;
|
||||||
Settings.Entry = Entry;
|
Settings.Entry = Entry;
|
||||||
Settings.EntryContent = EntryContent;
|
Settings.EntryContent = EntryContent;
|
||||||
|
Settings.FullnameInput = FullnameInput;
|
||||||
|
Settings.EmailInput = EmailInput;
|
||||||
|
Settings.AboutInput = AboutInput;
|
||||||
|
Settings.PhoneInput = PhoneInput;
|
||||||
|
Settings.TwitterInput = TwitterInput;
|
||||||
|
Settings.TelegramInput = TelegramInput;
|
||||||
|
Settings.PasswordInput = PasswordInput;
|
||||||
|
Settings.LanguageSelect = LanguageSelect;
|
||||||
|
Settings.ThemeSwitcher = ThemeSwitcher;
|
||||||
|
Settings.SaveSettingsButton = SaveSettingsButton;
|
||||||
|
|
||||||
export { Settings };
|
export { Settings };
|
||||||
|
|||||||
26
src/components/UserProfile/Settings/Settings.validator.ts
Normal file
26
src/components/UserProfile/Settings/Settings.validator.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import yup from '@educt/schema';
|
||||||
|
|
||||||
|
const SettingsSchema = yup.object().shape({
|
||||||
|
about: yup
|
||||||
|
.string()
|
||||||
|
.max(250, 'Description must be at most 250 characters')
|
||||||
|
.transform(val => (!val ? null : val))
|
||||||
|
.nullable(true),
|
||||||
|
phone_number: yup
|
||||||
|
.string()
|
||||||
|
.isMobileValid(['en-US', 'ru-RU'], { strictMode: true })
|
||||||
|
.transform(val => (!val ? null : val))
|
||||||
|
.nullable(true),
|
||||||
|
twitter_id: yup
|
||||||
|
.string()
|
||||||
|
.matches(/(^|[^@\w])@(\w{1,15})\b/, 'Twitter username is not valid')
|
||||||
|
.transform(val => (!val ? null : val))
|
||||||
|
.nullable(true),
|
||||||
|
telegram_id: yup
|
||||||
|
.string()
|
||||||
|
.matches(/(^|[^@\w])@(\w{1,64})\b/, 'Telegram username is not valid')
|
||||||
|
.transform(val => (!val ? null : val))
|
||||||
|
.nullable(true),
|
||||||
|
});
|
||||||
|
|
||||||
|
export { SettingsSchema };
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { runInAction } from 'mobx';
|
||||||
|
import * as helpers from '@educt/helpers';
|
||||||
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
|
import { FormTypes, SettingsContext } from './context';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
import { useUpdateUserInfo } from '@educt/hooks/queries';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validator
|
||||||
|
*/
|
||||||
|
import { SettingsSchema } from './Settings.validator';
|
||||||
|
|
||||||
|
const SettingsContextProvider: React.FC = ({ children }) => {
|
||||||
|
const {
|
||||||
|
userStore: { me },
|
||||||
|
} = useRootStore();
|
||||||
|
const form = useForm<FormTypes>({
|
||||||
|
resolver: yupResolver(SettingsSchema),
|
||||||
|
defaultValues: {
|
||||||
|
about: me?.about || '',
|
||||||
|
phone_number: me?.contacts?.phone_number || '',
|
||||||
|
twitter_id: me?.contacts?.twitter_id || '',
|
||||||
|
telegram_id: me?.contacts?.telegram_id || '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { updateInfo, isLoading } = useUpdateUserInfo();
|
||||||
|
const {
|
||||||
|
formState: { dirtyFields },
|
||||||
|
reset,
|
||||||
|
} = form;
|
||||||
|
|
||||||
|
const onSubmit = async (data: FormTypes) => {
|
||||||
|
try {
|
||||||
|
const updatedFields = helpers.getDirtyFields(dirtyFields, data);
|
||||||
|
|
||||||
|
const { about, phone_number, telegram_id, twitter_id } = await updateInfo(updatedFields);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update info in store
|
||||||
|
*/
|
||||||
|
runInAction(() => {
|
||||||
|
if (me !== null) {
|
||||||
|
me.about = about === undefined ? me.about : about;
|
||||||
|
if (me.contacts) {
|
||||||
|
me.contacts.phone_number = phone_number === undefined ? me.contacts.phone_number : phone_number;
|
||||||
|
me.contacts.twitter_id = twitter_id === undefined ? me.contacts.twitter_id : twitter_id;
|
||||||
|
me.contacts.telegram_id = telegram_id === undefined ? me.contacts.telegram_id : telegram_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
reset(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingsContext.Provider value={{ form, isLoading }}>
|
||||||
|
<form onSubmit={form.handleSubmit(onSubmit)}>{children}</form>
|
||||||
|
</SettingsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { SettingsContextProvider };
|
||||||
26
src/components/UserProfile/Settings/context.ts
Normal file
26
src/components/UserProfile/Settings/context.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
import { UseFormReturn } from 'react-hook-form';
|
||||||
|
|
||||||
|
interface ISettingsContext {
|
||||||
|
form: UseFormReturn<FormTypes, object>;
|
||||||
|
isLoading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type FormTypes = {
|
||||||
|
about: string;
|
||||||
|
phone_number: string;
|
||||||
|
twitter_id: string;
|
||||||
|
telegram_id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SettingsContext = createContext<ISettingsContext | undefined>(undefined);
|
||||||
|
|
||||||
|
export const useSettingsContext = () => {
|
||||||
|
const context = useContext(SettingsContext);
|
||||||
|
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('SettingsContext was outside of its provider');
|
||||||
|
}
|
||||||
|
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@ -1,22 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { runInAction } from 'mobx';
|
import { Stack } from '@chakra-ui/layout';
|
||||||
import yup from '@educt/schema';
|
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
|
||||||
import * as helpers from '@educt/helpers';
|
|
||||||
import {
|
|
||||||
InputGroup,
|
|
||||||
InputRightElement,
|
|
||||||
FormControl,
|
|
||||||
FormLabel,
|
|
||||||
Input,
|
|
||||||
Textarea,
|
|
||||||
Text,
|
|
||||||
FormHelperText,
|
|
||||||
Select,
|
|
||||||
Button,
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import { Box, Stack } from '@chakra-ui/layout';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
@ -28,47 +12,12 @@ import type { IPageProps } from '@educt/interfaces';
|
|||||||
*/
|
*/
|
||||||
import { Page } from '@educt/components/PageElements';
|
import { Page } from '@educt/components/PageElements';
|
||||||
import { UserProfile } from '@educt/components/UserProfile';
|
import { UserProfile } from '@educt/components/UserProfile';
|
||||||
import { SwitchDarkThemeButton } from '@educt/components/SwitchDarkThemeButton';
|
|
||||||
import { SaveButton } from '@educt/components/Buttons';
|
|
||||||
import LoadingPage from '@educt/components/LoadingPage';
|
import LoadingPage from '@educt/components/LoadingPage';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hooks
|
* Hooks
|
||||||
*/
|
*/
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
import { useUpdateUserInfo } from '@educt/hooks/queries';
|
|
||||||
|
|
||||||
type UpdateProfileInputType = {
|
|
||||||
about: string;
|
|
||||||
phone_number: string;
|
|
||||||
twitter_id: string;
|
|
||||||
telegram_id: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const schema = yup.object().shape({
|
|
||||||
about: yup
|
|
||||||
.string()
|
|
||||||
.max(250, 'Description must be at most 250 characters')
|
|
||||||
.transform(val => (!val ? null : val))
|
|
||||||
.nullable(true),
|
|
||||||
phone_number: yup
|
|
||||||
.string()
|
|
||||||
.isMobileValid(['en-US', 'ru-RU'], { strictMode: true })
|
|
||||||
.transform(val => (!val ? null : val))
|
|
||||||
.nullable(true),
|
|
||||||
twitter_id: yup
|
|
||||||
.string()
|
|
||||||
.matches(/(^|[^@\w])@(\w{1,15})\b/, 'Twitter username is not valid')
|
|
||||||
.transform(val => (!val ? null : val))
|
|
||||||
.nullable(true),
|
|
||||||
telegram_id: yup
|
|
||||||
.string()
|
|
||||||
.matches(/(^|[^@\w])@(\w{1,64})\b/, 'Telegram username is not valid')
|
|
||||||
.transform(val => (!val ? null : val))
|
|
||||||
.nullable(true),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Profile page
|
* Profile page
|
||||||
@ -77,55 +26,9 @@ const ProfilePage: React.FC<IPageProps> = () => {
|
|||||||
const {
|
const {
|
||||||
userStore: { me },
|
userStore: { me },
|
||||||
} = useRootStore();
|
} = useRootStore();
|
||||||
const { updateInfo, isLoading } = useUpdateUserInfo();
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const {
|
|
||||||
register,
|
|
||||||
handleSubmit,
|
|
||||||
reset,
|
|
||||||
formState: { errors, isDirty, dirtyFields },
|
|
||||||
} = useForm<UpdateProfileInputType>({
|
|
||||||
resolver: yupResolver(schema),
|
|
||||||
defaultValues: {
|
|
||||||
about: me?.about || '',
|
|
||||||
phone_number: me?.contacts?.phone_number || '',
|
|
||||||
twitter_id: me?.contacts?.twitter_id || '',
|
|
||||||
telegram_id: me?.contacts?.telegram_id || '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (me === null) return <LoadingPage />;
|
if (me === null) return <LoadingPage />;
|
||||||
|
|
||||||
const handleChangePassword = () => history.push(`/profile/change-password`);
|
|
||||||
|
|
||||||
const handleChangeEmail = () => history.push(`/profile/change-email`);
|
|
||||||
|
|
||||||
const handleUpdateProfile = async (data: UpdateProfileInputType) => {
|
|
||||||
try {
|
|
||||||
const updatedFields = helpers.getDirtyFields(dirtyFields, data);
|
|
||||||
const { about, phone_number, telegram_id, twitter_id } = await updateInfo(updatedFields);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update info in store
|
|
||||||
*/
|
|
||||||
runInAction(() => {
|
|
||||||
if (me !== null) {
|
|
||||||
me.about = about === undefined ? me.about : about;
|
|
||||||
if (me.contacts) {
|
|
||||||
me.contacts.phone_number = phone_number === undefined ? me.contacts.phone_number : phone_number;
|
|
||||||
me.contacts.twitter_id = twitter_id === undefined ? me.contacts.twitter_id : twitter_id;
|
|
||||||
me.contacts.telegram_id = telegram_id === undefined ? me.contacts.telegram_id : telegram_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
reset(data);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page maxW={'900px'}>
|
<Page maxW={'900px'}>
|
||||||
<Page.Heading heading='My account' description='Update your profile information or password here.' />
|
<Page.Heading heading='My account' description='Update your profile information or password here.' />
|
||||||
@ -140,126 +43,63 @@ const ProfilePage: React.FC<IPageProps> = () => {
|
|||||||
</UserProfile.Info>
|
</UserProfile.Info>
|
||||||
</UserProfile.Base>
|
</UserProfile.Base>
|
||||||
|
|
||||||
<form onSubmit={handleSubmit(handleUpdateProfile)}>
|
<UserProfile.Settings>
|
||||||
<UserProfile.Settings>
|
{/* Personal Info */}
|
||||||
{/* Personal Info */}
|
<UserProfile.Settings.Entry>
|
||||||
<UserProfile.Settings.Entry>
|
<UserProfile.Settings.Title title={'Personal Info'} />
|
||||||
<UserProfile.Settings.Title title={'Personal Info'} />
|
<UserProfile.Settings.EntryContent>
|
||||||
<UserProfile.Settings.EntryContent>
|
<Stack spacing={'6'}>
|
||||||
<Stack spacing={'6'}>
|
<UserProfile.Settings.FullnameInput defaultValue={me.fullname} />
|
||||||
<FormControl isReadOnly>
|
<UserProfile.Settings.EmailInput defaultValue={me.email} />
|
||||||
<FormLabel>Fullname</FormLabel>
|
<UserProfile.Settings.AboutInput />
|
||||||
<Input defaultValue={me.fullname} />
|
</Stack>
|
||||||
</FormControl>
|
</UserProfile.Settings.EntryContent>
|
||||||
<FormControl isReadOnly>
|
</UserProfile.Settings.Entry>
|
||||||
<FormLabel>Email</FormLabel>
|
|
||||||
<InputGroup size='md'>
|
|
||||||
<Input defaultValue={me.email} />
|
|
||||||
<InputRightElement width='4.5rem' pr='0.5rem'>
|
|
||||||
<Button onClick={handleChangeEmail} h='1.75rem' size='sm'>
|
|
||||||
Change
|
|
||||||
</Button>
|
|
||||||
</InputRightElement>
|
|
||||||
</InputGroup>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl>
|
|
||||||
<FormLabel>About</FormLabel>
|
|
||||||
<Textarea minH='130px' maxH='500px' isInvalid={!!errors.about} {...register('about')} />
|
|
||||||
<Text as='small' color='red.500'>
|
|
||||||
{errors.about?.message}
|
|
||||||
</Text>
|
|
||||||
<FormHelperText>Write description for your profile.</FormHelperText>
|
|
||||||
</FormControl>
|
|
||||||
</Stack>
|
|
||||||
</UserProfile.Settings.EntryContent>
|
|
||||||
</UserProfile.Settings.Entry>
|
|
||||||
|
|
||||||
{/* Contacts */}
|
{/* Contacts */}
|
||||||
<UserProfile.Settings.Entry>
|
<UserProfile.Settings.Entry>
|
||||||
<UserProfile.Settings.Title title={'Contacts'} />
|
<UserProfile.Settings.Title title={'Contacts'} />
|
||||||
<UserProfile.Settings.EntryContent>
|
<UserProfile.Settings.EntryContent>
|
||||||
<Stack spacing={'6'}>
|
<Stack spacing={'6'}>
|
||||||
<FormControl>
|
<UserProfile.Settings.PhoneInput />
|
||||||
<FormLabel>Phone</FormLabel>
|
<UserProfile.Settings.TwitterInput />
|
||||||
<Input
|
<UserProfile.Settings.TelegramInput />
|
||||||
placeholder='+1 (234) 555-1234'
|
</Stack>
|
||||||
isInvalid={!!errors.phone_number}
|
</UserProfile.Settings.EntryContent>
|
||||||
{...register('phone_number')}
|
</UserProfile.Settings.Entry>
|
||||||
/>
|
|
||||||
<Text as='small' color='red.500'>
|
|
||||||
{errors.phone_number?.message}
|
|
||||||
</Text>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl>
|
|
||||||
<FormLabel>Twitter</FormLabel>
|
|
||||||
<Input placeholder='@username' isInvalid={!!errors.twitter_id} {...register('twitter_id')} />
|
|
||||||
<Text as='small' color='red.500'>
|
|
||||||
{errors.twitter_id?.message}
|
|
||||||
</Text>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl>
|
|
||||||
<FormLabel>Telegram</FormLabel>
|
|
||||||
<Input placeholder='@username' isInvalid={!!errors.telegram_id} {...register('telegram_id')} />
|
|
||||||
<Text as='small' color='red.500'>
|
|
||||||
{errors.telegram_id?.message}
|
|
||||||
</Text>
|
|
||||||
</FormControl>
|
|
||||||
</Stack>
|
|
||||||
</UserProfile.Settings.EntryContent>
|
|
||||||
</UserProfile.Settings.Entry>
|
|
||||||
|
|
||||||
{/* Security */}
|
{/* Security */}
|
||||||
<UserProfile.Settings.Entry>
|
<UserProfile.Settings.Entry>
|
||||||
<UserProfile.Settings.Title title='Security' />
|
<UserProfile.Settings.Title title='Security' />
|
||||||
<UserProfile.Settings.EntryContent>
|
<UserProfile.Settings.EntryContent>
|
||||||
<FormControl isReadOnly>
|
<UserProfile.Settings.PasswordInput defaultValue={'**********'} />
|
||||||
<FormLabel>Password</FormLabel>
|
</UserProfile.Settings.EntryContent>
|
||||||
<InputGroup size='md'>
|
</UserProfile.Settings.Entry>
|
||||||
<Input defaultValue={'**********'} />
|
|
||||||
<InputRightElement width='4.5rem' pr='0.5rem'>
|
|
||||||
<Button onClick={handleChangePassword} h='1.75rem' size='sm'>
|
|
||||||
Change
|
|
||||||
</Button>
|
|
||||||
</InputRightElement>
|
|
||||||
</InputGroup>
|
|
||||||
</FormControl>
|
|
||||||
</UserProfile.Settings.EntryContent>
|
|
||||||
</UserProfile.Settings.Entry>
|
|
||||||
|
|
||||||
{/* Language */}
|
{/* Language */}
|
||||||
<UserProfile.Settings.Entry>
|
<UserProfile.Settings.Entry>
|
||||||
<UserProfile.Settings.Title title='Language' />
|
<UserProfile.Settings.Title title='Language' />
|
||||||
<UserProfile.Settings.EntryContent>
|
<UserProfile.Settings.EntryContent>
|
||||||
<FormControl>
|
<UserProfile.Settings.LanguageSelect />
|
||||||
<FormLabel>Display Language</FormLabel>
|
</UserProfile.Settings.EntryContent>
|
||||||
<Select>
|
</UserProfile.Settings.Entry>
|
||||||
<option value='en'>English</option>
|
|
||||||
</Select>
|
|
||||||
</FormControl>
|
|
||||||
</UserProfile.Settings.EntryContent>
|
|
||||||
</UserProfile.Settings.Entry>
|
|
||||||
|
|
||||||
{/* Night Theme */}
|
{/* Night Theme */}
|
||||||
<UserProfile.Settings.Entry>
|
<UserProfile.Settings.Entry>
|
||||||
<UserProfile.Settings.Title title='Night Theme' />
|
<UserProfile.Settings.Title title='Night Theme' />
|
||||||
<UserProfile.Settings.EntryContent>
|
<UserProfile.Settings.EntryContent>
|
||||||
<Box>
|
<UserProfile.Settings.ThemeSwitcher />
|
||||||
<SwitchDarkThemeButton />
|
</UserProfile.Settings.EntryContent>
|
||||||
</Box>
|
</UserProfile.Settings.Entry>
|
||||||
</UserProfile.Settings.EntryContent>
|
|
||||||
</UserProfile.Settings.Entry>
|
|
||||||
|
|
||||||
{/* Save Button */}
|
{/* Save Button */}
|
||||||
<UserProfile.Settings.Entry>
|
<UserProfile.Settings.Entry>
|
||||||
<UserProfile.Settings.Title />
|
<UserProfile.Settings.Title />
|
||||||
<UserProfile.Settings.EntryContent>
|
<UserProfile.Settings.EntryContent>
|
||||||
<Box>
|
<UserProfile.Settings.SaveSettingsButton />
|
||||||
<SaveButton type='submit' isLoading={isLoading} isDisabled={!isDirty} />
|
</UserProfile.Settings.EntryContent>
|
||||||
</Box>
|
</UserProfile.Settings.Entry>
|
||||||
</UserProfile.Settings.EntryContent>
|
</UserProfile.Settings>
|
||||||
</UserProfile.Settings.Entry>
|
|
||||||
</UserProfile.Settings>
|
|
||||||
</form>
|
|
||||||
</UserProfile>
|
</UserProfile>
|
||||||
</Page.Content>
|
</Page.Content>
|
||||||
</Page>
|
</Page>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user