mirror of
https://github.com/magnum-opus-nn-cp/frontend.git
synced 2024-11-10 19:56:40 +03:00
feat(home): add file processing loader
This commit is contained in:
parent
b7b15fd26b
commit
acaf7d0b5e
|
@ -6,7 +6,7 @@ import { PROCESS_API_URL } from './urlKeys';
|
|||
import { QUERY_KEY_PROCESSES } from './queryKeys';
|
||||
|
||||
export type CreateProcessDTO = Partial<Pick<TextDescriptor, 'text'>> & {
|
||||
files?: [];
|
||||
files?: File[];
|
||||
};
|
||||
|
||||
export type CreateProcessResponse = {
|
||||
|
@ -14,7 +14,33 @@ export type CreateProcessResponse = {
|
|||
};
|
||||
|
||||
export const createProcess = (data: CreateProcessDTO): Promise<CreateProcessResponse> => {
|
||||
return axios.post(`${PROCESS_API_URL}/`, data);
|
||||
const isForm = data.files?.length !== 0;
|
||||
|
||||
console.log(data);
|
||||
let inputData: any;
|
||||
if (isForm) {
|
||||
inputData = new FormData();
|
||||
|
||||
if (data.text) {
|
||||
inputData.append('text', `${data.text}`);
|
||||
}
|
||||
|
||||
data.files?.forEach((file, index) => {
|
||||
inputData.append(`file_${index + 1}`, file);
|
||||
});
|
||||
} else {
|
||||
inputData = {
|
||||
text: data.text
|
||||
};
|
||||
}
|
||||
|
||||
return axios.post(`${PROCESS_API_URL}/`, inputData, {
|
||||
headers: isForm
|
||||
? {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
: {}
|
||||
});
|
||||
};
|
||||
|
||||
type UseCreateProcessOptions = {
|
||||
|
|
|
@ -5,6 +5,6 @@ export type TextDescriptor = {
|
|||
|
||||
export type ProcessDescriptor = {
|
||||
texts: TextDescriptor[];
|
||||
done: number;
|
||||
count: number;
|
||||
current: number;
|
||||
total: number;
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
.Attachment {
|
||||
@include flex-col-middle;
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
.Attachment__text {
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
@import 'src/app/styles/vars';
|
||||
|
||||
.Loader {
|
||||
--loader-size: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: var(--loader-size);
|
||||
height: var(--loader-size);
|
||||
color: $color-on-surface-dark-100;
|
||||
}
|
||||
|
||||
.LoaderIcon {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
color: $color-on-surface-dark-100;
|
||||
animation: spinner 0.7s linear infinite;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,22 +1,7 @@
|
|||
import { memo } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import s from './Loader.module.scss';
|
||||
import { ReactComponent as LoaderIcon } from '../../assets/icons/loader.svg';
|
||||
|
||||
// export enum LoaderSize {
|
||||
// /**
|
||||
// * Размер лоадера 20х20
|
||||
// */
|
||||
// small = 'small',
|
||||
// /**
|
||||
// * Размер лоадера 30х30
|
||||
// */
|
||||
// medium = 'medium',
|
||||
// /**
|
||||
// * Размер лоадера 40х40
|
||||
// */
|
||||
// large = 'large'
|
||||
// }
|
||||
import s from './Loader.module.scss';
|
||||
|
||||
export interface LoaderProps {
|
||||
className?: string;
|
||||
|
@ -27,7 +12,7 @@ export const Loader = memo((props: LoaderProps) => {
|
|||
|
||||
return (
|
||||
<div className={clsx(s.Loader, className)}>
|
||||
<LoaderIcon className={clsx(s.LoaderIcon)} />
|
||||
<LoaderIcon className={clsx(s.LoaderIcon)} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -2,13 +2,19 @@
|
|||
|
||||
.DefaultLayout {
|
||||
@include grid-for(desktop-small);
|
||||
min-height: 100vh;
|
||||
|
||||
@include tablet-down {
|
||||
@include grid-for(tablet-large);
|
||||
}
|
||||
|
||||
@include mobile-down {
|
||||
@include grid-for(mobile-small);
|
||||
}
|
||||
}
|
||||
|
||||
.DefaultLayout__container {
|
||||
@include flex-col;
|
||||
grid-column: 3 / span 8;
|
||||
|
||||
@include tablet-down {
|
||||
|
@ -17,14 +23,27 @@
|
|||
}
|
||||
|
||||
.DefaultLayout__header {
|
||||
@include flex-middle;
|
||||
column-gap: $spacing-medium;
|
||||
height: $header-height;
|
||||
display: grid;
|
||||
gap: $spacing-small-4x $spacing-medium;
|
||||
margin-bottom: $spacing-medium-x;
|
||||
|
||||
@include mobile-up {
|
||||
grid-template: 'mopus cp cbr'/ auto 1fr auto;
|
||||
height: $header-height;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@include mobile-down {
|
||||
grid-template:
|
||||
'mopus cp'
|
||||
'mopus cbr'
|
||||
/ 1fr auto;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.DefaultLayout__logo {
|
||||
width: auto;
|
||||
width: fit-content;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
|
@ -33,8 +52,33 @@
|
|||
background-color: $color-accent;
|
||||
padding: $spacing-small-2x;
|
||||
margin-right: auto;
|
||||
grid-area: mopus;
|
||||
height: $header-height;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.DefaultLayout__logo_cp {
|
||||
grid-area: cp;
|
||||
|
||||
@include mobile-up {
|
||||
justify-self: end;
|
||||
}
|
||||
|
||||
@include mobile-down {
|
||||
height: 24px;
|
||||
align-self: end;
|
||||
}
|
||||
}
|
||||
|
||||
.DefaultLayout__logo_cbr {
|
||||
height: 44px;
|
||||
grid-area: cbr;
|
||||
|
||||
@include mobile-down {
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
.DefaultLayout__content {
|
||||
height: 100%;
|
||||
}
|
|
@ -3,7 +3,9 @@
|
|||
$textarea-height: 192px;
|
||||
|
||||
.HomePage {
|
||||
@include flex-col;
|
||||
padding-bottom: $spacing-medium-x;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.HomePage__title {
|
||||
|
@ -22,6 +24,11 @@ $textarea-height: 192px;
|
|||
background-color: $color-background-dark-100;
|
||||
border-radius: $radius-large;
|
||||
padding: $spacing-medium;
|
||||
|
||||
@include mobile-down {
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.HomePage__dropBox {
|
||||
|
@ -54,8 +61,10 @@ $textarea-height: 192px;
|
|||
}
|
||||
}
|
||||
|
||||
.HomePage__button {
|
||||
margin-left: auto;
|
||||
.HomePage__submitButton {
|
||||
@include mobile-up {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.HomePage__buttonHint {
|
||||
|
@ -80,17 +89,18 @@ $textarea-height: 192px;
|
|||
border-radius: $radius-small;
|
||||
background-color: $color-background-primary;
|
||||
padding: $spacing-small-x;
|
||||
|
||||
@include mobile-down {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.HomePage__uploadContainer {
|
||||
width: fit-content;
|
||||
//padding-top: $spacing-small-3x;
|
||||
//border-top: 1px dashed $color-divider-darker;
|
||||
}
|
||||
|
||||
.HomePage__uploadButton {
|
||||
margin-right: auto;
|
||||
//margin-bottom: $spacing-small-4x;
|
||||
}
|
||||
|
||||
.HomePage__uploadHint {
|
||||
|
@ -101,6 +111,21 @@ $textarea-height: 192px;
|
|||
.HomePage__orHint {
|
||||
margin-bottom: $spacing-small-3x;
|
||||
letter-spacing: 0.06rem;
|
||||
//color: $color-text-tertiary;
|
||||
//margin-left: 70px;
|
||||
}
|
||||
|
||||
.HomePage__loaderContainer {
|
||||
@include flex-col-middle;
|
||||
justify-content: center;
|
||||
row-gap: $spacing-small-4x;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.HomePage__loader {
|
||||
--loader-size: 96px;
|
||||
color: $color-background-secondary !important;
|
||||
}
|
||||
|
||||
.HomePage__loaderText {
|
||||
@include text-programming-code-regular;
|
||||
font-size: $font-size-16;
|
||||
}
|
|
@ -13,6 +13,7 @@ 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 { ReactComponent as PlusIcon } from './assets/plus.svg';
|
||||
import s from './HomePage.module.scss';
|
||||
|
||||
|
@ -38,21 +39,26 @@ export const HomePage: ReactFCC = () => {
|
|||
|
||||
const [processId, setProcessId] = useState<string | null>(null);
|
||||
|
||||
const { data: process, refetch: refetchProcess } = useProcess({
|
||||
const {
|
||||
data: process,
|
||||
refetch: refetchProcess,
|
||||
isFetching: processFetching
|
||||
} = useProcess({
|
||||
processId: processId || '',
|
||||
config: {
|
||||
enabled: !!processId
|
||||
}
|
||||
});
|
||||
|
||||
const { mutateAsync: createProcess, isLoading } = useCreateProcess();
|
||||
const { mutateAsync: createProcess, isLoading: createProcessLoading } = useCreateProcess();
|
||||
|
||||
const timeout = useSingleTimeout();
|
||||
|
||||
const onSubmit: SubmitHandler<FormFields> = useCallback(
|
||||
async (data) => {
|
||||
const response = await createProcess({
|
||||
text: data.text
|
||||
text: data.text,
|
||||
files: data.files
|
||||
});
|
||||
|
||||
setProcessId(response.id);
|
||||
|
@ -65,7 +71,7 @@ export const HomePage: ReactFCC = () => {
|
|||
const startPolling = () => {
|
||||
timeout.set(async () => {
|
||||
const { data: process } = await refetchProcess();
|
||||
if (process && process.done < process.count) {
|
||||
if (process && process.current < process.total) {
|
||||
startPolling();
|
||||
}
|
||||
}, PROCESS_POLLING_MS);
|
||||
|
@ -77,13 +83,6 @@ export const HomePage: ReactFCC = () => {
|
|||
}
|
||||
}, [processId, refetchProcess, timeout]);
|
||||
|
||||
// todo it's mock!
|
||||
useEffect(() => {
|
||||
if (process && process.done === process.count) {
|
||||
alert(`Кредитный рейтинг ${process.texts[0].score}`);
|
||||
}
|
||||
}, [process]);
|
||||
|
||||
// ------ Обработка DnD ------
|
||||
|
||||
const currentText = watch('text');
|
||||
|
@ -110,88 +109,88 @@ export const HomePage: ReactFCC = () => {
|
|||
|
||||
// ------ Логика UI ------
|
||||
|
||||
const isLoading = createProcessLoading || processFetching || !!(process && process.current < process.total);
|
||||
const isDisabled = !currentText && currentFiles.length === 0;
|
||||
|
||||
return (
|
||||
<div className={s.HomePage}>
|
||||
<Heading size={HeadingSize.H2} className={s.HomePage__title}>
|
||||
Анализ текстовых пресс-релизов
|
||||
</Heading>
|
||||
{!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}
|
||||
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 className={s.HomePage__text} variant={ETextVariants.BODY_M_REGULAR}>
|
||||
Позволяет оценить кредитный рейтинг компании на основе пресс-релиза с выделением в тексте меток по различным
|
||||
метрикам.
|
||||
</Text>
|
||||
|
||||
<Upload {...getInputProps()}>
|
||||
<Button
|
||||
component={'div'}
|
||||
className={s.HomePage__uploadButton}
|
||||
variant={ButtonVariant.secondary}
|
||||
size={ButtonSize.small_x}>
|
||||
Загрузить файлы
|
||||
</Button>
|
||||
</Upload>
|
||||
<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}
|
||||
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>
|
||||
)}
|
||||
|
||||
<div className={s.HomePage__dropBoxPlaceholder}>
|
||||
<PlusIcon className={s.HomePage__dropBoxPlaceholderIcon} />
|
||||
<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>
|
||||
|
||||
{/*<Text className={s.HomePage__orHint} variant={ETextVariants.PROGRAMMING_CODE_MEDIUM}>*/}
|
||||
{/* ИЛИ*/}
|
||||
{/*</Text>*/}
|
||||
|
||||
{/*<Button className={s.HomePage__button} size={ButtonSize.large}>*/}
|
||||
{/* Выбрать DOCX/PDF файл*/}
|
||||
{/*</Button>*/}
|
||||
|
||||
{/*<Text className={s.HomePage__buttonHint} variant={ETextVariants.BODY_S_REGULAR}>*/}
|
||||
{/* Или перетащите файл мышкой*/}
|
||||
{/*</Text>*/}
|
||||
|
||||
<Divider className={s.HomePage__divider} />
|
||||
|
||||
<Button
|
||||
type={'submit'}
|
||||
className={s.HomePage__button}
|
||||
size={ButtonSize.large_x}
|
||||
isLoading={isLoading}
|
||||
disabled={isDisabled}>
|
||||
Отправить
|
||||
</Button>
|
||||
</form>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user