feat: added new field to course model

This commit is contained in:
Sergey Yarkov 2021-12-18 21:01:53 +03:00
parent 329995fcda
commit eb1fd5d084
12 changed files with 53 additions and 20 deletions

View File

@ -31,6 +31,7 @@ export type InputFields = {
image?: File | undefined; image?: File | undefined;
category_id: string; category_id: string;
description: string; description: string;
education_description: string | null;
teacher_id: string; teacher_id: string;
}; };
@ -84,6 +85,7 @@ const CourseForm: React.FC<CourseFormPropsType> = ({ onSubmit, buttonLabel, isLo
const watchTitle = watch('title'); const watchTitle = watch('title');
const watchDescription = watch('description'); const watchDescription = watch('description');
const watchEducationDescription = watch('education_description');
return ( return (
<form onSubmit={onSubmit}> <form onSubmit={onSubmit}>
@ -117,6 +119,26 @@ const CourseForm: React.FC<CourseFormPropsType> = ({ onSubmit, buttonLabel, isLo
</Flex> </Flex>
</FormControl> </FormControl>
<FormControl id='education_description' isInvalid={!!errors.education_description}>
<FormLabel>What you&apos;ll learn section</FormLabel>
<Textarea resize='none' minH='80px' {...register('education_description')} />
<Flex justifyContent='space-between'>
<Text as='small' color='red.500'>
{errors.education_description?.message}
</Text>
{watchEducationDescription && (
<Text
as='small'
color={
!!errors.education_description || watchEducationDescription.length > 250 ? 'red.500' : 'gray.500'
}
>
{watchEducationDescription.length || 0}/250
</Text>
)}
</Flex>
</FormControl>
<Flex justifyContent='space-between' flexDirection={{ base: 'column', lg: 'row' }}> <Flex justifyContent='space-between' flexDirection={{ base: 'column', lg: 'row' }}>
<Box flexGrow={1} flexBasis='0' mr={{ base: '0', lg: '7' }}> <Box flexGrow={1} flexBasis='0' mr={{ base: '0', lg: '7' }}>
<Stack spacing='5'> <Stack spacing='5'>

View File

@ -9,6 +9,7 @@ const CreateCourseSchema = yup.object().shape({
.string() .string()
.required('Description field name is required') .required('Description field name is required')
.max(250, 'Description must be at most 250 characters'), .max(250, 'Description must be at most 250 characters'),
education_description: yup.string().max(250, 'This section must be at most 250 characters').nullable(),
image: yup image: yup
.mixed() .mixed()
.test('fileSize', 'File size is too large', (value: File) => { .test('fileSize', 'File size is too large', (value: File) => {

View File

@ -39,6 +39,7 @@ const CreateFormCourse: React.FC = () => {
const course = await createCourse({ const course = await createCourse({
title: data.title, title: data.title,
description: data.description, description: data.description,
education_description: data.education_description,
teacher_id: data.teacher_id, teacher_id: data.teacher_id,
category_id: data.category_id, category_id: data.category_id,
image: data.image, image: data.image,

View File

@ -32,6 +32,7 @@ type EditFormCoursePropsType = {
image?: File | undefined; image?: File | undefined;
category_id?: string | undefined; category_id?: string | undefined;
description?: string | undefined; description?: string | undefined;
education_description?: string | null | undefined;
teacher_id?: string | undefined; teacher_id?: string | undefined;
} }
| undefined; | undefined;
@ -43,6 +44,7 @@ const EditFormCourse: React.FC<EditFormCoursePropsType> = ({ defaultValues }) =>
defaultValues: { defaultValues: {
title: defaultValues?.title || '', title: defaultValues?.title || '',
description: defaultValues?.description || '', description: defaultValues?.description || '',
education_description: defaultValues?.education_description || '',
category_id: defaultValues?.category_id || '', category_id: defaultValues?.category_id || '',
teacher_id: defaultValues?.teacher_id || '', teacher_id: defaultValues?.teacher_id || '',
image: undefined, image: undefined,
@ -56,16 +58,17 @@ const EditFormCourse: React.FC<EditFormCoursePropsType> = ({ defaultValues }) =>
*/ */
const onSubmit: SubmitHandler<InputFields> = async data => { const onSubmit: SubmitHandler<InputFields> = async data => {
try { try {
const updated = await updateCourse(id, data); const updatedCourse = await updateCourse(id, data);
/** /**
* Update fields with new values * Update fields with new values
*/ */
form.reset({ form.reset({
title: updated.title, title: updatedCourse.title,
description: updated.description, description: updatedCourse.description,
teacher_id: updated.teacher.id, education_description: updatedCourse.education_description,
category_id: updated.category.id, teacher_id: updatedCourse.teacher.id,
category_id: updatedCourse.category.id,
image: undefined, image: undefined,
}); });
} catch (error) { } catch (error) {

View File

@ -1,9 +1,9 @@
import React from 'react'; import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { Link } from 'react-router-dom'; import { Link as ReactRouterLink } from 'react-router-dom';
import { IconButton, Skeleton } from '@chakra-ui/react'; import { IconButton, Skeleton } from '@chakra-ui/react';
import { Avatar } from '@chakra-ui/avatar'; import { Avatar } from '@chakra-ui/avatar';
import { Flex, Box, Text } from '@chakra-ui/layout'; import { Flex, Box, Text, Link } from '@chakra-ui/layout';
import { Drawer, DrawerOverlay, DrawerContent, DrawerBody } from '@chakra-ui/modal'; import { Drawer, DrawerOverlay, DrawerContent, DrawerBody } from '@chakra-ui/modal';
import { MdExitToApp } from 'react-icons/md'; import { MdExitToApp } from 'react-icons/md';
import { HamburgerIcon } from '@chakra-ui/icons'; import { HamburgerIcon } from '@chakra-ui/icons';
@ -55,20 +55,20 @@ const NavMobile: React.FC = () => {
<Flex alignItems='center'> <Flex alignItems='center'>
<Avatar size='sm' name={me.fullname} marginRight={3} /> <Avatar size='sm' name={me.fullname} marginRight={3} />
<Flex flexDirection='column'> <Flex flexDirection='column'>
<Text as='span' mr={2} lineHeight='1.2'> <Text as='span' fontWeight={'semibold'} fontSize={'sm'} mr={2} lineHeight='1.2'>
{me.fullname} {me.fullname}
</Text> </Text>
<Link to={`/profile`} style={{ fontSize: '13px', textDecoration: 'underline' }} onClick={onClose}> <Link
as={ReactRouterLink}
fontSize={'sm'}
to={`/profile`}
style={{ fontSize: '13px', textDecoration: 'underline' }}
onClick={onClose}
>
View profile View profile
</Link> </Link>
</Flex> </Flex>
<IconButton <IconButton onClick={logout} aria-label='Logout' marginLeft='auto' icon={<MdExitToApp />} />
onClick={logout}
aria-label='Logout'
backgroundColor={colorMode === 'dark' ? 'gray.600' : 'gray.200'}
marginLeft='auto'
icon={<MdExitToApp />}
/>
</Flex> </Flex>
</Box> </Box>
) : ( ) : (

View File

@ -31,6 +31,7 @@ export function userContainRoles(userRoles: IUserRole[], roles: UserRoleEnum[]):
* @returns Clean object that don't have null values * @returns Clean object that don't have null values
*/ */
export function removeEmptyValues(obj: object) { export function removeEmptyValues(obj: object) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== null)); return Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== null));
} }
@ -47,7 +48,7 @@ export function transformToFormData(data: object): FormData {
* Append value to form-data format * Append value to form-data format
*/ */
Object.entries(data).forEach(([key, value]) => { Object.entries(data).forEach(([key, value]) => {
if (value) { if (value !== undefined) {
if (Array.isArray(value)) { if (Array.isArray(value)) {
value.forEach((v, i) => formData.append(`${key}[${i}]`, v)); value.forEach((v, i) => formData.append(`${key}[${i}]`, v));
return; return;

View File

@ -17,6 +17,7 @@ const useCreateCourse = () => {
const result = await CourseServiceInstance.create({ const result = await CourseServiceInstance.create({
title: data.title, title: data.title,
description: data.description, description: data.description,
education_description: data.education_description,
teacher_id: data.teacher_id, teacher_id: data.teacher_id,
category_id: data.category_id, category_id: data.category_id,
image: data.image, image: data.image,

View File

@ -16,6 +16,7 @@ const useUpdateCourse = () => {
const result = await CourseServiceInstance.update(id, { const result = await CourseServiceInstance.update(id, {
title: data.title, title: data.title,
description: data.description, description: data.description,
education_description: data.education_description,
teacher_id: data.teacher_id, teacher_id: data.teacher_id,
category_id: data.category_id, category_id: data.category_id,
image: data.image, image: data.image,

View File

@ -23,6 +23,7 @@ export interface ICourse {
image: AttachmentFileType | null; image: AttachmentFileType | null;
title: string; title: string;
description: string; description: string;
education_description: string | null;
status: CourseStatusEnum; status: CourseStatusEnum;
teacher: Pick<IUser, 'id' | 'first_name' | 'fullname' | 'last_name' | 'email' | 'created_at' | 'updated_at'>; teacher: Pick<IUser, 'id' | 'first_name' | 'fullname' | 'last_name' | 'email' | 'created_at' | 'updated_at'>;
category: ICategory; category: ICategory;

View File

@ -114,14 +114,13 @@ const CoursePage: React.FC<IPageProps> = () => {
<Divider mt='5' /> <Divider mt='5' />
</GridItem> </GridItem>
<GridItem borderRadius={'lg'} minWidth={'290px'} h='550px'> <GridItem borderRadius={'lg'} minWidth={'290px'} h='550px' p={{ base: '4', xl: '0px' }}>
<Box> <Box>
<Text fontSize={'2xl'} fontWeight={'medium'}> <Text fontSize={'2xl'} fontWeight={'medium'}>
What you&apos;ll learn. What you&apos;ll learn.
</Text> </Text>
<Text fontSize={'sm'} mt='2'> <Text fontSize={'sm'} mt='2'>
Lorem, ipsum dolor sit amet consectetur adipisicing elit. Iste temporibus harum eos praesentium hic, {course.education_description}
sunt corporis deserunt impedit natus consequuntur?
</Text> </Text>
</Box> </Box>

View File

@ -75,6 +75,7 @@ const EditCoursePage: React.FC<IPageProps> = () => {
defaultValues={{ defaultValues={{
title: course.title, title: course.title,
description: course.description, description: course.description,
education_description: course.education_description,
category_id: course.category.id, category_id: course.category.id,
teacher_id: course.teacher.id, teacher_id: course.teacher.id,
image: undefined, image: undefined,

View File

@ -127,6 +127,7 @@ export type UpdateUserContactsParamsType = {
export type CreateCourseParamsType = { export type CreateCourseParamsType = {
title: string; title: string;
description: string; description: string;
education_description: string | null;
teacher_id: string; teacher_id: string;
category_id: string; category_id: string;
image?: File | undefined; image?: File | undefined;
@ -136,6 +137,7 @@ export type CreateCourseParamsType = {
export type UpdateCourseParamsType = { export type UpdateCourseParamsType = {
title?: string | undefined; title?: string | undefined;
description?: string | undefined; description?: string | undefined;
education_description?: string | null | undefined;
teacher_id?: string | undefined; teacher_id?: string | undefined;
category_id?: string | undefined; category_id?: string | undefined;
image?: File | undefined; image?: File | undefined;