feat(home): add file processing loader

This commit is contained in:
Pavel Torbeev 2023-09-09 01:37:46 +03:00
parent b7b15fd26b
commit acaf7d0b5e
8 changed files with 199 additions and 118 deletions

View File

@ -6,7 +6,7 @@ import { PROCESS_API_URL } from './urlKeys';
import { QUERY_KEY_PROCESSES } from './queryKeys'; import { QUERY_KEY_PROCESSES } from './queryKeys';
export type CreateProcessDTO = Partial<Pick<TextDescriptor, 'text'>> & { export type CreateProcessDTO = Partial<Pick<TextDescriptor, 'text'>> & {
files?: []; files?: File[];
}; };
export type CreateProcessResponse = { export type CreateProcessResponse = {
@ -14,7 +14,33 @@ export type CreateProcessResponse = {
}; };
export const createProcess = (data: CreateProcessDTO): Promise<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 = { type UseCreateProcessOptions = {

View File

@ -5,6 +5,6 @@ export type TextDescriptor = {
export type ProcessDescriptor = { export type ProcessDescriptor = {
texts: TextDescriptor[]; texts: TextDescriptor[];
done: number; current: number;
count: number; total: number;
}; };

View File

@ -3,6 +3,7 @@
.Attachment { .Attachment {
@include flex-col-middle; @include flex-col-middle;
cursor: pointer; cursor: pointer;
width: 100px;
} }
.Attachment__text { .Attachment__text {

View File

@ -1,16 +1,17 @@
@import 'src/app/styles/vars'; @import 'src/app/styles/vars';
.Loader { .Loader {
--loader-size: 20px;
display: flex; display: flex;
align-items: center; align-items: center;
width: 20px; width: var(--loader-size);
height: 20px; height: var(--loader-size);
color: $color-on-surface-dark-100;
} }
.LoaderIcon { .LoaderIcon {
width: 100%; width: 100%;
height: 100%; height: 100%;
color: $color-on-surface-dark-100;
animation: spinner 0.7s linear infinite; animation: spinner 0.7s linear infinite;
} }

View File

@ -1,22 +1,7 @@
import { memo } from 'react'; import { memo } from 'react';
import clsx from 'clsx'; import clsx from 'clsx';
import s from './Loader.module.scss';
import { ReactComponent as LoaderIcon } from '../../assets/icons/loader.svg'; import { ReactComponent as LoaderIcon } from '../../assets/icons/loader.svg';
import s from './Loader.module.scss';
// export enum LoaderSize {
// /**
// * Размер лоадера 20х20
// */
// small = 'small',
// /**
// * Размер лоадера 30х30
// */
// medium = 'medium',
// /**
// * Размер лоадера 40х40
// */
// large = 'large'
// }
export interface LoaderProps { export interface LoaderProps {
className?: string; className?: string;

View File

@ -2,13 +2,19 @@
.DefaultLayout { .DefaultLayout {
@include grid-for(desktop-small); @include grid-for(desktop-small);
min-height: 100vh;
@include tablet-down { @include tablet-down {
@include grid-for(tablet-large); @include grid-for(tablet-large);
} }
@include mobile-down {
@include grid-for(mobile-small);
}
} }
.DefaultLayout__container { .DefaultLayout__container {
@include flex-col;
grid-column: 3 / span 8; grid-column: 3 / span 8;
@include tablet-down { @include tablet-down {
@ -17,14 +23,27 @@
} }
.DefaultLayout__header { .DefaultLayout__header {
@include flex-middle; display: grid;
column-gap: $spacing-medium; gap: $spacing-small-4x $spacing-medium;
height: $header-height;
margin-bottom: $spacing-medium-x; 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 { .DefaultLayout__logo {
width: auto; width: fit-content;
height: 40px; height: 40px;
} }
@ -33,8 +52,33 @@
background-color: $color-accent; background-color: $color-accent;
padding: $spacing-small-2x; padding: $spacing-small-2x;
margin-right: auto; 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 { .DefaultLayout__logo_cbr {
height: 44px; height: 44px;
grid-area: cbr;
@include mobile-down {
height: 28px;
}
}
.DefaultLayout__content {
height: 100%;
} }

View File

@ -3,7 +3,9 @@
$textarea-height: 192px; $textarea-height: 192px;
.HomePage { .HomePage {
@include flex-col;
padding-bottom: $spacing-medium-x; padding-bottom: $spacing-medium-x;
height: 100%;
} }
.HomePage__title { .HomePage__title {
@ -22,6 +24,11 @@ $textarea-height: 192px;
background-color: $color-background-dark-100; background-color: $color-background-dark-100;
border-radius: $radius-large; border-radius: $radius-large;
padding: $spacing-medium; padding: $spacing-medium;
@include mobile-down {
padding: 0;
background-color: transparent;
}
} }
.HomePage__dropBox { .HomePage__dropBox {
@ -54,9 +61,11 @@ $textarea-height: 192px;
} }
} }
.HomePage__button { .HomePage__submitButton {
@include mobile-up {
margin-left: auto; margin-left: auto;
} }
}
.HomePage__buttonHint { .HomePage__buttonHint {
color: $color-text-tertiary; color: $color-text-tertiary;
@ -80,17 +89,18 @@ $textarea-height: 192px;
border-radius: $radius-small; border-radius: $radius-small;
background-color: $color-background-primary; background-color: $color-background-primary;
padding: $spacing-small-x; padding: $spacing-small-x;
@include mobile-down {
justify-content: center;
}
} }
.HomePage__uploadContainer { .HomePage__uploadContainer {
width: fit-content; width: fit-content;
//padding-top: $spacing-small-3x;
//border-top: 1px dashed $color-divider-darker;
} }
.HomePage__uploadButton { .HomePage__uploadButton {
margin-right: auto; margin-right: auto;
//margin-bottom: $spacing-small-4x;
} }
.HomePage__uploadHint { .HomePage__uploadHint {
@ -101,6 +111,21 @@ $textarea-height: 192px;
.HomePage__orHint { .HomePage__orHint {
margin-bottom: $spacing-small-3x; margin-bottom: $spacing-small-3x;
letter-spacing: 0.06rem; 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;
} }

View File

@ -13,6 +13,7 @@ import { useProcess } from '../../api/process/getProcess';
import { useSingleTimeout } from '../../hooks/useSingleTimeout'; import { useSingleTimeout } from '../../hooks/useSingleTimeout';
import { Upload } from '../../components/Upload'; import { Upload } from '../../components/Upload';
import { Attachment } from '../../components/Attachment'; import { Attachment } from '../../components/Attachment';
import { Loader } from '../../components/Loader';
import { ReactComponent as PlusIcon } from './assets/plus.svg'; import { ReactComponent as PlusIcon } from './assets/plus.svg';
import s from './HomePage.module.scss'; import s from './HomePage.module.scss';
@ -38,21 +39,26 @@ export const HomePage: ReactFCC = () => {
const [processId, setProcessId] = useState<string | null>(null); const [processId, setProcessId] = useState<string | null>(null);
const { data: process, refetch: refetchProcess } = useProcess({ const {
data: process,
refetch: refetchProcess,
isFetching: processFetching
} = useProcess({
processId: processId || '', processId: processId || '',
config: { config: {
enabled: !!processId enabled: !!processId
} }
}); });
const { mutateAsync: createProcess, isLoading } = useCreateProcess(); const { mutateAsync: createProcess, isLoading: createProcessLoading } = useCreateProcess();
const timeout = useSingleTimeout(); const timeout = useSingleTimeout();
const onSubmit: SubmitHandler<FormFields> = useCallback( const onSubmit: SubmitHandler<FormFields> = useCallback(
async (data) => { async (data) => {
const response = await createProcess({ const response = await createProcess({
text: data.text text: data.text,
files: data.files
}); });
setProcessId(response.id); setProcessId(response.id);
@ -65,7 +71,7 @@ export const HomePage: ReactFCC = () => {
const startPolling = () => { const startPolling = () => {
timeout.set(async () => { timeout.set(async () => {
const { data: process } = await refetchProcess(); const { data: process } = await refetchProcess();
if (process && process.done < process.count) { if (process && process.current < process.total) {
startPolling(); startPolling();
} }
}, PROCESS_POLLING_MS); }, PROCESS_POLLING_MS);
@ -77,13 +83,6 @@ export const HomePage: ReactFCC = () => {
} }
}, [processId, refetchProcess, timeout]); }, [processId, refetchProcess, timeout]);
// todo it's mock!
useEffect(() => {
if (process && process.done === process.count) {
alert(`Кредитный рейтинг ${process.texts[0].score}`);
}
}, [process]);
// ------ Обработка DnD ------ // ------ Обработка DnD ------
const currentText = watch('text'); const currentText = watch('text');
@ -110,10 +109,13 @@ export const HomePage: ReactFCC = () => {
// ------ Логика UI ------ // ------ Логика UI ------
const isLoading = createProcessLoading || processFetching || !!(process && process.current < process.total);
const isDisabled = !currentText && currentFiles.length === 0; const isDisabled = !currentText && currentFiles.length === 0;
return ( return (
<div className={s.HomePage}> <div className={s.HomePage}>
{!isLoading ? (
<div className={s.HomePage__main}>
<Heading size={HeadingSize.H2} className={s.HomePage__title}> <Heading size={HeadingSize.H2} className={s.HomePage__title}>
Анализ текстовых пресс-релизов Анализ текстовых пресс-релизов
</Heading> </Heading>
@ -169,23 +171,11 @@ export const HomePage: ReactFCC = () => {
</div> </div>
</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} /> <Divider className={s.HomePage__divider} />
<Button <Button
type={'submit'} type={'submit'}
className={s.HomePage__button} className={s.HomePage__submitButton}
size={ButtonSize.large_x} size={ButtonSize.large_x}
isLoading={isLoading} isLoading={isLoading}
disabled={isDisabled}> disabled={isDisabled}>
@ -193,5 +183,14 @@ export const HomePage: ReactFCC = () => {
</Button> </Button>
</form> </form>
</div> </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>
); );
}; };