mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: added new field to course model
This commit is contained in:
parent
329995fcda
commit
eb1fd5d084
@ -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'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'>
|
||||||
|
|||||||
@ -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) => {
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
@ -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>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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'll learn.
|
What you'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>
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
2
src/types/index.d.ts
vendored
2
src/types/index.d.ts
vendored
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user