Skip to content

Commit

Permalink
feat(ota): refactor table
Browse files Browse the repository at this point in the history
  • Loading branch information
nurikk committed Nov 21, 2021
1 parent 10e93e5 commit 9a15fdb
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 145 deletions.
105 changes: 105 additions & 0 deletions src/components/grid/ReactTableCom.tsx
@@ -0,0 +1,105 @@
import React from "react";
import { useTable, useSortBy, HeaderGroup, Column, useAsyncDebounce, useGlobalFilter } from 'react-table'

import cx from "classnames";
import { useTranslation } from "react-i18next";

interface Props {
columns: Array<Column<any>>;
data: Array<any>;
}

function GlobalFilter({
globalFilter,
setGlobalFilter,
}) {
const [value, setValue] = React.useState(globalFilter)
const onChange = useAsyncDebounce(value => {
setGlobalFilter(value || undefined)
}, 200);

const { t } = useTranslation(['common'])

return (
<span>
<input
value={value || ""}
onChange={e => {
setValue(e.target.value);
onChange(e.target.value);
}}
placeholder={t('common:enter_search_criteria')}
className="form-control"
/>
</span>
)
}

export const Table: React.FC<Props> = ({ columns, data }) => {

const {
getTableProps,
getTableBodyProps,
headerGroups,
rows,
prepareRow,
state,
visibleColumns,
setGlobalFilter,
} = useTable<any>(
{
columns,
data,
},
useGlobalFilter,
useSortBy
)


return (
<table {...getTableProps()} className="table responsive">
<thead>
<tr>
<th
colSpan={visibleColumns.length}
>
<GlobalFilter
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
/>
</th>
</tr>
{headerGroups.map((headerGroup: HeaderGroup<any>) => (
<tr {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map(column => (
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
<span className={cx({ 'btn btn-link': column.canSort })}>{column.render('Header')}</span>
<span>
{column.isSorted
? column.isSortedDesc
? <i className={`fa fa-sort-amount-down-alt`} />
: <i className={`fa fa-sort-amount-down`} />
: <i className={`fa fa-sort-amount-down invisible`} />}
</span>
</th>
))}
</tr>
))}

</thead>
<tbody {...getTableBodyProps()}>
{rows.map(
(row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => <td {...cell.getCellProps()}>{cell.render('Cell')}</td>)}
</tr>
)
}
)}
</tbody>
</table>

)
}
85 changes: 50 additions & 35 deletions src/components/ota-page/index.tsx
Expand Up @@ -11,6 +11,8 @@ import { Link } from "react-router-dom";
import { Device, DeviceState, OTAState } from "../../types";
import { VendorLink, ModelLink, OTALink } from "../vendor-links/verndor-links";
import { useTranslation, WithTranslation, withTranslation } from "react-i18next";
import { Column } from "react-table";
import { Table } from "../grid/ReactTableCom";


type OtaRowProps = {
Expand Down Expand Up @@ -39,57 +41,70 @@ const StateCell: FunctionComponent<OtaRowProps & OtaApi> = (props) => {

}
}
const OtaRow: FunctionComponent<OtaRowProps & OtaApi> = (props) => {
const { device, state, ...rest } = props;
return <tr>
<td><Link to={genDeviceDetailsLink(device.ieee_address)}>{device.friendly_name}</Link></td>

<td className="text-truncate text-nowrap position-relative"><VendorLink device={device} /></td>
<td title={device?.definition?.description}><ModelLink device={device} /></td>
<td>{device.date_code}</td>
<td><OTALink device={device} /></td>
<td>
<StateCell device={device} state={state} {...rest} />
</td>
</tr>
}
type PropsFromStore = Pick<GlobalState, 'devices' | 'deviceStates'>;

