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 { 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 { Entry } from './Entry';
|
||||
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 {
|
||||
Title: React.FC<ITitleProps & BoxProps>;
|
||||
Entry: 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 => {
|
||||
return (
|
||||
<Box maxW={{ base: 'full', lg: '750px' }} {...props}>
|
||||
<ChakraHeading as='h2' mt='12' fontSize={'2xl'} fontWeight={'bold'}>
|
||||
Account Settings
|
||||
</ChakraHeading>
|
||||
<Divider my='5' />
|
||||
<Stack divider={<StackDivider />} spacing={'5'}>
|
||||
{props.children}
|
||||
</Stack>
|
||||
</Box>
|
||||
<SettingsContextProvider>
|
||||
<Box maxW={{ base: 'full', lg: '750px' }} {...props}>
|
||||
<ChakraHeading as='h2' mt='12' fontSize={'2xl'} fontWeight={'bold'}>
|
||||
Account Settings
|
||||
</ChakraHeading>
|
||||
<Divider my='5' />
|
||||
<Stack divider={<StackDivider />} spacing={'5'}>
|
||||
{props.children}
|
||||
</Stack>
|
||||
</Box>
|
||||
</SettingsContextProvider>
|
||||
);
|
||||
};
|
||||
|
||||
Settings.Title = Title;
|
||||
Settings.Entry = Entry;
|
||||
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 };
|
||||
|
||||
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 { observer } from 'mobx-react';
|
||||
import { runInAction } from 'mobx';
|
||||
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';
|
||||
import { Stack } from '@chakra-ui/layout';
|
||||
|
||||
/**
|
||||
* Types
|
||||
@ -28,47 +12,12 @@ import type { IPageProps } from '@educt/interfaces';
|
||||
*/
|
||||
import { Page } from '@educt/components/PageElements';
|
||||
import { UserProfile } from '@educt/components/UserProfile';
|
||||
import { SwitchDarkThemeButton } from '@educt/components/SwitchDarkThemeButton';
|
||||
import { SaveButton } from '@educt/components/Buttons';
|
||||
import LoadingPage from '@educt/components/LoadingPage';
|
||||
|
||||
/**
|
||||
* Hooks
|
||||
*/
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useForm } from 'react-hook-form';
|
||||
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
|
||||
@ -77,55 +26,9 @@ const ProfilePage: React.FC<IPageProps> = () => {
|
||||
const {
|
||||
userStore: { me },
|
||||
} = 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 />;
|
||||
|
||||
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 (
|
||||
<Page maxW={'900px'}>
|
||||
<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.Base>
|
||||
|
||||
<form onSubmit={handleSubmit(handleUpdateProfile)}>
|
||||
<UserProfile.Settings>
|
||||
{/* Personal Info */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title={'Personal Info'} />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<Stack spacing={'6'}>
|
||||
<FormControl isReadOnly>
|
||||
<FormLabel>Fullname</FormLabel>
|
||||
<Input defaultValue={me.fullname} />
|
||||
</FormControl>
|
||||
<FormControl isReadOnly>
|
||||
<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>
|
||||
<UserProfile.Settings>
|
||||
{/* Personal Info */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title={'Personal Info'} />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<Stack spacing={'6'}>
|
||||
<UserProfile.Settings.FullnameInput defaultValue={me.fullname} />
|
||||
<UserProfile.Settings.EmailInput defaultValue={me.email} />
|
||||
<UserProfile.Settings.AboutInput />
|
||||
</Stack>
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
|
||||
{/* Contacts */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title={'Contacts'} />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<Stack spacing={'6'}>
|
||||
<FormControl>
|
||||
<FormLabel>Phone</FormLabel>
|
||||
<Input
|
||||
placeholder='+1 (234) 555-1234'
|
||||
isInvalid={!!errors.phone_number}
|
||||
{...register('phone_number')}
|
||||
/>
|
||||
<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>
|
||||
{/* Contacts */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title={'Contacts'} />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<Stack spacing={'6'}>
|
||||
<UserProfile.Settings.PhoneInput />
|
||||
<UserProfile.Settings.TwitterInput />
|
||||
<UserProfile.Settings.TelegramInput />
|
||||
</Stack>
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
|
||||
{/* Security */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title='Security' />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<FormControl isReadOnly>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<InputGroup size='md'>
|
||||
<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>
|
||||
{/* Security */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title='Security' />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<UserProfile.Settings.PasswordInput defaultValue={'**********'} />
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
|
||||
{/* Language */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title='Language' />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<FormControl>
|
||||
<FormLabel>Display Language</FormLabel>
|
||||
<Select>
|
||||
<option value='en'>English</option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
{/* Language */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title='Language' />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<UserProfile.Settings.LanguageSelect />
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
|
||||
{/* Night Theme */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title='Night Theme' />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<Box>
|
||||
<SwitchDarkThemeButton />
|
||||
</Box>
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
{/* Night Theme */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title title='Night Theme' />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<UserProfile.Settings.ThemeSwitcher />
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
|
||||
{/* Save Button */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<Box>
|
||||
<SaveButton type='submit' isLoading={isLoading} isDisabled={!isDirty} />
|
||||
</Box>
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
</UserProfile.Settings>
|
||||
</form>
|
||||
{/* Save Button */}
|
||||
<UserProfile.Settings.Entry>
|
||||
<UserProfile.Settings.Title />
|
||||
<UserProfile.Settings.EntryContent>
|
||||
<UserProfile.Settings.SaveSettingsButton />
|
||||
</UserProfile.Settings.EntryContent>
|
||||
</UserProfile.Settings.Entry>
|
||||
</UserProfile.Settings>
|
||||
</UserProfile>
|
||||
</Page.Content>
|
||||
</Page>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user