Feature: add connections filter

This commit is contained in:
Dreamacro 2020-11-03 21:51:03 +08:00
parent 9f602e23a4
commit d5fa59f477
6 changed files with 89 additions and 8 deletions

View File

@ -0,0 +1,35 @@
import React from 'react'
import classnames from 'classnames'
import { BaseComponentProps } from '@models'
import './style.scss'
interface DevicesProps extends BaseComponentProps {
devices: Array<{ label: string, number: number }>
selected: string
onChange?: (label: string) => void
}
export function Devices (props: DevicesProps) {
const { className, style } = props
const classname = classnames('connections-devices', className)
function handleSelected (label: string) {
props.onChange?.(label)
}
return (
<div className={classname} style={style}>
<div className={classnames('connections-devices-item', { selected: props.selected === '' })} onClick={() => handleSelected('')}></div>
{
props.devices.map(
device => (
<div
className={classnames('connections-devices-item', { selected: props.selected === device.label })}
onClick={() => handleSelected(device.label)}>
{ device.label } ({ device.number })
</div>
)
)
}
</div>
)
}

View File

@ -0,0 +1,21 @@
@import '~@styles/variables';
.connections-devices {
display: flex;
padding: 12px 0;
}
.connections-devices-item {
padding: 4px 10px;
margin-right: 12px;
font-size: 14px;
color: $color-gray-darken;
background-color: $color-gray-light;
border-radius: 3px;
cursor: pointer;
transition: color .3s ease;
&.selected {
color: $color-primary-dark;
}
}

View File

