Skip to content

Commit

Permalink
MUI DataGrid refinements
Browse files Browse the repository at this point in the history
- Changed page size to 15.
- Removed unused code for page size in .env.
- Edited api call to accept page size variable.
- Added page size to the query string in the frontend.
- Built filter by name drop down component in DataGrid toolbar.
- Built filter by status drop down component in DataGrid toolbar.
  • Loading branch information
hawkishpolicy committed Jun 21, 2024
1 parent 6ac4d2c commit 8379806
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 9 deletions.
17 changes: 13 additions & 4 deletions backend/src/api/scan-tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
} from './auth';
import ECSClient from '../tasks/ecs-client';

const PAGE_SIZE = parseInt(process.env.PAGE_SIZE ?? '') || 25;
const PAGE_SIZE = 15;

class ScanTaskFilters {
@IsString()
Expand Down Expand Up @@ -59,6 +59,11 @@ class ScanTaskSearch {
@IsOptional()
filters?: ScanTaskFilters;

@IsInt()
@IsOptional()
// If set to -1, returns all results.
pageSize?: number;

async filterResultQueryset(qs: SelectQueryBuilder<ScanTask>, event) {
if (this.filters?.name) {
qs.andWhere('scan.name ILIKE :name', {
Expand All @@ -84,12 +89,16 @@ class ScanTaskSearch {
}

async getResults(event) {
const pageSize = this.pageSize || PAGE_SIZE;

const qs = ScanTask.createQueryBuilder('scan_task')
.leftJoinAndSelect('scan_task.scan', 'scan')
.leftJoinAndSelect('scan_task.organizations', 'organization')
.orderBy(`scan_task.${this.sort}`, this.order)
.skip(PAGE_SIZE * (this.page - 1))
.take(PAGE_SIZE);
.orderBy(`scan_task.${this.sort}`, this.order);

if (pageSize !== -1) {
qs.skip(pageSize * (this.page - 1)).take(pageSize);
}

await this.filterResultQueryset(qs, event);
return qs.getManyAndCount();
Expand Down
160 changes: 155 additions & 5 deletions frontend/src/pages/Scans/ScanTasksView.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { TableInstance, Column, CellProps, Row } from 'react-table';
import { Query, Scan } from 'types';
import { Table, Paginator, ColumnFilter, selectFilter } from 'components';
Expand All @@ -18,12 +18,16 @@ import {
DialogTitle,
Icon,
IconButton,
Menu,
MenuItem,
Paper,
Typography
} from '@mui/material';
import { Box } from '@mui/system';
import { DataGrid, GridColDef, GridRenderCellParams } from '@mui/x-data-grid';
import CustomToolbar from 'components/DataGrid/CustomToolbar';
import { Button as MuiButton } from '@mui/material';
import { KeyboardArrowDown } from '@mui/icons-material';

interface ApiResponse {
result: ScanTask[];
Expand All @@ -37,7 +41,7 @@ interface Errors {
export interface ScansTaskRow {
id: string;
status: string;
type: string;
name: string;
input: string;
output: string;
createdAt: string;
Expand Down Expand Up @@ -271,6 +275,7 @@ export const ScanTasksView: React.FC = () => {
{
body: {
page,
pageSize: query.pageSize ?? PAGE_SIZE,
sort: sort[0]?.id ?? 'createdAt',
order: sort[0]?.desc ? 'DESC' : 'ASC',
filters: tableFilters
Expand Down Expand Up @@ -301,7 +306,7 @@ export const ScanTasksView: React.FC = () => {
const scansTasksRows: ScansTaskRow[] = scanTasks.map((scanTask) => ({
id: scanTask.id,
status: scanTask.status,
type: scanTask.type,
name: scanTask.scan?.name,
input: scanTask.input,
output: scanTask.output,
createdAt: scanTask.createdAt,
Expand All @@ -315,7 +320,7 @@ export const ScanTasksView: React.FC = () => {
const scansTasksCols: GridColDef[] = [
{ field: 'id', headerName: 'ID', minWidth: 100, flex: 2 },
{ field: 'status', headerName: 'Status', minWidth: 100, flex: 1 },
{ field: 'type', headerName: 'Name', minWidth: 100, flex: 1 },
{ field: 'name', headerName: 'Name', minWidth: 100, flex: 1 },
{ field: 'createdAt', headerName: 'Created At', minWidth: 200, flex: 1 },
{ field: 'finishedAt', headerName: 'Finished At', minWidth: 200, flex: 1 },
{
Expand Down Expand Up @@ -343,9 +348,139 @@ export const ScanTasksView: React.FC = () => {
page: 0,
pageSize: PAGE_SIZE,
sort: [],
filters: []
filters: [] as { id: string; value: any }[]
});

useEffect(() => {
fetchScanTasks({
page: 1,
pageSize: PAGE_SIZE,
sort: paginationModel.sort,
filters: paginationModel.filters
});
}, [fetchScanTasks, paginationModel.sort, paginationModel.filters]);

const [anchorElName, setAnchorElName] = useState<null | HTMLElement>(null);
const [anchorElStatus, setAnchorElStatus] = useState<null | HTMLElement>(
null
);
const openNameMenu = Boolean(anchorElName);
const openStatusMenu = Boolean(anchorElStatus);
const handleStatusClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElStatus(event.currentTarget);
};
const handleNameClick = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElName(event.currentTarget);
};

const handleNameSelect = (name: string) => {
setPaginationModel((prev) => ({
...prev,
filters: [
...prev.filters.filter((f) => f.id !== 'name'),
{ id: 'name', value: name }
]
}));
setAnchorElName(null);
};
const handleStatusSelect = (status: string) => {
setPaginationModel((prev) => ({
...prev,
filters: [
...prev.filters.filter((f) => f.id !== 'status'),
{ id: 'status', value: status }
]
}));
setAnchorElStatus(null);
};
const scanNameValues = [
'censys',
'amass',
'findomain',
'portscanner',
'wappalyzer',
'censysIpv4',
'censysCertificates',
'sslyze',
'searchSync',
'cve',
'dotgov',
'webscraper',
'intrigueIdent',
'shodan',
'hibp',
'lookingGlass',
'dnstwist',
'rootDomainSync'
];

const statusValues = [
'created',
'queued',
'requested',
'started',
'finished',
'failed'
];

const scanNameDropdown = (
<>
<MuiButton
size="small"
sx={{ '& .MuiButton-startIcon': { mr: '2px', mb: '2px' } }}
endIcon={<KeyboardArrowDown />}
onClick={handleNameClick}
>
Name
</MuiButton>
<Menu
anchorEl={anchorElName}
open={openNameMenu}
onClose={() => setAnchorElName(null)}
>
<MenuItem value="">All</MenuItem>
{scanNameValues.map((name) => (
<MenuItem
key={name}
value={name}
onClick={handleNameSelect.bind(null, name)}
>
{name}
</MenuItem>
))}
</Menu>
</>
);

const scanStatusDropdown = (
<>
<MuiButton
size="small"
sx={{ '& .MuiButton-startIcon': { mr: '2px', mb: '2px' } }}
endIcon={<KeyboardArrowDown />}
onClick={handleStatusClick}
>
Status
</MuiButton>
<Menu
anchorEl={anchorElStatus}
open={openStatusMenu}
onClose={() => setAnchorElStatus(null)}
>
<MenuItem value="">All</MenuItem>
{statusValues.map((status) => (
<MenuItem
key={status}
value={status}
onClick={handleStatusSelect.bind(null, status)}
>
{status}
</MenuItem>
))}
</Menu>
</>
);

return (
<>
{errors.global && <p className={classes.error}>{errors.global}</p>}
Expand Down Expand Up @@ -374,6 +509,9 @@ export const ScanTasksView: React.FC = () => {
rowCount={totalResults}
columns={scansTasksCols}
slots={{ toolbar: CustomToolbar }}
slotProps={{
toolbar: { children: [scanNameDropdown, scanStatusDropdown] }
}}
paginationMode="server"
paginationModel={paginationModel}
onPaginationModelChange={(model) => {
Expand All @@ -384,6 +522,18 @@ export const ScanTasksView: React.FC = () => {
filters: paginationModel.filters
});
}}
filterMode="server"
onFilterModelChange={(model) => {
fetchScanTasks({
page: 1,
pageSize: paginationModel.pageSize,
sort: paginationModel.sort,
filters: model.items.map((item) => ({
id: item.field,
value: item.value
}))
});
}}
pageSizeOptions={[15, 30, 50, 100]}
/>
)}
Expand Down

0 comments on commit 8379806

Please sign in to comment.