mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 18:29:15 +03:00
feat: update user contacts
This commit is contained in:
parent
cd27d81ccd
commit
e402e6ac29
11
package-lock.json
generated
11
package-lock.json
generated
@ -3133,6 +3133,12 @@
|
|||||||
"source-map": "^0.6.1"
|
"source-map": "^0.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/validator": {
|
||||||
|
"version": "13.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.6.3.tgz",
|
||||||
|
"integrity": "sha512-fWG42pMJOL4jKsDDZZREnXLjc3UE0R8LOJfARWYg6U966rxDT7TYejYzLnUF5cvSObGg34nd0+H2wHHU5Omdfw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@types/warning": {
|
"@types/warning": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
|
||||||
@ -15258,6 +15264,11 @@
|
|||||||
"spdx-expression-parse": "^3.0.0"
|
"spdx-expression-parse": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"validator": {
|
||||||
|
"version": "13.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/validator/-/validator-13.6.0.tgz",
|
||||||
|
"integrity": "sha512-gVgKbdbHgtxpRyR8K0O6oFZPhhB5tT1jeEHZR0Znr9Svg03U0+r9DXWMrnRAB+HtCStDQKlaIZm42tVsVjqtjg=="
|
||||||
|
},
|
||||||
"value-equal": {
|
"value-equal": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz",
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
"typescript": "^3.9.5",
|
"typescript": "^3.9.5",
|
||||||
|
"validator": "^13.6.0",
|
||||||
"web-vitals": "^0.2.2",
|
"web-vitals": "^0.2.2",
|
||||||
"yup": "^0.32.9"
|
"yup": "^0.32.9"
|
||||||
},
|
},
|
||||||
@ -58,6 +59,7 @@
|
|||||||
"@types/js-cookie": "^2.2.7",
|
"@types/js-cookie": "^2.2.7",
|
||||||
"@types/react": "^16.14.13",
|
"@types/react": "^16.14.13",
|
||||||
"@types/react-helmet": "^6.1.2",
|
"@types/react-helmet": "^6.1.2",
|
||||||
"@types/react-router-dom": "^5.1.8"
|
"@types/react-router-dom": "^5.1.8",
|
||||||
|
"@types/validator": "^13.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import {
|
|||||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import useLoginQuery from 'hooks/useLoginQuery';
|
import useLoginQuery from 'hooks/useLoginQuery';
|
||||||
import LoginValidatorSchema from 'validators/LoginValidatorSchema';
|
import LoginSchema from 'validators/LoginSchema';
|
||||||
|
|
||||||
type AuthInputType = {
|
type AuthInputType = {
|
||||||
login: string;
|
login: string;
|
||||||
@ -29,7 +29,7 @@ const AuthForm: React.FC = () => {
|
|||||||
formState: { errors },
|
formState: { errors },
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
} = useForm<AuthInputType>({
|
} = useForm<AuthInputType>({
|
||||||
resolver: yupResolver(LoginValidatorSchema),
|
resolver: yupResolver(LoginSchema),
|
||||||
});
|
});
|
||||||
const { login, loading } = useLoginQuery();
|
const { login, loading } = useLoginQuery();
|
||||||
|
|
||||||
|
|||||||
@ -1,39 +1,107 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||||
import { MdSave } from 'react-icons/md';
|
import { MdSave } from 'react-icons/md';
|
||||||
|
import { IUserContacts } from 'interfaces';
|
||||||
import { Box, Heading, Stack } from '@chakra-ui/layout';
|
import { Box, Heading, Stack } from '@chakra-ui/layout';
|
||||||
import { FormControl, FormHelperText } from '@chakra-ui/form-control';
|
import { FormControl, FormHelperText } from '@chakra-ui/form-control';
|
||||||
import { Input } from '@chakra-ui/input';
|
import { Input } from '@chakra-ui/input';
|
||||||
|
import { Text } from '@chakra-ui/react';
|
||||||
import { Button } from '@chakra-ui/button';
|
import { Button } from '@chakra-ui/button';
|
||||||
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
|
|
||||||
|
import UpdateUserContactsSchema from 'validators/UpdateUserContactsSchema';
|
||||||
|
import useUpdateUserContactsQuery from 'hooks/useUpdateUserContactsQuery';
|
||||||
|
|
||||||
|
type UpdateUserContactsInputType = {
|
||||||
|
phone_number: string | null;
|
||||||
|
twitter_id: string | null;
|
||||||
|
telegram_id: string | null;
|
||||||
|
vk_id: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
type UpdateUserContactsFormProps = {
|
||||||
|
contacts: IUserContacts | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const UpdateUserContactsForm: React.FC<UpdateUserContactsFormProps> = ({ contacts }) => {
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
formState: { errors, isDirty },
|
||||||
|
handleSubmit,
|
||||||
|
reset,
|
||||||
|
} = useForm<UpdateUserContactsInputType>({
|
||||||
|
resolver: yupResolver(UpdateUserContactsSchema),
|
||||||
|
defaultValues: {
|
||||||
|
phone_number: contacts && (contacts.phone_number ?? ''),
|
||||||
|
twitter_id: contacts && (contacts.twitter_id?.replace(/^/, '@') ?? ''),
|
||||||
|
telegram_id: contacts && (contacts.telegram_id?.replace(/^/, '@') ?? ''),
|
||||||
|
vk_id: contacts && (contacts.vk_id ?? ''),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { updateContacts, loading } = useUpdateUserContactsQuery();
|
||||||
|
|
||||||
|
const onSubmit: SubmitHandler<UpdateUserContactsInputType> = data => {
|
||||||
|
updateContacts(data).finally(() => reset(data));
|
||||||
|
};
|
||||||
|
|
||||||
const UpdateUserContactsForm: React.FC = () => {
|
|
||||||
return (
|
return (
|
||||||
<Box as='form'>
|
<Box>
|
||||||
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<Box borderRadius='md' borderWidth='1px' padding='20px'>
|
<Box borderRadius='md' borderWidth='1px' padding='20px'>
|
||||||
<Heading as='h3' size='lg'>
|
<Heading as='h3' size='lg'>
|
||||||
My Contacts
|
My Contacts
|
||||||
</Heading>
|
</Heading>
|
||||||
<Stack spacing='5px' margin='20px 0'>
|
<Stack spacing='5px' margin='20px 0'>
|
||||||
<FormControl id='tel'>
|
<FormControl id='phone_number'>
|
||||||
<FormHelperText color='gray.500'>Telephone</FormHelperText>
|
<FormHelperText color='gray.500'>Phone number with country code</FormHelperText>
|
||||||
<Input type='number' size='md' variant='flushed' placeholder='123-456-7890' />
|
<Input
|
||||||
|
type='text'
|
||||||
|
size='md'
|
||||||
|
variant='flushed'
|
||||||
|
placeholder='+1 (234) 555-1234'
|
||||||
|
{...register('phone_number')}
|
||||||
|
/>
|
||||||
|
<Text as='small' color='red.500'>
|
||||||
|
{errors.phone_number?.message}
|
||||||
|
</Text>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl id='twitter'>
|
<FormControl id='twitter_id'>
|
||||||
<FormHelperText color='gray.500'>Twitter</FormHelperText>
|
<FormHelperText color='gray.500'>Twitter</FormHelperText>
|
||||||
<Input type='text' size='md' variant='flushed' placeholder='https://twitter.com/id' />
|
<Input type='text' size='md' variant='flushed' placeholder='@tw_username' {...register('twitter_id')} />
|
||||||
|
<Text as='small' color='red.500'>
|
||||||
|
{errors.twitter_id?.message}
|
||||||
|
</Text>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl id='telegram'>
|
<FormControl id='telegram'>
|
||||||
<FormHelperText color='gray.500'>Telegram</FormHelperText>
|
<FormHelperText color='gray.500'>Telegram</FormHelperText>
|
||||||
<Input type='text' size='md' variant='flushed' placeholder='@tag' />
|
<Input type='text' size='md' variant='flushed' placeholder='@tg_username' {...register('telegram_id')} />
|
||||||
|
<Text as='small' color='red.500'>
|
||||||
|
{errors.telegram_id?.message}
|
||||||
|
</Text>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormControl id='vk'>
|
<FormControl id='vk'>
|
||||||
<FormHelperText color='gray.500'>VKontakte</FormHelperText>
|
<FormHelperText color='gray.500'>VKontakte</FormHelperText>
|
||||||
<Input type='text' size='md' variant='flushed' placeholder='https://vk.com/id' />
|
<Input type='text' size='md' variant='flushed' placeholder='VKontakte id' {...register('vk_id')} />
|
||||||
|
<Text as='small' color='red.500'>
|
||||||
|
{errors.vk_id?.message}
|
||||||
|
</Text>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
<Button colorScheme='blue' mt='4' type='submit' size='md' variant='outline' rightIcon={<MdSave />}>
|
<Button
|
||||||
|
colorScheme='blue'
|
||||||
|
mt='4'
|
||||||
|
type='submit'
|
||||||
|
size='md'
|
||||||
|
variant='outline'
|
||||||
|
isLoading={loading}
|
||||||
|
loadingText='Saving...'
|
||||||
|
rightIcon={<MdSave />}
|
||||||
|
isDisabled={!isDirty}
|
||||||
|
>
|
||||||
Save
|
Save
|
||||||
</Button>
|
</Button>
|
||||||
|
</form>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,3 +12,13 @@ export function userHasRoles(userRoles: IUserRole[], roles: UserRoleEnum[]): boo
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean object values
|
||||||
|
*
|
||||||
|
* @param obj Current object that have null values
|
||||||
|
* @returns Clean object that don't have null values
|
||||||
|
*/
|
||||||
|
export function removeEmptyValues(obj: object) {
|
||||||
|
return Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== null));
|
||||||
|
}
|
||||||
|
|||||||
65
src/hooks/useUpdateUserContactsQuery.ts
Normal file
65
src/hooks/useUpdateUserContactsQuery.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { useToast } from '@chakra-ui/react';
|
||||||
|
import { IDataResult, IUserContacts } from 'interfaces';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useErrorHandler } from 'react-error-boundary';
|
||||||
|
import useIsMountedRef from './useIsMountedRef';
|
||||||
|
import { useRootStore } from './useRootStore';
|
||||||
|
|
||||||
|
type UpdateContactsStateType = {
|
||||||
|
result: IDataResult | null;
|
||||||
|
error: any;
|
||||||
|
loading: boolean;
|
||||||
|
fetched: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const useUpdateUserContactsQuery = () => {
|
||||||
|
const isMountedRef = useIsMountedRef();
|
||||||
|
const [state, setState] = useState<UpdateContactsStateType>({
|
||||||
|
result: null,
|
||||||
|
error: null,
|
||||||
|
loading: false,
|
||||||
|
fetched: false,
|
||||||
|
});
|
||||||
|
const { userStore } = useRootStore();
|
||||||
|
const toast = useToast();
|
||||||
|
const handleError = useErrorHandler();
|
||||||
|
|
||||||
|
const updateContacts = async (data: IUserContacts) => {
|
||||||
|
try {
|
||||||
|
setState(s => ({ ...s, loading: true }));
|
||||||
|
const result = await userStore.updateCurrentUserContacts(data);
|
||||||
|
toast({ title: `Contacts saved!`, isClosable: true, status: 'info' });
|
||||||
|
|
||||||
|
if (isMountedRef.current) {
|
||||||
|
setState(s => ({ ...s, result }));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (isMountedRef.current) {
|
||||||
|
setState(s => ({ ...s, error }));
|
||||||
|
}
|
||||||
|
if (error.response) {
|
||||||
|
/**
|
||||||
|
* Catch response error
|
||||||
|
*/
|
||||||
|
switch (error.response.status) {
|
||||||
|
case 422:
|
||||||
|
toast({ title: 'Validation error.', status: 'error', duration: 2000 });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
handleError(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(error);
|
||||||
|
handleError(error);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (isMountedRef.current) {
|
||||||
|
setState(s => ({ ...s, loading: false, fetched: true }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...state, updateContacts };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useUpdateUserContactsQuery;
|
||||||
@ -24,6 +24,14 @@ export interface IUser {
|
|||||||
last_name: string;
|
last_name: string;
|
||||||
email: string;
|
email: string;
|
||||||
roles: IUserRole[];
|
roles: IUserRole[];
|
||||||
|
contacts: IUserContacts | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IUserContacts {
|
||||||
|
phone_number: string | null;
|
||||||
|
twitter_id: string | null;
|
||||||
|
telegram_id: string | null;
|
||||||
|
vk_id: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IUserRole {
|
export interface IUserRole {
|
||||||
@ -46,14 +54,12 @@ export interface ILoginResult extends IDataResult {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IUpdatedContactsResult extends IDataResult {
|
||||||
|
data: IUserContacts;
|
||||||
|
}
|
||||||
|
|
||||||
export interface IUserResult extends IDataResult {
|
export interface IUserResult extends IDataResult {
|
||||||
data: {
|
data: IUser;
|
||||||
id: string;
|
|
||||||
first_name: string;
|
|
||||||
last_name: string;
|
|
||||||
email: string;
|
|
||||||
roles: IUserRole[];
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPageProps extends RouteComponentProps {
|
export interface IPageProps extends RouteComponentProps {
|
||||||
|
|||||||
@ -108,7 +108,7 @@ const ProfilePage: React.FC<IPageProps> = ({ title }) => {
|
|||||||
<UserAccountInfo user={userStore.me} />
|
<UserAccountInfo user={userStore.me} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel padding='10px 0' mt='20px'>
|
<TabPanel padding='10px 0' mt='20px'>
|
||||||
<UpdateUserContactsForm />
|
<UpdateUserContactsForm contacts={userStore.me.contacts} />
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</TabPanels>
|
</TabPanels>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { AxiosInstance } from 'axios';
|
import { AxiosInstance } from 'axios';
|
||||||
import { IUserResult } from 'interfaces';
|
import { IUpdatedContactsResult, IUserContacts, IUserResult } from 'interfaces';
|
||||||
|
import * as helpers from 'helpers';
|
||||||
|
|
||||||
export default class UserService {
|
export default class UserService {
|
||||||
public api: AxiosInstance;
|
public api: AxiosInstance;
|
||||||
@ -17,4 +18,15 @@ export default class UserService {
|
|||||||
const result = await this.api.get('v1/me');
|
const result = await this.api.get('v1/me');
|
||||||
return result.data;
|
return result.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update authorized user contacts data
|
||||||
|
*
|
||||||
|
* @param data User contacts
|
||||||
|
* @returns Updated user contacts data
|
||||||
|
*/
|
||||||
|
public async updateContacts(data: IUserContacts): Promise<IUpdatedContactsResult> {
|
||||||
|
const result = await this.api.put('v1/me/contacts', helpers.removeEmptyValues(data));
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { AxiosInstance } from 'axios';
|
import { AxiosInstance } from 'axios';
|
||||||
import { makeAutoObservable, runInAction } from 'mobx';
|
import { makeAutoObservable, runInAction } from 'mobx';
|
||||||
import { IUserResult, IUser } from 'interfaces';
|
import { IUserResult, IUser, IUserContacts, IUpdatedContactsResult } from 'interfaces';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Services
|
* Services
|
||||||
@ -41,6 +41,22 @@ export default class UserStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async updateCurrentUserContacts(data: IUserContacts): Promise<IUpdatedContactsResult> {
|
||||||
|
try {
|
||||||
|
const result = await this.userService.updateContacts(data);
|
||||||
|
|
||||||
|
runInAction(() => {
|
||||||
|
if (this.me) {
|
||||||
|
this.me.contacts = result.data;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public reset(): void {
|
public reset(): void {
|
||||||
runInAction(() => {
|
runInAction(() => {
|
||||||
this.me = null;
|
this.me = null;
|
||||||
|
|||||||
10
src/types/yup/index.d.ts
vendored
Normal file
10
src/types/yup/index.d.ts
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import validator from 'validator';
|
||||||
|
|
||||||
|
declare module 'yup' {
|
||||||
|
interface StringSchema {
|
||||||
|
isMobileValid(
|
||||||
|
locales: validator.MobilePhoneLocale[],
|
||||||
|
options?: validator.IsMobilePhoneOptions | undefined
|
||||||
|
): StringSchema;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import * as yup from 'yup';
|
import yup from 'validators';
|
||||||
|
|
||||||
const LoginValidatorSchema = yup.object().shape({
|
const LoginSchema = yup.object().shape({
|
||||||
login: yup.string().required('Login is required'),
|
login: yup.string().required('Login is required'),
|
||||||
password: yup.string().min(6, 'Password must be at least 6 characters').required('Password is required'),
|
password: yup.string().min(6, 'Password must be at least 6 characters').required('Password is required'),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default LoginValidatorSchema;
|
export default LoginSchema;
|
||||||
26
src/validators/UpdateUserContactsSchema.ts
Normal file
26
src/validators/UpdateUserContactsSchema.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import yup from 'validators';
|
||||||
|
|
||||||
|
const UpdateUserContactsSchema = yup.object().shape({
|
||||||
|
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),
|
||||||
|
vk_id: yup
|
||||||
|
.string()
|
||||||
|
.matches(/^([a-zA-Z0-9_]){1,64}$/, 'VKontakte username is not valid')
|
||||||
|
.transform(val => (!val ? null : val))
|
||||||
|
.nullable(true),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default UpdateUserContactsSchema;
|
||||||
25
src/validators/index.ts
Normal file
25
src/validators/index.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import * as yup from 'yup';
|
||||||
|
import validator from 'validator';
|
||||||
|
import isMobilePhone from 'validator/lib/isMobilePhone';
|
||||||
|
import { string, StringSchema } from 'yup';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delcare custom methods
|
||||||
|
*/
|
||||||
|
yup.addMethod<StringSchema>(
|
||||||
|
string,
|
||||||
|
'isMobileValid',
|
||||||
|
function (
|
||||||
|
this: StringSchema,
|
||||||
|
locales: validator.MobilePhoneLocale[],
|
||||||
|
options?: validator.IsMobilePhoneOptions | undefined
|
||||||
|
) {
|
||||||
|
return this.test({
|
||||||
|
name: 'isMobileValid',
|
||||||
|
message: 'Phone number is not valid',
|
||||||
|
test: value => (!value ? true : isMobilePhone(value, locales, options)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default yup;
|
||||||
@ -15,7 +15,9 @@
|
|||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react"
|
"jsx": "react",
|
||||||
|
"typeRoots": ["./types", "./node_modules/@types"]
|
||||||
},
|
},
|
||||||
"include": ["src"]
|
"include": ["src"],
|
||||||
|
"exclude": ["types"]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user