type OtaGridData = {
id: string;
device: Device;
state: DeviceState;
}
class OtaPage extends Component<PropsFromStore & OtaApi & WithTranslation<"ota">, unknown> {
getAllOtaDevices() {
const { devices } = this.props;
return Object.values(devices).filter(device => device?.definition?.supports_ota)
const { devices, deviceStates } = this.props;
return Object.values(devices)
.filter(device => device?.definition?.supports_ota)
.map((device) => {
const state = deviceStates[device.friendly_name] ?? {} as DeviceState;
return { id: device.friendly_name, device, state } as OtaGridData;
})
}
checkAllOTA = () => {
const { checkOTA } = this.props;
const otaDevices = this.getAllOtaDevices();
otaDevices.forEach(device => checkOTA(device.friendly_name));
otaDevices.forEach(({ device }) => checkOTA(device.friendly_name));
}
render() {
const { deviceStates, checkOTA, updateOTA, t } = this.props;
const { checkOTA, updateOTA, t } = this.props;
const otaApi = { checkOTA, updateOTA };
const otaDevices = this.getAllOtaDevices();
const columns: Column<OtaGridData>[] = [
{
Header: t('zigbee:friendly_name') as string,
accessor: ({ device }) => device.friendly_name,
Cell: ({ row: { original: { device } } }) => <Link to={genDeviceDetailsLink(device.ieee_address)}>{device.friendly_name}</Link>

},
{
Header: t('zigbee:manufacturer') as string,
accessor: ({ device }) => [device.manufacturer, device.definition?.vendor].join(' '),
Cell: ({ row: { original: { device } } }) => <VendorLink device={device} />
},
{
Header: t('zigbee:model') as string,
accessor: ({ device }) => [device.model_id, device.definition?.model].join(' '),
Cell: ({ row: { original: { device } } }) => <ModelLink device={device} />
},
{
Header: t('zigbee:firmware_build_date') as string,
accessor: ({ device }) => device.date_code

},
{
Header: t('zigbee:firmware_version') as string,
accessor: ({ device }) => [device.model_id, device.definition?.model].join(' '),
Cell: ({ row: { original: { device } } }) => <OTALink device={device} />
},
{
Header: () => <Button className="btn btn-danger btn-sm" onClick={this.checkAllOTA} promt>{t('check_all')}</Button>,
id: 'check_all',
Cell: ({ row: { original: { device, state } } }) => <StateCell device={device} state={state} {...otaApi} />
},
]

return <div className="card">
<div className="card-body table-responsive">
<table className="table">
<thead>
<tr>
<th scope="col">{t("zigbee:friendly_name")}</th>
<th>{t("zigbee:manufacturer")}</th>
<th>{t("zigbee:model")}</th>
<th>{t("zigbee:firmware_build_date")}</th>
<th>{t("zigbee:firmware_version")}</th>
<th><Button className="btn btn-danger btn-sm" onClick={this.checkAllOTA} promt>{t('check_all')}</Button></th>
</tr>
</thead>
<tbody>
{otaDevices.length === 0 ? <tr><td colSpan={6}>{t('empty_ota_message')}</td></tr> : null}
{otaDevices.map(device => (
<OtaRow key={device.ieee_address} device={device} state={deviceStates[device.friendly_name]} {...otaApi} />
))}
</tbody>
</table>
<Table columns={columns} data={otaDevices} />
</div>
</div>
}
Expand Down
106 changes: 0 additions & 106 deletions src/components/zigbee/ReactTableCom.tsx

This file was deleted.

8 changes: 4 additions & 4 deletions src/components/zigbee/index.tsx
Expand Up @@ -16,9 +16,9 @@ import { DisplayValue } from "../display-value/DisplayValue";
import { LastSeen } from "../LastSeen";
import PowerSource from "../power-source";
import DeviceControlGroup from "../device-control/DeviceControlGroup";
import { Table } from "./ReactTableCom";
import { Table } from "../grid/ReactTableCom";
import { CellProps, Column } from "react-table";
import { join } from "lodash";


type SortOrder = "asc" | "desc";

Expand Down Expand Up @@ -146,7 +146,7 @@ export class ZigbeeTable extends Component<ZigbeeTableProps, ZigbeeTableState> {
const devices = this.getDevicesToRender();
const { sortColumnId, sortDirection, search } = this.state;
const lastSeenType = getLastSeenType(bridgeInfo.config.advanced);
const reactTableColumns: Column<ZigbeeTableData>[] = [
const columns: Column<ZigbeeTableData>[] = [
{
Header: '#',
id: '-rownumber',
Expand Down Expand Up @@ -209,7 +209,7 @@ export class ZigbeeTable extends Component<ZigbeeTableProps, ZigbeeTableState> {
return (<div className="card">
<div className="table-responsive mt-1">
<Table
columns={reactTableColumns}
columns={columns}
data={devices}
/>
</div>
Expand Down

0 comments on commit 9a15fdb

Please sign in to comment.