|
|
|
@ -1,4 +1,4 @@
|
|
|
|
|
import React, { useEffect } from 'react'; |
|
|
|
|
import React, { useEffect, useState } from 'react'; |
|
|
|
|
import { |
|
|
|
|
Spinner, |
|
|
|
|
HTMLTable, |
|
|
|
@ -9,12 +9,16 @@ import {
|
|
|
|
|
NonIdealState, |
|
|
|
|
Callout, |
|
|
|
|
Intent, |
|
|
|
|
Classes, |
|
|
|
|
Dialog, |
|
|
|
|
NumericInput, |
|
|
|
|
} from '@blueprintjs/core'; |
|
|
|
|
import { observer } from 'mobx-react'; |
|
|
|
|
import { Link, useHistory, useParams } from 'react-router-dom'; |
|
|
|
|
import { useStores } from '../hooks/useStores'; |
|
|
|
|
import { dateTime } from '../utils/dateTime'; |
|
|
|
|
import SelectCategories from './SelectCategories'; |
|
|
|
|
import ReactPaginate from 'react-paginate'; |
|
|
|
|
|
|
|
|
|
const Objects = observer(props => { |
|
|
|
|
let { catID } = useParams(); |
|
|
|
@ -29,28 +33,39 @@ const Objects = observer(props => {
|
|
|
|
|
categories, |
|
|
|
|
searchMask, |
|
|
|
|
setSearchMask, |
|
|
|
|
selectedCategories, |
|
|
|
|
setSelectedCategories, |
|
|
|
|
selectedCategories, |
|
|
|
|
count, |
|
|
|
|
} = rootStore.objectsStore; |
|
|
|
|
|
|
|
|
|
const { objectsPerPage, setObjectsPerPage } = rootStore.localStore; |
|
|
|
|
|
|
|
|
|
let history = useHistory(); |
|
|
|
|
|
|
|
|
|
let [showSettings, setShowSettings] = useState(false); |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
setSelectedCategories(catID); |
|
|
|
|
}, [catID, setSelectedCategories, categories]); |
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
if (searchMask.length > 2) { |
|
|
|
|
console.log('rerender'); |
|
|
|
|
|
|
|
|
|
selectedCategories.map(c => console.log(c.ID_CAT)); |
|
|
|
|
console.log(selectedCategories); |
|
|
|
|
console.log(JSON.stringify(selectedCategories)); |
|
|
|
|
getObjects( |
|
|
|
|
searchMask, |
|
|
|
|
categories.map(c => c.ID_CAT) |
|
|
|
|
); |
|
|
|
|
if (searchMask === '') { |
|
|
|
|
getObjects(searchMask, true); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
}, [searchMask]); |
|
|
|
|
const delayDebounceFn = setTimeout(() => { |
|
|
|
|
getObjects(searchMask, true); |
|
|
|
|
}, 300); |
|
|
|
|
|
|
|
|
|
return () => clearTimeout(delayDebounceFn); |
|
|
|
|
}, [searchMask, getObjects, selectedCategories.length]); |
|
|
|
|
|
|
|
|
|
const handlePageClick = data => { |
|
|
|
|
let selected = data.selected; |
|
|
|
|
let offset = Math.ceil(selected * 3); |
|
|
|
|
|
|
|
|
|
getObjects(searchMask, false, offset); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const typeImage = id => { |
|
|
|
|
if (types.length) { |
|
|
|
@ -59,6 +74,33 @@ const Objects = observer(props => {
|
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const settingsDialog = ( |
|
|
|
|
<Dialog |
|
|
|
|
icon="cog" |
|
|
|
|
isOpen={showSettings} |
|
|
|
|
onClose={() => { |
|
|
|
|
setShowSettings(false); |
|
|
|
|
}} |
|
|
|
|
title="Поиск объектов"> |
|
|
|
|
<div className={Classes.DIALOG_BODY}> |
|
|
|
|
<FormGroup label="Объектов на странице" labelFor="text-input"> |
|
|
|
|
<NumericInput |
|
|
|
|
placeholder="Введите число" |
|
|
|
|
value={objectsPerPage} |
|
|
|
|
onValueChange={number => setObjectsPerPage(number)} |
|
|
|
|
/> |
|
|
|
|
</FormGroup> |
|
|
|
|
</div> |
|
|
|
|
<div className={Classes.DIALOG_FOOTER}> |
|
|
|
|
<div className={Classes.DIALOG_FOOTER_ACTIONS}> |
|
|
|
|
<Button onClick={() => setShowSettings(false)} intent={Intent.PRIMARY}> |
|
|
|
|
Закрыть |
|
|
|
|
</Button> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
</Dialog> |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
const objsList = ( |
|
|
|
|
<> |
|
|
|
|
<HTMLTable interactive={true}> |
|
|
|
@ -98,12 +140,30 @@ const Objects = observer(props => {
|
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
let clearButton = <Button icon="cross" disabled={!searchMask} minimal={true} onClick={() => (searchMask = '')} />; |
|
|
|
|
let clearButton = <Button icon="cross" disabled={!searchMask} minimal={true} onClick={() => setSearchMask('')} />; |
|
|
|
|
|
|
|
|
|
let noResults = <NonIdealState icon={'search'} title={`Нет результатов по запросу «${searchMask}»`} />; |
|
|
|
|
|
|
|
|
|
let noResults = ( |
|
|
|
|
<NonIdealState |
|
|
|
|
icon={'search'} |
|
|
|
|
title={searchMask.length > 2 ? `Нет результатов по запросу "${searchMask}"` : 'Введите запрос'} |
|
|
|
|
let paginator = ( |
|
|
|
|
<ReactPaginate |
|
|
|
|
previousLabel={'<'} |
|
|
|
|
nextLabel={'>'} |
|
|
|
|
breakLabel={'...'} |
|
|
|
|
pageCount={Math.floor(count / objectsPerPage)} |
|
|
|
|
marginPagesDisplayed={2} |
|
|
|
|
pageRangeDisplayed={5} |
|
|
|
|
onPageChange={handlePageClick} |
|
|
|
|
containerClassName={'bp3-button-group'} |
|
|
|
|
subContainerClassName={'bp3-button'} |
|
|
|
|
activeClassName={'bp3-active bp3-intent-primary'} |
|
|
|
|
activeLinkClassName={'objects-search-page-active'} |
|
|
|
|
pageLinkClassName={'objects-search-page'} |
|
|
|
|
breakLinkClassName={'objects-search-dots'} |
|
|
|
|
disabledClassName={'bp3-disabled'} |
|
|
|
|
pageClassName={'bp3-button'} |
|
|
|
|
previousClassName={'bp3-button'} |
|
|
|
|
nextClassName={'bp3-button'} |
|
|
|
|
breakClassName={'bp3-button'} |
|
|
|
|
/> |
|
|
|
|
); |
|
|
|
|
|
|
|
|
@ -142,14 +202,18 @@ const Objects = observer(props => {
|
|
|
|
|
return text.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const noCat = !!catID && !!categories.length && !categories.some(c => c.ID_CAT === Number(catID)); |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
<> |
|
|
|
|
{settingsDialog} |
|
|
|
|
|
|
|
|
|
<ul class="bp3-breadcrumbs"> |
|
|
|
|
<li> |
|
|
|
|
<span className="bp3-breadcrumb bp3-breadcrumb-current">Объекты</span> |
|
|
|
|
</li> |
|
|
|
|
</ul>{' '} |
|
|
|
|
<FormGroup label={'Фильтр'} labelInfo="(минимум 3 символа)"> |
|
|
|
|
</ul> |
|
|
|
|
<FormGroup label={'Фильтр'}> |
|
|
|
|
<ControlGroup> |
|
|
|
|
<InputGroup |
|
|
|
|
style={{ width: 500 }} |
|
|
|
@ -158,6 +222,9 @@ const Objects = observer(props => {
|
|
|
|
|
rightElement={clearButton} |
|
|
|
|
onChange={e => setSearchMask(e.target.value)} |
|
|
|
|
/> |
|
|
|
|
<Button icon="cog" onClick={() => setShowSettings(true)}> |
|
|
|
|
Настройки поиска |
|
|
|
|
</Button> |
|
|
|
|
{fetching && ( |
|
|
|
|
<div style={{ marginLeft: 10, display: 'flex', alignItems: 'center' }}> |
|
|
|
|
<Spinner size={Spinner.SIZE_SMALL} /> |
|
|
|
@ -165,14 +232,42 @@ const Objects = observer(props => {
|
|
|
|
|
)} |
|
|
|
|
</ControlGroup> |
|
|
|
|
</FormGroup> |
|
|
|
|
<SelectCategories /> |
|
|
|
|
{!!catID && !!categories.length && !categories.some(c => c.ID_CAT === Number(catID)) && ( |
|
|
|
|
<SelectCategories |
|
|
|
|
callback={() => { |
|
|
|
|
getObjects(searchMask, true); |
|
|
|
|
}} |
|
|
|
|
/> |
|
|
|
|
{noCat && ( |
|
|
|
|
<FormGroup> |
|
|
|
|
<Callout intent={Intent.DANGER}>Категория {catID} не найдена!</Callout>{' '} |
|
|
|
|
<Callout intent={Intent.DANGER}>Категория {catID} не найдена!</Callout> |
|
|
|
|
</FormGroup> |
|
|
|
|
)} |
|
|
|
|
|
|
|
|
|
{objects.length === 0 || (searchMask === '' && !fetching) ? noResults : objsList} |
|
|
|
|
<div style={{ height: '100%' }}> |
|
|
|
|
{!fetching && ( |
|
|
|
|
<div style={{ height: '100%' }}> |
|
|
|
|
{objects.length !== 0 && objsList} |
|
|
|
|
{objects.length === 0 && noResults} |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
{objects.length === 0 && fetching && ( |
|
|
|
|
<div style={{ height: '100%', display: 'flex', justifyContent: 'center' }}> |
|
|
|
|
<Spinner /> |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
</div> |
|
|
|
|
{(objects.length || fetching) && ( |
|
|
|
|
<div style={{ display: 'flex', flexDirection: 'row' }}> |
|
|
|
|
<div style={{ alignSelf: 'center' }}> |
|
|
|
|
<p> |
|
|
|
|
{selectedCategories.length === categories.length ? 'В базе: ' : 'По фильтру: '} {count} |
|
|
|
|
</p> |
|
|
|
|
</div> |
|
|
|
|
<div style={{ flexGrow: 1, textAlign: 'center' }}>{count > objectsPerPage && paginator}</div> |
|
|
|
|
<div style={{ alignSelf: 'center' }}> |
|
|
|
|
<Button>Загрузить все</Button> |
|
|
|
|
</div> |
|
|
|
|
</div> |
|
|
|
|
)} |
|
|
|
|
</> |
|
|
|
|
); |
|
|
|
|
}); |
|
|
|
|