Skip to content

Commit 38d5642

Browse files
committed
web: Add search for objects list, closes #211
Signed-off-by: Mikhail Petrov <mike@nspcc.ru>
1 parent aeb6012 commit 38d5642

File tree

3 files changed

+206
-2
lines changed

3 files changed

+206
-2
lines changed

public/img/icons/info_circle.svg

Lines changed: 7 additions & 0 deletions
Loading

src/App.css

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ label.is-error {
136136
padding-left: 7px;
137137
}
138138

139+
.input_icon {
140+
position: absolute;
141+
cursor: pointer;
142+
right: -20px;
143+
top: 1px;
144+
}
145+
139146
.navbar-item,
140147
.navbar-link {
141148
padding: 0.5rem 0;
@@ -428,6 +435,30 @@ label.panel-block:hover {
428435
position: relative;
429436
}
430437

438+
.filters_block {
439+
background: #f0f5f6;
440+
border: 1px solid #dfe3e3;
441+
margin: 16px 0;
442+
padding: 20px;
443+
}
444+
445+
.filter_item {
446+
align-items: center;
447+
display: flex;
448+
justify-content: space-between;
449+
margin-top: 8px;
450+
position: relative;
451+
}
452+
453+
.filter_item .control {
454+
width: 100%;
455+
}
456+
457+
.filter_item .control.select_wrapper {
458+
max-width: 50px;
459+
margin: 0 12px;
460+
}
461+
431462
.tooltip {
432463
position: absolute;
433464
min-width: 70px;
@@ -453,6 +484,20 @@ label.panel-block:hover {
453484
border-top-color: #29363b;
454485
}
455486

487+
.tooltip_block .tooltip {
488+
width: 350px;
489+
top: -95px;
490+
display: none;
491+
}
492+
493+
.tooltip_block:hover .tooltip {
494+
display: block;
495+
}
496+
497+
.tooltip_block .tooltip:after {
498+
left: 52px;
499+
}
500+
456501
.pagination {
457502
margin: 1rem 0 0;
458503
justify-content: center;
@@ -822,4 +867,29 @@ label.panel-block:hover {
822867
padding: 10px;
823868
margin: 5px 0 0;
824869
}
870+
871+
.filter_item {
872+
flex-direction: column;
873+
border-bottom: 1px dashed #dfe3e3;
874+
margin-bottom: 12px;
875+
}
876+
877+
.filter_item:last-child {
878+
margin-bottom: 0;
879+
border-bottom: none;
880+
}
881+
882+
.filter_item .control.select_wrapper {
883+
margin: 6px 0;
884+
max-width: unset;
885+
}
886+
887+
.filter_item>img {
888+
margin: 8px 0 12px;
889+
}
890+
891+
.tooltip_block .tooltip {
892+
top: -120px;
893+
max-width: 220px;
894+
}
825895
}

src/Components/ContainerItem/ContainerItem.js

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react";
22
import {
33
Heading,
44
Section,
5+
Form,
56
Button,
67
Tile,
78
Notification,
@@ -31,6 +32,11 @@ export default function ContainerItem({
3132
page: 0,
3233
objects: 0,
3334
});
35+
const [filters, setFilters] = useState([{
36+
key: '',
37+
match: 'MatchStringEqual',
38+
value: '',
39+
}]);
3440
const [objects, setObjects] = useState(null);
3541
const [isLoadingObjects, setLoadingObjects] = useState(false);
3642
const [isLoadingEACL, setLoadingEACL] = useState(false);
@@ -84,7 +90,7 @@ export default function ContainerItem({
8490
setPagination({ ...pagination, page: pageTemp});
8591
setLoadingObjects(true);
8692
api('POST', `/objects/${containerId}/search?limit=${ObjectsPerPage}&offset=${pageTemp * ObjectsPerPage}`, {
87-
"filters": [],
93+
filters: filters.every((item) => item.key !== '' && item.value !== '') ? filters : [],
8894
}, {
8995
"Authorization": `Bearer ${walletData.tokens.object.bearer}`,
9096
}).then((e) => {
@@ -292,7 +298,7 @@ export default function ContainerItem({
292298
/>
293299
Objects
294300
</div>
295-
{activePanel === 'objects' && !isLoadingObjects && (
301+
{activePanel === 'objects' && (
296302
<Button
297303
size="small"
298304
color="primary"
@@ -307,6 +313,127 @@ export default function ContainerItem({
307313
</Heading>
308314
{activePanel === 'objects' && (
309315
<>
316+
{objects && (objects.length !== 0 || (objects.length === 0 && filters !== null)) && (
317+
<div className="filters_block">
318+
<Heading
319+
align="center"
320+
weight="normal"
321+
size={6}
322+
style={{
323+
display: 'flex',
324+
alignItems: 'center',
325+
justifyContent: 'space-between',
326+
margin: 0,
327+
}}
328+
>
329+
<span style={{ position: 'relative' }}>
330+
Filter
331+
<div className="tooltip_block">
332+
<img
333+
className="input_icon"
334+
src="/img/icons/info_circle.svg"
335+
height={18}
336+
width={18}
337+
alt="info"
338+
/>
339+
<div className="tooltip">Flexible search allows you to use any attributes to search through the attribute-operation-value formula, you can use parameters such as FilePath, FileName, etc.</div>
340+
</div>
341+
</span>
342+
<Button
343+
color="primary"
344+
size="small"
345+
onClick={() => {
346+
let filtersTemp = [ ...filters ];
347+
filtersTemp.push({
348+
key: '',
349+
match: 'MatchStringEqual',
350+
value: '',
351+
});
352+
setFilters(filtersTemp);
353+
}}
354+
disabled={isLoadingObjects}
355+
>
356+
Add filter
357+
</Button>
358+
</Heading>
359+
{filters.map((filterItem, indexFilter) => (
360+
<div className="filter_item" key={indexFilter}>
361+
<Form.Control>
362+
<Form.Input
363+
size='small'
364+
placeholder="Attribute"
365+
value={filterItem.key}
366+
onChange={(e) => {
367+
let filtersTemp = [ ...filters ];
368+
filtersTemp[indexFilter].key = e.target.value;
369+
setFilters(filtersTemp);
370+
}}
371+
disabled={isLoadingObjects}
372+
/>
373+
</Form.Control>
374+
<Form.Control className="select_wrapper">
375+
<Form.Select
376+
size='small'
377+
value={filterItem.match}
378+
onChange={(e) => {
379+
let filtersTemp = [ ...filters ];
380+
filtersTemp[indexFilter].match = e.target.value;
381+
setFilters(filtersTemp);
382+
}}
383+
disabled={isLoadingObjects}
384+
>
385+
{[
386+
{ value: 'MatchStringEqual', title: '=' },
387+
{ value: 'MatchStringNotEqual', title: '≠' },
388+
{ value: 'MatchNumGT', title: '>' },
389+
{ value: 'MatchNumGE', title: '≥' },
390+
{ value: 'MatchNumLT', title: '<' },
391+
{ value: 'MatchNumLE', title: '≤' },
392+
].map((item) => (
393+
<option value={item.value} key={item.title}>{item.title}</option>
394+
))}
395+
</Form.Select>
396+
</Form.Control>
397+
<Form.Control>
398+
<Form.Input
399+
size='small'
400+
placeholder="Value"
401+
value={filterItem.value}
402+
onChange={(e) => {
403+
let filtersTemp = [ ...filters ];
404+
filtersTemp[indexFilter].value = e.target.value;
405+
setFilters(filtersTemp);
406+
}}
407+
disabled={isLoadingObjects}
408+
/>
409+
</Form.Control>
410+
<img
411+
src="/img/icons/trashbin.svg"
412+
width={14}
413+
height={14}
414+
fill="#f14668"
415+
alt="delete"
416+
style={{ cursor: 'pointer', marginLeft: 8 }}
417+
onClick={() => {
418+
let filtersTemp = [ ...filters ];
419+
filtersTemp.splice(indexFilter, 1);
420+
setFilters(filtersTemp);
421+
}}
422+
/>
423+
</div>
424+
))}
425+
<Button
426+
fullwidth
427+
color="primary"
428+
size="small"
429+
style={{ marginTop: 8 }}
430+
onClick={() => onGetObjects(containerItem.containerId, 0)}
431+
disabled={isLoadingObjects || filters.some((item) => item.key === '' || item.value === '')}
432+
>
433+
Search
434+
</Button>
435+
</div>
436+
)}
310437
{!isLoadingObjects ? (
311438
<>
312439
{objects && objects.length === 0 && (

0 commit comments

Comments
 (0)