mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: added materials uploader
This commit is contained in:
parent
359deec3bc
commit
03c8e3b8cc
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import * as helpres from '@educt/helpers';
|
import * as helpres from '@educt/helpers';
|
||||||
import { Stack, Flex, Box, Icon, Text, Input, IconButton, Button } from '@chakra-ui/react';
|
import { Stack, Flex, Box, Icon, Text, Input, IconButton, Button } from '@chakra-ui/react';
|
||||||
import { MdOutlineFilePresent } from 'react-icons/md';
|
import { MdOutlineFilePresent } from 'react-icons/md';
|
||||||
@ -12,12 +12,11 @@ import { FileSupportedFormatsEnum } from '@educt/enums';
|
|||||||
|
|
||||||
type FilesUploaderPropsType = {
|
type FilesUploaderPropsType = {
|
||||||
onChange: (files: File[]) => void;
|
onChange: (files: File[]) => void;
|
||||||
files?: Array<File> | undefined;
|
files?: File[] | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const FilesUploader: React.FC<FilesUploaderPropsType> = ({ onChange, files }) => {
|
const FilesUploader: React.FC<FilesUploaderPropsType> = ({ onChange, files }) => {
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const [selectedFiles, setSelectedFiles] = useState<Array<File>>([]);
|
|
||||||
|
|
||||||
const onSelectFileHandler = () => {
|
const onSelectFileHandler = () => {
|
||||||
if (fileInputRef.current) {
|
if (fileInputRef.current) {
|
||||||
@ -28,12 +27,9 @@ const FilesUploader: React.FC<FilesUploaderPropsType> = ({ onChange, files }) =>
|
|||||||
/**
|
/**
|
||||||
* Detach file
|
* Detach file
|
||||||
*/
|
*/
|
||||||
const onRemoveSelectedFileHandler = (selectedFile: File) => () =>
|
const onRemoveSelectedFileHandler = (selectedFile: File) => () => {
|
||||||
setSelectedFiles(selectedFiles => {
|
onChange([...Array.from(files?.filter(file => file.name !== selectedFile.name) || [])]);
|
||||||
const updated = selectedFiles.filter(file => file.name !== selectedFile.name);
|
};
|
||||||
onChange(updated);
|
|
||||||
return updated;
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach file
|
* Attach file
|
||||||
@ -41,20 +37,13 @@ const FilesUploader: React.FC<FilesUploaderPropsType> = ({ onChange, files }) =>
|
|||||||
const onChangeFileHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const onChangeFileHandler = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const { files } = e.target;
|
if (e.target.files && e.target.files.length !== 0) {
|
||||||
|
const isAlreadySelected = (files || []).find(selectedFile =>
|
||||||
if (files && files.length !== 0) {
|
Array.from(e.target.files || []).some(file => file.name === selectedFile.name)
|
||||||
const isAlreadySelected = selectedFiles.find(selectedFile =>
|
|
||||||
Array.from(files).some(file => file.name === selectedFile.name)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isAlreadySelected) return;
|
if (isAlreadySelected) return;
|
||||||
|
onChange([...Array.from(files || []), ...e.target.files]);
|
||||||
setSelectedFiles(selectedFiles => {
|
|
||||||
const updated = [...selectedFiles, ...Array.from(files)];
|
|
||||||
onChange(updated);
|
|
||||||
return updated;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -72,7 +61,7 @@ const FilesUploader: React.FC<FilesUploaderPropsType> = ({ onChange, files }) =>
|
|||||||
/>
|
/>
|
||||||
{files && (
|
{files && (
|
||||||
<Stack>
|
<Stack>
|
||||||
{selectedFiles.map((file, index) => (
|
{files.map((file, index) => (
|
||||||
<Flex
|
<Flex
|
||||||
key={index}
|
key={index}
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
|
|||||||
@ -37,7 +37,6 @@ const LessonForm: React.FC<LessonFormPropsType> = ({ onSubmit, buttonLabel, isLo
|
|||||||
const {
|
const {
|
||||||
control,
|
control,
|
||||||
register,
|
register,
|
||||||
watch,
|
|
||||||
formState: { errors, isDirty },
|
formState: { errors, isDirty },
|
||||||
} = reactHookForm;
|
} = reactHookForm;
|
||||||
|
|
||||||
@ -104,7 +103,7 @@ const LessonForm: React.FC<LessonFormPropsType> = ({ onSubmit, buttonLabel, isLo
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
<Text as='small' color='red.500'>
|
<Text as='small' color='red.500'>
|
||||||
{errors.materials?.map(m => m.message)}
|
{errors.materials?.message}
|
||||||
</Text>
|
</Text>
|
||||||
<FormHelperText>
|
<FormHelperText>
|
||||||
Supported formats:{' '}
|
Supported formats:{' '}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { VideoSupportedFormatsEnum } from '@educt/enums';
|
import * as helpers from '@educt/helpers';
|
||||||
import yup from '@educt/schema';
|
import yup from '@educt/schema';
|
||||||
|
|
||||||
const VIDEO_FILE_SIZE = 5000 * 1024 * 1024;
|
const VIDEO_FILE_SIZE = 5000 * 1024 * 1024;
|
||||||
const MATERIAL_FILE_SIZE = 10000 * 1024;
|
const MATERIAL_FILE_SIZE = 100000 * 1024;
|
||||||
|
|
||||||
const LessonFormSchema = yup.object().shape({
|
const LessonFormSchema = yup.object().shape({
|
||||||
title: yup.string().required('Title field name is required').max(90, 'Title must be at most 90 characters'),
|
title: yup.string().required('Title field name is required').max(90, 'Title must be at most 90 characters'),
|
||||||
@ -13,25 +13,26 @@ const LessonFormSchema = yup.object().shape({
|
|||||||
duration: yup.string().required('Duration field name is required'),
|
duration: yup.string().required('Duration field name is required'),
|
||||||
video: yup
|
video: yup
|
||||||
.mixed()
|
.mixed()
|
||||||
.test('fileSize', 'File size is too large', (value: File) => {
|
.test(
|
||||||
|
'fileSize',
|
||||||
|
`File size is too large, maximum size is ${helpers.transformBytes(VIDEO_FILE_SIZE)}`,
|
||||||
|
(value: File) => {
|
||||||
if (!value) return true;
|
if (!value) return true;
|
||||||
return value && value.size <= VIDEO_FILE_SIZE;
|
return value && value.size <= VIDEO_FILE_SIZE;
|
||||||
})
|
}
|
||||||
.test('fileFormat', 'This file format is not supported', (value: File) => {
|
)
|
||||||
if (!value) return true;
|
|
||||||
return value && Object.values(VideoSupportedFormatsEnum).includes(value.type as VideoSupportedFormatsEnum);
|
|
||||||
})
|
|
||||||
.required('Video field name is required'),
|
.required('Video field name is required'),
|
||||||
// materials: yup
|
materials: yup
|
||||||
// .mixed()
|
.mixed()
|
||||||
// .test('fileSize', 'File size is too large', (value: File) => {
|
.test(
|
||||||
// if (!value) return true;
|
'fileSize',
|
||||||
// return value && value.size <= VIDEO_FILE_SIZE;
|
`File size is too large, maximum size is ${helpers.transformBytes(MATERIAL_FILE_SIZE)}`,
|
||||||
// })
|
(values: File[]) => {
|
||||||
// .test('fileFormat', 'This file format is not supported', (value: File) => {
|
if (!values) return true;
|
||||||
// if (!value) return true;
|
return values && values.every(value => value.size <= MATERIAL_FILE_SIZE);
|
||||||
// return value && VIDEO_SUPPORTED_FORMATS.includes(value.type);
|
}
|
||||||
// }),
|
)
|
||||||
|
.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default LessonFormSchema;
|
export default LessonFormSchema;
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export enum VideoSupportedFormatsEnum {
|
|||||||
|
|
||||||
export enum FileSupportedFormatsEnum {
|
export enum FileSupportedFormatsEnum {
|
||||||
PDF = 'application/pdf',
|
PDF = 'application/pdf',
|
||||||
ZIP = 'application/zip',
|
ZIP = 'application/x-zip-compressed',
|
||||||
RAR = 'application/x-rar-compressed',
|
RAR = 'application/x-rar-compressed',
|
||||||
DOC = 'application/msword',
|
DOC = 'application/msword',
|
||||||
DOCX = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
DOCX = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user