Browse Source

Update table component

pull/82/head
Sacha Weatherstone 3 years ago
parent
commit
703eacc269
No known key found for this signature in database GPG Key ID: 7AB2D7E206124B31
  1. 7
      src/components/form/IPInput.tsx
  2. 53
      src/components/generic/Table/index.tsx
  3. 9
      src/components/generic/Table/tmp/TimeAgo.tsx
  4. 143
      src/pages/Peers.tsx

7
src/components/form/IPInput.tsx

@ -38,7 +38,12 @@ export const IPInput = forwardRef<HTMLInputElement, InputProps>(function Input(
return ( return (
<InfoWrapper label={label} description={description} error={error}> <InfoWrapper label={label} description={description} error={error}>
<div className="relative flex rounded-md"> <div className="relative flex rounded-md">
<input value={numericalValue} ref={ref} hidden /> <input
value={numericalValue}
onChange={(e) => setNumericalValue(parseInt(e.target.value))}
ref={ref}
hidden
/>
<input <input
value={facadeInputValue} value={facadeInputValue}
onChange={(e) => { onChange={(e) => {

53
src/components/generic/Table/index.tsx

@ -0,0 +1,53 @@
import { ChevronUpIcon } from "@heroicons/react/24/outline";
export interface TableProps {
headings: Heading[];
rows: JSX.Element[][];
}
export interface Heading {
title: string;
type: "blank" | "normal";
sortable: boolean;
}
export const Table = ({ headings, rows }: TableProps): JSX.Element => {
return (
<table className="min-w-full">
<thead className="bg-backgroundPrimary text-sm font-semibold text-textPrimary">
<tr>
{headings.map((heading, index) => (
<th
key={index}
scope="col"
className={`py-2 pr-3 pl-6 text-left ${
heading.sortable
? "cursor-pointer hover:brightness-hover active:brightness-press"
: ""
}`}
>
<div className="flex gap-2">
{heading.title}
{heading.sortable && <ChevronUpIcon className="my-auto h-3" />}
</div>
</th>
))}
</tr>
</thead>
<tbody>
{rows.map((row, index) => (
<tr key={index}>
{row.map((item, index) => (
<td
key={index}
className="whitespace-nowrap py-2 text-sm text-textSecondary first:pl-2"
>
{item}
</td>
))}
</tr>
))}
</tbody>
</table>
);
};

9
src/components/generic/Table/tmp/TimeAgo.tsx

@ -0,0 +1,9 @@
import TimeAgoReact from "timeago-react";
export interface TimeAgoProps {
timestamp: number;
}
export const TimeAgo = ({ timestamp }: TimeAgoProps): JSX.Element => {
return <TimeAgoReact datetime={timestamp} opts={{ minInterval: 10 }} />;
};

143
src/pages/Peers.tsx

@ -1,122 +1,53 @@
import type React from "react"; import type React from "react";
import toast from "react-hot-toast";
import { base16 } from "rfc4648"; import { base16 } from "rfc4648";
import { IconButton } from "@app/components/form/IconButton.js"; import { Mono } from "@components/generic/Mono.js";
import { Mono } from "@app/components/generic/Mono.js"; import { Table } from "@components/generic/Table";
import { TimeAgo } from "@components/generic/Table/tmp/TimeAgo.js";
import { useDevice } from "@core/providers/useDevice.js"; import { useDevice } from "@core/providers/useDevice.js";
import { Hashicon } from "@emeraldpay/hashicon-react"; import { Hashicon } from "@emeraldpay/hashicon-react";
import {
ArrowPathRoundedSquareIcon,
EllipsisHorizontalIcon
} from "@heroicons/react/24/outline";
import { Protobuf } from "@meshtastic/meshtasticjs"; import { Protobuf } from "@meshtastic/meshtasticjs";
import TimeAgo from "timeago-react";
export const PeersPage = (): JSX.Element => { export const PeersPage = (): JSX.Element => {
const { connection, nodes } = useDevice(); const { connection, nodes } = useDevice();
return ( return (
<div className="w-full overflow-y-auto"> <div className="w-full overflow-y-auto">
<table className="min-w-full"> <Table
<thead className="bg-backgroundPrimary text-sm font-semibold text-textPrimary"> headings={[
<tr> { title: "", type: "blank", sortable: false },
<th scope="col" className="py-2 pr-3 pl-6 text-left"> { title: "Name", type: "normal", sortable: true },
Name { title: "Model", type: "normal", sortable: true },
</th> { title: "MAC Address", type: "normal", sortable: true },
<th scope="col" className="py-2 text-left"> { title: "Last Heard", type: "normal", sortable: true },
Model { title: "SNR", type: "normal", sortable: true }
</th> ]}
<th scope="col" className="py-2 text-left"> rows={nodes.map((node) => [
MAC Address <Hashicon size={24} value={node.data.num.toString()} />,
</th> <h1>
<th scope="col" className="py-2 text-left"> {node.data.user?.longName ?? node.data.user?.macaddr
Versions ? `Meshtastic_${base16
</th> .stringify(node.data.user?.macaddr.subarray(4, 6) ?? [])
<th scope="col" className="py-2 text-left"> .toLowerCase()}`
Last Heard : `UNK: ${node.data.num}`}
</th> </h1>,
<th scope="col" className="py-2 text-left">
SNR <Mono>{Protobuf.HardwareModel[node.data.user?.hwModel ?? 0]}</Mono>,
</th> <Mono>
<th scope="col" className="relative py-2 pl-3 pr-4 sm:pr-6"> {base16
<span className="sr-only">Edit</span> .stringify(node.data.user?.macaddr ?? [])
</th> .match(/.{1,2}/g)
</tr> ?.join(":") ?? "UNK"}
</thead> </Mono>,
<tbody> <TimeAgo timestamp={node.data.lastHeard * 1000} />,
{nodes.map((node, index) => ( <Mono>
<tr key={index}> {node.data.snr}db/
<td className="flex gap-2 whitespace-nowrap py-2 pr-3 pl-6 text-sm font-medium text-textPrimary"> {Math.min(Math.max((node.data.snr + 10) * 5, 0), 100)}%/
<Hashicon size={24} value={node.data.num.toString()} /> {(node.data.snr + 10) * 5}raw
<span className="my-auto"> </Mono>
{node.data.user?.longName ?? ])}
`Meshtastic_${base16 />
.stringify(node.data.user?.macaddr.subarray(4, 6) ?? [])
.toLowerCase()}`}
</span>
</td>
<td className="whitespace-nowrap py-2 text-sm text-textSecondary">
<span className="bg-slate-200 rounded-md p-1">
<Mono>
{Protobuf.HardwareModel[node.data.user?.hwModel ?? 0]}
</Mono>
</span>
</td>
<td className="whitespace-nowrap py-2 text-sm text-textSecondary">
<Mono>
{base16
.stringify(node.data.user?.macaddr ?? [])
.match(/.{1,2}/g)
?.join(":") ?? ""}
</Mono>
</td>
<td className="whitespace-nowrap py-2 text-sm text-textSecondary">
{node.metadata ? (
<>
<Mono>{node.metadata.firmwareVersion}</Mono>
<span className="text-black">/</span>
<Mono>{node.metadata.deviceStateVersion}</Mono>
</>
) : (
<IconButton
size="sm"
onClick={() => {
if (connection) {
void toast.promise(
connection.getMetadata({ nodeNum: node.data.num }),
{
loading: "Requesting Metadata...",
success: "Recieved Metadata",
error: "No response received"
}
);
}
}}
icon={<ArrowPathRoundedSquareIcon className="h-4" />}
/>
)}
</td>
<td className="whitespace-nowrap py-2 text-sm text-textSecondary">
<TimeAgo
datetime={node.data.lastHeard * 1000}
opts={{ minInterval: 10 }}
/>
</td>
<td className="whitespace-nowrap py-2 text-sm text-textSecondary">
<Mono>{node.data.snr}db</Mono>
</td>
<td className="relative whitespace-nowrap pl-3 pr-4 text-right text-sm font-medium">
<IconButton
size="sm"
icon={<EllipsisHorizontalIcon className="h-4" />}
/>
</td>
</tr>
))}
</tbody>
</table>
</div> </div>
); );
}; };

Loading…
Cancel
Save