@ -1,7 +1,8 @@
import React, { useMemo, useLayoutEffect, useCallback, useRef } from 'react' import React, { useMemo, useLayoutEffect, useCallback, useRef, useState } from 'react'
import { Cell, Column, ColumnInstance, TableOptions, useBlockLayout, useResizeColumns, UseResizeColumnsColumnProps, UseResizeColumnsOptions, useSortBy, UseSortByColumnOptions, UseSortByColumnProps, UseSortByOptions, useTable } from 'react-table' import { Cell, Column, ColumnInstance, TableInstance, TableOptions, useBlockLayout, useFilters, UseFiltersInstanceProps, UseFiltersOptions, useResizeColumns, UseResizeColumnsColumnProps, UseResizeColumnsOptions, useSortBy, UseSortByColumnOptions, UseSortByColumnProps, UseSortByOptions, useTable } from 'react-table'
import classnames from 'classnames' import classnames from 'classnames'
import { useScroll } from 'react-use' import { useScroll } from 'react-use'
import { groupBy } from 'lodash'
import { Header, Card, Checkbox, Modal, Icon } from '@components' import { Header, Card, Checkbox, Modal, Icon } from '@components'
import { useI18n } from '@stores' import { useI18n } from '@stores'
import * as API from '@lib/request' import * as API from '@lib/request'
@ -9,6 +10,7 @@ import { StreamReader } from '@lib/streamer'
import { useObject, useVisible } from '@lib/hook' import { useObject, useVisible } from '@lib/hook'
import { fromNow } from '@lib/date' import { fromNow } from '@lib/date'
import { RuleType } from '@models' import { RuleType } from '@models'
import { Devices } from './Devices'
import { useConnections } from './store' import { useConnections } from './store'
import './style.scss' import './style.scss'
@ -39,7 +41,12 @@ type TableColumnOption<D extends object = {}> =
interface ITableOptions<D extends object = {}> extends interface ITableOptions<D extends object = {}> extends
TableOptions<D>, TableOptions<D>,
UseSortByOptions<D> {} UseSortByOptions<D>,
UseFiltersOptions<D> {}
interface ITableInstance<D extends object = {}> extends
TableInstance<D>,
UseFiltersInstanceProps<D> {}
function formatTraffic(num: number) { function formatTraffic(num: number) {
const s = ['B', 'KB', 'MB', 'GB', 'TB'] const s = ['B', 'KB', 'MB', 'GB', 'TB']
@ -117,6 +124,10 @@ export default function Connections() {
completed: !!c.completed completed: !!c.completed
}) })
), [connections]) ), [connections])
const devices = useMemo(() => {
const gb = groupBy(connections, 'metadata.sourceIP')
return Object.keys(gb).map(key => ({ label: key, number: gb[key].length }))
}, [connections])
// table // table
const tableRef = useRef<HTMLDivElement>(null) const tableRef = useRef<HTMLDivElement>(null)
@ -181,18 +192,21 @@ export default function Connections() {
getTableBodyProps, getTableBodyProps,
headerGroups, headerGroups,
rows, rows,
prepareRow prepareRow,
setFilter
} = useTable( } = useTable(
{ {
columns, columns,
data, data,
autoResetSortBy: false, autoResetSortBy: false,
autoResetFilters: false,
initialState: { sortBy: [{ id: Columns.Time, desc: false }] } initialState: { sortBy: [{ id: Columns.Time, desc: false }] }
} as ITableOptions<formatConnection>, } as ITableOptions<formatConnection>,
useResizeColumns, useResizeColumns,
useBlockLayout, useBlockLayout,
useFilters,
useSortBy useSortBy
) ) as ITableInstance<formatConnection>
const headerGroup = useMemo(() => headerGroups[0], [headerGroups]) const headerGroup = useMemo(() => headerGroups[0], [headerGroups])
const renderCell = useCallback(function (cell: Cell<formatConnection>) { const renderCell = useCallback(function (cell: Cell<formatConnection>) {
switch (cell.column.id) { switch (cell.column.id) {
@ -208,6 +222,13 @@ export default function Connections() {
} }
}, [lang]) }, [lang])
// filter
const [device, setDevice] = useState('')
function handleDeviceSelected (label: string) {
setDevice(label)
setFilter?.(Columns.SourceIP, label)
}
return ( return (
<div className="page"> <div className="page">
<Header title={t('title')}> <Header title={t('title')}>
@ -217,6 +238,7 @@ export default function Connections() {
<Checkbox className="connections-filter" checked={save} onChange={toggleSave}>{t('keepClosed')}</Checkbox> <Checkbox className="connections-filter" checked={save} onChange={toggleSave}>{t('keepClosed')}</Checkbox>
<Icon className="connections-filter dangerous" onClick={show} type="close-all" size={20} /> <Icon className="connections-filter dangerous" onClick={show} type="close-all" size={20} />
</Header> </Header>
{ devices.length > 1 && <Devices devices={devices} selected={device} onChange={handleDeviceSelected} /> }
<Card className="connections-card"> <Card className="connections-card">
<div {...getTableProps()} className="connections" ref={tableRef}> <div {...getTableProps()} className="connections" ref={tableRef}>
<div {...headerGroup.getHeaderGroupProps()} className="connections-header"> <div {...headerGroup.getHeaderGroupProps()} className="connections-header">

View File

@ -27,7 +27,7 @@
position: relative; position: relative;
text-align: center; text-align: center;
color: $color-gray-darken; color: $color-gray-darken;
background: #f3f6f9; background: $color-gray-light;
height: $height; height: $height;
line-height: $height; line-height: $height;
font-weight: 500; font-weight: 500;
@ -105,7 +105,7 @@
} }
&.completed { &.completed {
background-color: darken(#f3f6f9, 3%); background-color: darken($color-gray-light, 3%);
color: rgba($color-primary-darken, 50%); color: rgba($color-primary-darken, 50%);
} }

View File

@ -1,3 +1,5 @@
@import '~@styles/variables';
.logs-card { .logs-card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -13,7 +15,7 @@
list-style: none; list-style: none;
padding: 10px; padding: 10px;
border-radius: 2px; border-radius: 2px;
background-color: #f3f6f9; background-color: $color-gray-light;
font-size: 12px; font-size: 12px;
color: #73808f; color: #73808f;
overflow-y: auto; overflow-y: auto;

View File

@ -14,6 +14,7 @@ $color-primary-lightly: #e4eaef;
// common colors // common colors
$color-gray: #d8dee2; $color-gray: #d8dee2;
$color-gray-light: #f3f6f9;
$color-gray-dark: #b7c5d6; $color-gray-dark: #b7c5d6;
$color-gray-darken: #909399; $color-gray-darken: #909399;
$color-white: #fff; $color-white: #fff;