frontend/src/pages/home/HomePage.tsx
2023-09-09 15:14:45 +03:00

215 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useCallback, useEffect, useState } from 'react';
import { SubmitHandler, useForm } from 'react-hook-form';
import { useDropzone } from 'react-dropzone';
import { useNavigate } from 'react-router-dom';
import clsx from 'clsx';
import { ReactFCC } from '../../utils/ReactFCC';
import { Heading, HeadingSize } from '../../components/Heading';
import { ETextVariants, Text } from '../../components/Text';
import { Button, ButtonSize, ButtonVariant } from '../../components/Button';
import { Divider } from '../../components/Divider';
import { Textarea } from '../../components/Textarea';
import { useCreateProcess } from '../../api/process';
import { useProcess } from '../../api/process/getProcess';
import { useSingleTimeout } from '../../hooks/useSingleTimeout';
import { Upload } from '../../components/Upload';
import { Attachment } from '../../components/Attachment';
import { Loader } from '../../components/Loader';
import { PathBuilder } from '../../app/routes';
import { ReactComponent as PlusIcon } from './assets/plus.svg';
import s from './HomePage.module.scss';
export type FormFields = {
text?: string;
files: File[];
};
export const PROCESS_POLLING_MS = 500;
export const HomePage: ReactFCC = () => {
const {
register,
handleSubmit,
watch,
setValue,
formState: { errors }
} = useForm<FormFields>({
defaultValues: {
files: []
}
});
// const [processId, setProcessId] = useState<string | null>(null);
const {
data: createProcessResponse,
mutateAsync: createProcess,
isLoading: createProcessLoading
} = useCreateProcess();
const processId = createProcessResponse?.id;
const {
data: process,
refetch: refetchProcess,
isFetching: processFetching
} = useProcess({
processId: processId || '',
config: {
enabled: !!processId
}
});
const timeout = useSingleTimeout();
const onSubmit: SubmitHandler<FormFields> = useCallback(
async (data) => {
await createProcess({
text: data.text,
files: data.files
});
},
[createProcess]
);
useEffect(() => {
if (processId) {
const startPolling = () => {
timeout.set(async () => {
const { data: process } = await refetchProcess();
if (process && process.current < process.total) {
startPolling();
}
}, PROCESS_POLLING_MS);
};
if (processId) {
startPolling();
}
}
}, [processId, refetchProcess, timeout]);
const navigate = useNavigate();
useEffect(() => {
if (processId && process && process.current === process.total) {
navigate(PathBuilder.getProcessPath(processId));
}
}, [navigate, process, processId]);
// ------ Обработка DnD ------
const currentText = watch('text');
const currentFiles = watch('files');
const onDrop = useCallback(
(acceptedFiles: File[]) => {
setValue('text', '');
setValue('files', [...currentFiles, ...acceptedFiles]);
},
[currentFiles, setValue]
);
const { getRootProps, getInputProps, isDragActive } = useDropzone({
onDrop,
noClick: true
// todo подумать насчет валидации на фронте (нужна ли?)
// accept: {
// 'application/msword': ['.doc'],
// 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'],
// 'application/vnd.ms-excel': ['.xls'],
// 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx']
// }
});
// ------ Логика UI ------
const isLoading =
createProcessLoading || !!processId || processFetching || !!(process && process.current < process.total);
const isDisabled = !currentText && currentFiles.length === 0;
return (
<div className={s.HomePage}>
{!isLoading ? (
<div className={s.HomePage__main}>
<Heading size={HeadingSize.H2} className={s.HomePage__title}>
Анализ текстовых пресс-релизов
</Heading>
<Text className={s.HomePage__text} variant={ETextVariants.BODY_M_REGULAR}>
Позволяет оценить кредитный рейтинг компании на основе пресс-релиза с выделением в тексте меток по различным
метрикам.
</Text>
<form className={s.HomePage__box} onSubmit={handleSubmit(onSubmit)}>
<div
className={clsx(s.HomePage__dropBox, {
[s.HomePage__dropBox_hidden]: isDragActive
})}
{...getRootProps()}>
{currentFiles.length === 0 ? (
<Textarea
className={s.HomePage__textarea}
classes={{
input: s.HomePage__textareaInput
}}
registration={register('text')}
rows={8}
placeholder={'Текст пресс-релиза...'}
error={!!errors.text}
/>
) : (
<div className={s.HomePage__filesContainer}>
{currentFiles.map((item, index) => (
<Attachment
file={item}
onClick={() => setValue('files', [...currentFiles.filter((i) => i !== item)])}
key={index}
/>
))}
</div>
)}
<Text className={s.HomePage__uploadHint} variant={ETextVariants.CAPTION_S_REGULAR}>
Загрузите файлы, перетащив их мышкой или нажав кнопку ниже <br />
Доступны файлы Word, Excel, PDF, TXT, изображения
</Text>
<Upload {...getInputProps()}>
<Button
component={'div'}
className={s.HomePage__uploadButton}
variant={ButtonVariant.secondary}
size={ButtonSize.small_x}>
Загрузить файлы
</Button>
</Upload>
<div className={s.HomePage__dropBoxPlaceholder}>
<PlusIcon className={s.HomePage__dropBoxPlaceholderIcon} />
</div>
</div>
<Divider className={s.HomePage__divider} />
<Button
type={'submit'}
className={s.HomePage__submitButton}
size={ButtonSize.large_x}
isLoading={isLoading}
disabled={isDisabled}>
Отправить
</Button>
</form>
</div>
) : (
<div className={s.HomePage__loaderContainer}>
<Loader className={s.HomePage__loader} />
<div className={s.HomePage__loaderText}>
{process?.current ?? 0}/{process?.total ?? 0}
</div>
</div>
)}
</div>
);
};