mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: refactor & added custom async select
This commit is contained in:
parent
3e69a47907
commit
eb33113912
72
package-lock.json
generated
72
package-lock.json
generated
@ -953,18 +953,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@emotion/cache": {
|
|
||||||
"version": "11.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.4.0.tgz",
|
|
||||||
"integrity": "sha512-Zx70bjE7LErRO9OaZrhf22Qye1y4F7iDl+ITjet0J+i+B88PrAOBkKvaAWhxsZf72tDLajwCgfCjJ2dvH77C3g==",
|
|
||||||
"requires": {
|
|
||||||
"@emotion/memoize": "^0.7.4",
|
|
||||||
"@emotion/sheet": "^1.0.0",
|
|
||||||
"@emotion/utils": "^1.0.0",
|
|
||||||
"@emotion/weak-memoize": "^0.2.5",
|
|
||||||
"stylis": "^4.0.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@emotion/hash": {
|
"@emotion/hash": {
|
||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz",
|
||||||
@ -1028,11 +1016,6 @@
|
|||||||
"csstype": "^3.0.2"
|
"csstype": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@emotion/sheet": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-QQPB1B70JEVUHuNtzjHftMGv6eC3Y9wqavyarj4x4lg47RACkeSfNo5pxIOKizwS9AEFLohsqoaxGQj4p0vSIw=="
|
|
||||||
},
|
|
||||||
"@emotion/styled": {
|
"@emotion/styled": {
|
||||||
"version": "11.3.0",
|
"version": "11.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.3.0.tgz",
|
||||||
@ -1206,14 +1189,6 @@
|
|||||||
"@types/react-router": "*"
|
"@types/react-router": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/react-transition-group": {
|
|
||||||
"version": "4.4.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz",
|
|
||||||
"integrity": "sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==",
|
|
||||||
"requires": {
|
|
||||||
"@types/react": "*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@types/scheduler": {
|
"@types/scheduler": {
|
||||||
"version": "0.16.2",
|
"version": "0.16.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
|
||||||
@ -1414,15 +1389,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
|
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="
|
||||||
},
|
},
|
||||||
"dom-helpers": {
|
|
||||||
"version": "5.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
|
||||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.8.7",
|
|
||||||
"csstype": "^3.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.3.870",
|
"version": "1.3.870",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.870.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.870.tgz",
|
||||||
@ -1870,11 +1836,6 @@
|
|||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"memoize-one": {
|
|
||||||
"version": "5.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz",
|
|
||||||
"integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q=="
|
|
||||||
},
|
|
||||||
"mini-create-react-context": {
|
"mini-create-react-context": {
|
||||||
"version": "0.4.1",
|
"version": "0.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz",
|
||||||
@ -2207,25 +2168,19 @@
|
|||||||
"tiny-warning": "^1.0.0"
|
"tiny-warning": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-select": {
|
|
||||||
"version": "5.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.1.0.tgz",
|
|
||||||
"integrity": "sha512-SkEBD1AYsSXrIdNj5HBt7+Ehe+jxdiB448J0atJqR6lE3l/GcFlRf4JYB3NlHe/02jrW4AnIQLo1t0IqWrxXOw==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.12.0",
|
|
||||||
"@emotion/cache": "^11.4.0",
|
|
||||||
"@emotion/react": "^11.1.1",
|
|
||||||
"@types/react-transition-group": "^4.4.0",
|
|
||||||
"memoize-one": "^5.0.0",
|
|
||||||
"prop-types": "^15.6.0",
|
|
||||||
"react-transition-group": "^4.3.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"react-side-effect": {
|
"react-side-effect": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.1.tgz",
|
||||||
"integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ=="
|
"integrity": "sha512-2FoTQzRNTncBVtnzxFOk2mCpcfxQpenBMbk5kSVBg5UcPqV9fRbgY2zhb7GTWWOlpFmAxhClBDlIq8Rsubz1yQ=="
|
||||||
},
|
},
|
||||||
|
"react-spinners": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-spinners/-/react-spinners-0.11.0.tgz",
|
||||||
|
"integrity": "sha512-rDZc0ABWn/M1OryboGsWVmIPg8uYWl0L35jPUhr40+Yg+syVPjeHwvnB7XWaRpaKus3M0cG9BiJA+ZB0dAwWyw==",
|
||||||
|
"requires": {
|
||||||
|
"@emotion/react": "^11.1.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-style-singleton": {
|
"react-style-singleton": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.1.1.tgz",
|
||||||
@ -2243,17 +2198,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-transition-group": {
|
|
||||||
"version": "4.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz",
|
|
||||||
"integrity": "sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==",
|
|
||||||
"requires": {
|
|
||||||
"@babel/runtime": "^7.5.5",
|
|
||||||
"dom-helpers": "^5.0.1",
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"prop-types": "^15.6.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"regenerator-runtime": {
|
"regenerator-runtime": {
|
||||||
"version": "0.13.9",
|
"version": "0.13.9",
|
||||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
"react-hook-form": "^7.12.2",
|
"react-hook-form": "^7.12.2",
|
||||||
"react-icons": "^3.11.0",
|
"react-icons": "^3.11.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-select": "^5.1.0",
|
"react-spinners": "^0.11.0",
|
||||||
"validator": "^13.6.0",
|
"validator": "^13.6.0",
|
||||||
"yup": "^0.32.9"
|
"yup": "^0.32.9"
|
||||||
},
|
},
|
||||||
|
|||||||
25
src/components/AsyncSelect/AsyncSelect.tsx
Normal file
25
src/components/AsyncSelect/AsyncSelect.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import Select from '@educt/components/Select';
|
||||||
|
import { ISelectPropsType } from '../Select/Select';
|
||||||
|
import { OptionType } from '@educt/types';
|
||||||
|
|
||||||
|
interface IAsyncSelectPropsType extends ISelectPropsType {
|
||||||
|
loadOptions: () => Promise<OptionType[] | undefined>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AsyncSelect: React.FC<IAsyncSelectPropsType> = props => {
|
||||||
|
const [options, setOptions] = useState<OptionType[] | []>([]);
|
||||||
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
const { loadOptions, ...selectProps } = props;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsLoading(true);
|
||||||
|
loadOptions()
|
||||||
|
.then(options => options && setOptions(options))
|
||||||
|
.finally(() => setIsLoading(false));
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <Select options={options} isLoading={isLoading} {...selectProps} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AsyncSelect;
|
||||||
3
src/components/AsyncSelect/index.ts
Normal file
3
src/components/AsyncSelect/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import AsyncSelect from './AsyncSelect';
|
||||||
|
|
||||||
|
export default AsyncSelect;
|
||||||
@ -46,7 +46,7 @@ const NavMobile: React.FC = () => {
|
|||||||
padding='10px'
|
padding='10px'
|
||||||
borderRadius='md'
|
borderRadius='md'
|
||||||
marginTop='auto'
|
marginTop='auto'
|
||||||
backgroundColor={colorMode === 'dark' ? 'gray.700' : 'gray.100'}
|
backgroundColor={colorMode === 'dark' ? 'gray.700' : 'gray.50'}
|
||||||
>
|
>
|
||||||
<Flex alignItems='center'>
|
<Flex alignItems='center'>
|
||||||
<Avatar size='sm' name={me.fullname} marginRight={3} />
|
<Avatar size='sm' name={me.fullname} marginRight={3} />
|
||||||
|
|||||||
63
src/components/Select/Select.tsx
Normal file
63
src/components/Select/Select.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { BeatLoader } from 'react-spinners';
|
||||||
|
import { Box } from '@chakra-ui/layout';
|
||||||
|
import { Button } from '@chakra-ui/button';
|
||||||
|
import { Menu, MenuButton, MenuItem, MenuList } from '@chakra-ui/menu';
|
||||||
|
import { ChevronDownIcon } from '@chakra-ui/icons';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types
|
||||||
|
*/
|
||||||
|
import type { OptionType } from '@educt/types';
|
||||||
|
|
||||||
|
export interface ISelectPropsType {
|
||||||
|
options?: OptionType[];
|
||||||
|
value?: string;
|
||||||
|
onChange: (newValue: string) => void;
|
||||||
|
placeholder?: string;
|
||||||
|
noOptionsMessage?: string;
|
||||||
|
isLoading?: boolean;
|
||||||
|
loadingText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Select: React.FC<ISelectPropsType> = props => {
|
||||||
|
const { options, value, noOptionsMessage, loadingText, isLoading } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Menu isLazy matchWidth>
|
||||||
|
<MenuButton
|
||||||
|
as={Button}
|
||||||
|
variant='outline'
|
||||||
|
width='full'
|
||||||
|
textAlign='left'
|
||||||
|
justifyContent='flex-end'
|
||||||
|
isLoading={isLoading}
|
||||||
|
loadingText={loadingText || 'Loading...'}
|
||||||
|
spinner={<BeatLoader size={6} color='gray' />}
|
||||||
|
rightIcon={<ChevronDownIcon />}
|
||||||
|
>
|
||||||
|
{options?.find(option => option.value === value)?.label || props?.placeholder || 'Select Option'}
|
||||||
|
</MenuButton>
|
||||||
|
<MenuList>
|
||||||
|
{options && options.length !== 0 ? (
|
||||||
|
options.map(option => (
|
||||||
|
<MenuItem
|
||||||
|
key={option.value}
|
||||||
|
onClick={() => {
|
||||||
|
props.onChange(option.value);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{option.label}
|
||||||
|
</MenuItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<Box textAlign='center'>{noOptionsMessage || 'No options.'}</Box>
|
||||||
|
)}
|
||||||
|
</MenuList>
|
||||||
|
</Menu>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Select;
|
||||||
3
src/components/Select/index.ts
Normal file
3
src/components/Select/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Select from './Select';
|
||||||
|
|
||||||
|
export default Select;
|
||||||
@ -1,13 +1,24 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from 'react-helmet';
|
||||||
import { Redirect } from 'react-router-dom';
|
|
||||||
import { observer } from 'mobx-react';
|
import { observer } from 'mobx-react';
|
||||||
import { IPageProps } from '@educt/interfaces';
|
import { Redirect } from 'react-router-dom';
|
||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
import { useRootStore } from '@educt/hooks/useRootStore';
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Types
|
||||||
|
*/
|
||||||
|
import { IPageProps } from '@educt/interfaces';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Components
|
||||||
|
*/
|
||||||
import AuthForm from './components/AuthForm';
|
import AuthForm from './components/AuthForm';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hooks
|
||||||
|
*/
|
||||||
|
import { useRootStore } from '@educt/hooks/useRootStore';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Auth page
|
* Auth page
|
||||||
*/
|
*/
|
||||||
@ -16,6 +27,7 @@ const AuthPage: React.FC<IPageProps> = ({ title }) => {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* User already authorized
|
* User already authorized
|
||||||
|
* This is triggered by mobx observer function
|
||||||
*/
|
*/
|
||||||
if (authStore.isLoggedIn) {
|
if (authStore.isLoggedIn) {
|
||||||
return <Redirect to='/' />;
|
return <Redirect to='/' />;
|
||||||
|
|||||||
@ -16,8 +16,6 @@ import {
|
|||||||
import { MdCloudUpload } from 'react-icons/md';
|
import { MdCloudUpload } from 'react-icons/md';
|
||||||
import { SubmitHandler, useForm, useWatch, Controller } from 'react-hook-form';
|
import { SubmitHandler, useForm, useWatch, Controller } from 'react-hook-form';
|
||||||
import { yupResolver } from '@hookform/resolvers/yup';
|
import { yupResolver } from '@hookform/resolvers/yup';
|
||||||
import AsyncSelect from 'react-select/async';
|
|
||||||
import * as helpers from '@educt/helpers';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types
|
* Types
|
||||||
@ -37,6 +35,8 @@ import useIsMountedRef from '@educt/hooks/useIsMountedRef';
|
|||||||
* Schema
|
* Schema
|
||||||
*/
|
*/
|
||||||
import CreateCourseSchema from './CreateCourseForm.validator';
|
import CreateCourseSchema from './CreateCourseForm.validator';
|
||||||
|
import AsyncSelect from '@educt/components/AsyncSelect';
|
||||||
|
import Select from '@educt/components/Select';
|
||||||
|
|
||||||
type CreateCourseInputType = {
|
type CreateCourseInputType = {
|
||||||
title: string;
|
title: string;
|
||||||
@ -79,17 +79,14 @@ const CreateCourseForm: React.FC<CreateCourseFormProps> = () => {
|
|||||||
/**
|
/**
|
||||||
* Load users into select field
|
* Load users into select field
|
||||||
*/
|
*/
|
||||||
const loadUsersOptions = async (inputValue: string): Promise<OptionType[] | undefined> => {
|
const loadUsersOptions = async (): Promise<OptionType[] | undefined> => {
|
||||||
try {
|
try {
|
||||||
const users = await userService.fetchAll({ search: inputValue, role: UserRoleEnum.TEACHER });
|
const users = await userService.fetchAll({ limit: 12, role: UserRoleEnum.TEACHER });
|
||||||
|
|
||||||
// TODO: remove filter later
|
return users.data.map(user => ({
|
||||||
return users.data
|
label: user.fullname,
|
||||||
.filter(user => helpers.userContainRoles(user.roles, [UserRoleEnum.TEACHER]))
|
value: user.id,
|
||||||
.map(user => ({
|
}));
|
||||||
label: user.fullname,
|
|
||||||
value: user.id,
|
|
||||||
}));
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
toast({ title: 'Error' });
|
toast({ title: 'Error' });
|
||||||
@ -102,7 +99,7 @@ const CreateCourseForm: React.FC<CreateCourseFormProps> = () => {
|
|||||||
/**
|
/**
|
||||||
* Load categories into select filed
|
* Load categories into select filed
|
||||||
*/
|
*/
|
||||||
const loadCategoriesOptions = async (inputValue: string): Promise<OptionType[] | undefined> => {
|
const loadCategoriesOptions = async (): Promise<OptionType[] | undefined> => {
|
||||||
try {
|
try {
|
||||||
const categories = await categoryService.fetchAll();
|
const categories = await categoryService.fetchAll();
|
||||||
return categories.data.map(category => ({
|
return categories.data.map(category => ({
|
||||||
@ -163,8 +160,8 @@ const CreateCourseForm: React.FC<CreateCourseFormProps> = () => {
|
|||||||
<InputRightElement
|
<InputRightElement
|
||||||
mr='2'
|
mr='2'
|
||||||
children={
|
children={
|
||||||
<Text as='small' color={!!errors.title || title.length > 60 ? 'red.500' : 'gray.500'}>
|
<Text as='small' color={!!errors.title || title.length > 90 ? 'red.500' : 'gray.500'}>
|
||||||
{title.length}/60
|
{title.length}/90
|
||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
@ -187,16 +184,14 @@ const CreateCourseForm: React.FC<CreateCourseFormProps> = () => {
|
|||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name='category_id'
|
name='category_id'
|
||||||
render={({ field: { onChange } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<AsyncSelect
|
<AsyncSelect
|
||||||
defaultOptions
|
|
||||||
cacheOptions
|
|
||||||
loadOptions={loadCategoriesOptions}
|
loadOptions={loadCategoriesOptions}
|
||||||
isSearchable={false}
|
onChange={selected => onChange(selected)}
|
||||||
onChange={selected => onChange(selected?.value)}
|
value={value}
|
||||||
placeholder='Select Category'
|
placeholder='Select Category'
|
||||||
loadingMessage={() => 'Loading...'}
|
noOptionsMessage='There are no categories.'
|
||||||
noOptionsMessage={() => 'There are no categories.'}
|
loadingText='Wait...'
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
@ -209,15 +204,14 @@ const CreateCourseForm: React.FC<CreateCourseFormProps> = () => {
|
|||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
name='teacher_id'
|
name='teacher_id'
|
||||||
render={({ field: { onChange } }) => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<AsyncSelect
|
<AsyncSelect
|
||||||
defaultOptions
|
|
||||||
cacheOptions
|
|
||||||
loadOptions={loadUsersOptions}
|
loadOptions={loadUsersOptions}
|
||||||
onChange={selected => onChange(selected?.value)}
|
onChange={selected => onChange(selected)}
|
||||||
|
value={value}
|
||||||
placeholder='Select Teacher'
|
placeholder='Select Teacher'
|
||||||
loadingMessage={() => 'Loading...'}
|
noOptionsMessage='There are no teachers.'
|
||||||
noOptionsMessage={() => 'Cannot find any teacher.'}
|
loadingText='Wait...'
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -4,7 +4,7 @@ const FILE_SIZE = 160 * 1024;
|
|||||||
const SUPPORTED_FORMATS = ['image/jpg', 'image/jpeg', 'image/gif', 'image/png'];
|
const SUPPORTED_FORMATS = ['image/jpg', 'image/jpeg', 'image/gif', 'image/png'];
|
||||||
|
|
||||||
const CreateCourseSchema = yup.object().shape({
|
const CreateCourseSchema = yup.object().shape({
|
||||||
title: yup.string().required('Title field name is required').max(60, 'Title must be at most 60 characters'),
|
title: yup.string().required('Title field name is required').max(90, 'Title must be at most 90 characters'),
|
||||||
description: yup.string().required('Description field name is required'),
|
description: yup.string().required('Description field name is required'),
|
||||||
image: yup
|
image: yup
|
||||||
.mixed()
|
.mixed()
|
||||||
|
|||||||
@ -7,22 +7,12 @@ import { observer } from 'mobx-react';
|
|||||||
/**
|
/**
|
||||||
* Main page
|
* Main page
|
||||||
*/
|
*/
|
||||||
const MainPage: React.FC<IPageProps> = ({ title }) => {
|
const MainPage: React.FC<IPageProps> = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<Box>
|
||||||
<Breadcrumb fontWeight='medium' fontSize='sm'>
|
<Heading as='h1'>Main page</Heading>
|
||||||
<BreadcrumbItem isCurrentPage>
|
</Box>
|
||||||
<BreadcrumbLink href='/'>{title}</BreadcrumbLink>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
</Breadcrumb>
|
|
||||||
<Box mt='10'>
|
|
||||||
<Box>
|
|
||||||
<Heading as='h1'>Main page</Heading>
|
|
||||||
<Grid templateColumns='3fr 1fr' mt='25px'></Grid>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default observer(MainPage);
|
export default MainPage;
|
||||||
|
|||||||
@ -6,23 +6,11 @@ import { IPageProps } from '@educt/interfaces';
|
|||||||
/**
|
/**
|
||||||
* Messages page
|
* Messages page
|
||||||
*/
|
*/
|
||||||
const MessagesPage: React.FC<IPageProps> = ({ title }) => {
|
const MessagesPage: React.FC<IPageProps> = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<Box>
|
||||||
<Breadcrumb fontWeight='medium' fontSize='sm'>
|
<Heading as='h1'>Messages page</Heading>
|
||||||
<BreadcrumbItem isCurrentPage>
|
</Box>
|
||||||
<BreadcrumbLink href='/'>{title}</BreadcrumbLink>
|
|
||||||
</BreadcrumbItem>
|
|
||||||
</Breadcrumb>
|
|
||||||
<Box mt='10'>
|
|
||||||
<Box>
|
|
||||||
<Heading as='h1'>Messages page</Heading>
|
|
||||||
<Grid templateColumns='3fr 1fr' mt='25px'>
|
|
||||||
...
|
|
||||||
</Grid>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -147,4 +147,4 @@ const ProfilePage: React.FC<IPageProps> = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default observer(ProfilePage);
|
export default ProfilePage;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user