Skip to content

Commit

Permalink
feat: search devices & workers
Browse files Browse the repository at this point in the history
  • Loading branch information
TurtIeSocks committed Nov 25, 2023
1 parent 3d849aa commit a053dc9
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 137 deletions.
194 changes: 104 additions & 90 deletions packages/client/src/app/status/devicesTable.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import { Fragment, memo, useCallback } from 'react';
import { Fragment, memo, useCallback, useState } from 'react';
import { StatusDTO, MitmControlDTO } from '@rotom/types';
import { Table, Dropdown, SortDescriptor } from '@nextui-org/react';
import { toast } from 'react-toastify';

import { RelativeTimeLabel } from './relativeTimeLabel';
import { StatusTable } from './statusTable';
import { useTableSort } from './useTableSort';
import { SearchInput } from './search';

const kBNumberFormat = new Intl.NumberFormat('en', { style: 'unit', unit: 'kilobyte', unitDisplay: 'short' });

const initialSortDescriptor: SortDescriptor = { column: 'origin', direction: 'ascending' };

export const DevicesTable = ({ devices, workers }: StatusDTO): JSX.Element => {
const [search, setSearch] = useState('');
const executeAction = useCallback(
async ({ deviceId, action }: { deviceId: string; action: 'reboot' | 'restart' | 'getLogcat' | 'delete' }) => {
const promise = fetch(`/api/device/${deviceId}/action/${action}`, { method: 'POST' }).then(async (response) => {
Expand Down Expand Up @@ -64,97 +66,109 @@ export const DevicesTable = ({ devices, workers }: StatusDTO): JSX.Element => {
return <Fragment />;

Check warning on line 66 in packages/client/src/app/status/devicesTable.tsx

View workflow job for this annotation

GitHub Actions / Nx Cloud - Main Job / Run

Fragments should contain more than one child - otherwise, there’s no need for a Fragment at all

Check warning on line 66 in packages/client/src/app/status/devicesTable.tsx

View workflow job for this annotation

GitHub Actions / Nx Cloud - Main Job / Run

Fragments should contain more than one child - otherwise, there’s no need for a Fragment at all
}

const lowercaseSearch = search.toLowerCase();
return (
<StatusTable
aria-label="Devices"
onSortChange={list.sort}
rowsPerPage={30}
sortDescriptor={list.sortDescriptor}
tableLength={list.items.length}
>
<Table.Header>
<Table.Column key="origin" allowsSorting>
Origin
</Table.Column>
<Table.Column key="load" allowsSorting>
Load
</Table.Column>
<Table.Column key="deviceId" allowsSorting>
Device Id
</Table.Column>
<Table.Column key="isAlive" allowsSorting>
Is Alive
</Table.Column>
<Table.Column key="version" allowsSorting>
MITM's Version
</Table.Column>
<Table.Column key="dateLastMessageReceived" allowsSorting>
Last message received
</Table.Column>
<Table.Column key="dateLastMessageSent" allowsSorting>
Last message sent
</Table.Column>
<Table.Column key="lastMemory.memFree" allowsSorting>
Free memory
</Table.Column>
<Table.Column key="lastMemory.memMitm" allowsSorting>
MITM memory
</Table.Column>
<Table.Column key="lastMemory.memStart" allowsSorting>
Start memory
</Table.Column>
<Table.Column>Actions</Table.Column>
</Table.Header>
<Table.Body loadingState={list.loadingState}>
{list.items.map((device, index) => {
const deviceWorkers = workers.filter((worker) => worker.deviceId === device.deviceId);
<>
<SearchInput value={search} onChange={setSearch} />
<StatusTable
aria-label="Devices"
onSortChange={list.sort}
rowsPerPage={30}
sortDescriptor={list.sortDescriptor}
tableLength={list.items.length}
>
<Table.Header>
<Table.Column key="origin" allowsSorting>
Origin
</Table.Column>
<Table.Column key="load" allowsSorting>
Load
</Table.Column>
<Table.Column key="deviceId" allowsSorting>
Device Id
</Table.Column>
<Table.Column key="isAlive" allowsSorting>
Is Alive
</Table.Column>
<Table.Column key="version" allowsSorting>
MITM's Version
</Table.Column>
<Table.Column key="dateLastMessageReceived" allowsSorting>
Last message received
</Table.Column>
<Table.Column key="dateLastMessageSent" allowsSorting>
Last message sent
</Table.Column>
<Table.Column key="lastMemory.memFree" allowsSorting>
Free memory
</Table.Column>
<Table.Column key="lastMemory.memMitm" allowsSorting>
MITM memory
</Table.Column>
<Table.Column key="lastMemory.memStart" allowsSorting>
Start memory
</Table.Column>
<Table.Column>Actions</Table.Column>
</Table.Header>
<Table.Body loadingState={list.loadingState}>
{list.items
.filter(
(device) =>
!lowercaseSearch ||
device.origin?.toLowerCase().includes(lowercaseSearch) ||
device.deviceId?.toLowerCase().includes(lowercaseSearch) ||
device.version === lowercaseSearch,
)
.map((device, index) => {
const deviceWorkers = workers.filter((worker) => worker.deviceId === device.deviceId);

return (
<Table.Row key={`${device.deviceId}-${index}`}>
<Table.Cell>{device.origin}</Table.Cell>
<Table.Cell>
{deviceWorkers.filter((worker) => worker.isAllocated).length}/{deviceWorkers.length}
</Table.Cell>
<Table.Cell>{device.deviceId}</Table.Cell>
<Table.Cell>{device.isAlive ? '✅' : '❌'}</Table.Cell>
<Table.Cell>{device.version}</Table.Cell>
<Table.Cell>
<RelativeTimeLabel timestamp={device.dateLastMessageReceived} />
</Table.Cell>
<Table.Cell>
<RelativeTimeLabel timestamp={device.dateLastMessageSent} />
</Table.Cell>
<Table.Cell>{kBNumberFormat.format(device.lastMemory.memFree)}</Table.Cell>
<Table.Cell>{kBNumberFormat.format(device.lastMemory.memMitm)}</Table.Cell>
<Table.Cell>{kBNumberFormat.format(device.lastMemory.memStart)}</Table.Cell>
<Table.Cell>
{device.deviceId !== undefined && (
<Dropdown>
<Dropdown.Button size="xs" css={{ minWidth: 0 }} />
<Dropdown.Menu
aria-label="Actions"
onAction={(key) => {
if (
device.deviceId &&
(key === 'restart' || key === 'reboot' || key === 'getLogcat' || key === 'delete')
) {
executeAction({ deviceId: device.deviceId, action: key });
}
}}
>
<Dropdown.Item key="restart">Restart</Dropdown.Item>
<Dropdown.Item key="reboot">Reboot</Dropdown.Item>
<Dropdown.Item key="getLogcat">Logcat</Dropdown.Item>
<Dropdown.Item key="delete">Delete</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
)}
</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</StatusTable>
return (
<Table.Row key={`${device.deviceId}-${index}`}>
<Table.Cell>{device.origin}</Table.Cell>
<Table.Cell>
{deviceWorkers.filter((worker) => worker.isAllocated).length}/{deviceWorkers.length}
</Table.Cell>
<Table.Cell>{device.deviceId}</Table.Cell>
<Table.Cell>{device.isAlive ? '✅' : '❌'}</Table.Cell>
<Table.Cell>{device.version}</Table.Cell>
<Table.Cell>
<RelativeTimeLabel timestamp={device.dateLastMessageReceived} />
</Table.Cell>
<Table.Cell>
<RelativeTimeLabel timestamp={device.dateLastMessageSent} />
</Table.Cell>
<Table.Cell>{kBNumberFormat.format(device.lastMemory.memFree)}</Table.Cell>
<Table.Cell>{kBNumberFormat.format(device.lastMemory.memMitm)}</Table.Cell>
<Table.Cell>{kBNumberFormat.format(device.lastMemory.memStart)}</Table.Cell>
<Table.Cell>
{device.deviceId !== undefined && (
<Dropdown>
<Dropdown.Button size="xs" css={{ minWidth: 0 }} />
<Dropdown.Menu
aria-label="Actions"
onAction={(key) => {
if (
device.deviceId &&
(key === 'restart' || key === 'reboot' || key === 'getLogcat' || key === 'delete')
) {
executeAction({ deviceId: device.deviceId, action: key });
}
}}
>
<Dropdown.Item key="restart">Restart</Dropdown.Item>
<Dropdown.Item key="reboot">Reboot</Dropdown.Item>
<Dropdown.Item key="getLogcat">Logcat</Dropdown.Item>
<Dropdown.Item key="delete">Delete</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
)}
</Table.Cell>
</Table.Row>
);
})}
</Table.Body>
</StatusTable>
</>
);
};

Expand Down
16 changes: 16 additions & 0 deletions packages/client/src/app/status/search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { FormElement, Input, InputProps } from '@nextui-org/react';
import { useCallback } from 'react';

const css: InputProps['css'] = {
width: '100%',
pt: 4,
pb: 6,
};

export const SearchInput = ({
value,
onChange,
}: Partial<Omit<InputProps, 'onChange'>> & { onChange: (newValue: string) => void }): JSX.Element => {
const onChangeMemo = useCallback((event: React.ChangeEvent<FormElement>) => onChange(event.target.value), [onChange]);
return <Input placeholder="Search" animated={false} clearable onChange={onChangeMemo} css={css} value={value} />;
};
2 changes: 1 addition & 1 deletion packages/client/src/app/status/statusTable.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Children } from 'react';
import { CollectionElement } from '@react-types/shared';
import { Table, TableProps } from '@nextui-org/react';
import { Table, TableProps, Input } from '@nextui-org/react';

Check warning on line 3 in packages/client/src/app/status/statusTable.tsx

View workflow job for this annotation

GitHub Actions / Nx Cloud - Main Job / Run

'Input' is defined but never used

Check warning on line 3 in packages/client/src/app/status/statusTable.tsx

View workflow job for this annotation

GitHub Actions / Nx Cloud - Main Job / Run

'Input' is defined but never used

interface StatusTableProps extends TableProps {
tableLength: number;
Expand Down
106 changes: 60 additions & 46 deletions packages/client/src/app/status/workersTable.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { Fragment, memo } from 'react';
import { SortDescriptor, Table } from '@nextui-org/react';
import { Fragment, memo, useState } from 'react';
import { Input, SortDescriptor, Table } from '@nextui-org/react';

Check warning on line 2 in packages/client/src/app/status/workersTable.tsx

View workflow job for this annotation

GitHub Actions / Nx Cloud - Main Job / Run

'Input' is defined but never used

Check warning on line 2 in packages/client/src/app/status/workersTable.tsx

View workflow job for this annotation

GitHub Actions / Nx Cloud - Main Job / Run

'Input' is defined but never used
import { WorkerDTO, StatusDTO } from '@rotom/types';

import { RelativeTimeLabel } from './relativeTimeLabel';
import { StatusTable } from './statusTable';
import { useTableSort } from './useTableSort';
import { SearchInput } from './search';

const initialSortDescriptor: SortDescriptor = { column: 'workerId', direction: 'ascending' };

export const WorkersTable = ({ workers }: { workers: StatusDTO['workers'] }): JSX.Element => {
const [search, setSearch] = useState('');
const list = useTableSort<WorkerDTO>({
items: workers,
initialSortDescriptor,
Expand All @@ -18,51 +20,63 @@ export const WorkersTable = ({ workers }: { workers: StatusDTO['workers'] }): JS
return <Fragment />;

Check warning on line 20 in packages/client/src/app/status/workersTable.tsx

View workflow job for this annotation

GitHub Actions / Nx Cloud - Main Job / Run

Fragments should contain more than one child - otherwise, there’s no need for a Fragment at all

Check warning on line 20 in packages/client/src/app/status/workersTable.tsx

View workflow job for this annotation

GitHub Actions / Nx Cloud - Main Job / Run

Fragments should contain more than one child - otherwise, there’s no need for a Fragment at all
}

const lowercaseSearch = search.toLowerCase();
return (
<StatusTable
aria-label="Workers"
onSortChange={list.sort}
rowsPerPage={100}
sortDescriptor={list.sortDescriptor}
tableLength={list.items.length}
>
<Table.Header>
<Table.Column key="origin" allowsSorting>
Origin
</Table.Column>
<Table.Column key="workerId" allowsSorting>
Worker Id
</Table.Column>
<Table.Column key="isAllocated" allowsSorting>
Is Active
</Table.Column>
<Table.Column key="scanner.workerName" allowsSorting>
Scanner Worker Name
</Table.Column>
<Table.Column key="mitm.dateLastMessageReceived" allowsSorting>
Last message received
</Table.Column>
<Table.Column key="mitm.dateLastMessageSent" allowsSorting>
Last message sent
</Table.Column>
</Table.Header>
<Table.Body loadingState={list.loadingState}>
{list.items.map((worker, index) => (
<Table.Row key={`${worker.workerId}-${index}`}>
<Table.Cell>{worker.mitm.origin}</Table.Cell>
<Table.Cell>{worker.workerId}</Table.Cell>
<Table.Cell>{worker.isAllocated ? '✅' : '❌'}</Table.Cell>
<Table.Cell>{worker.scanner?.workerName}</Table.Cell>
<Table.Cell>
<RelativeTimeLabel timestamp={worker.mitm.dateLastMessageReceived} />
</Table.Cell>
<Table.Cell>
<RelativeTimeLabel timestamp={worker.mitm.dateLastMessageSent} />
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</StatusTable>
<>
<SearchInput value={search} onChange={setSearch} />
<StatusTable
aria-label="Workers"
onSortChange={list.sort}
rowsPerPage={100}
sortDescriptor={list.sortDescriptor}
tableLength={list.items.length}
>
<Table.Header>
<Table.Column key="origin" allowsSorting>
Origin
</Table.Column>
<Table.Column key="workerId" allowsSorting>
Worker Id
</Table.Column>
<Table.Column key="isAllocated" allowsSorting>
Is Active
</Table.Column>
<Table.Column key="scanner.workerName" allowsSorting>
Scanner Worker Name
</Table.Column>
<Table.Column key="mitm.dateLastMessageReceived" allowsSorting>
Last message received
</Table.Column>
<Table.Column key="mitm.dateLastMessageSent" allowsSorting>
Last message sent
</Table.Column>
</Table.Header>
<Table.Body loadingState={list.loadingState}>
{list.items
.filter(
(worker) =>
!lowercaseSearch ||
worker.mitm.origin?.toLowerCase().includes(lowercaseSearch) ||
worker.workerId.toLowerCase().includes(lowercaseSearch) ||
worker.scanner?.workerName.toLowerCase().includes(lowercaseSearch),
)
.map((worker, index) => (
<Table.Row key={`${worker.workerId}-${index}`}>
<Table.Cell>{worker.mitm.origin}</Table.Cell>
<Table.Cell>{worker.workerId}</Table.Cell>
<Table.Cell>{worker.isAllocated ? '✅' : '❌'}</Table.Cell>
<Table.Cell>{worker.scanner?.workerName}</Table.Cell>
<Table.Cell>
<RelativeTimeLabel timestamp={worker.mitm.dateLastMessageReceived} />
</Table.Cell>
<Table.Cell>
<RelativeTimeLabel timestamp={worker.mitm.dateLastMessageSent} />
</Table.Cell>
</Table.Row>
))}
</Table.Body>
</StatusTable>
</>
);
};

Expand Down

0 comments on commit a053dc9

Please sign in to comment.