Skip to content

Commit 4e56441

Browse files
[WIP] serial number lookup + filter to only "All" group
Signed-off-by: Edouard Vanbelle <[email protected]>
1 parent 2aa7b0c commit 4e56441

File tree

5 files changed

+166
-21
lines changed

5 files changed

+166
-21
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import Button from "@components/Button";
2+
import { Tooltip, TooltipContent, TooltipTrigger } from "@components/Tooltip";
3+
import { motion } from "framer-motion";
4+
import { RefreshCcw } from "lucide-react";
5+
import * as React from "react";
6+
import { useState } from "react";
7+
8+
type Props = {
9+
onClick: () => void;
10+
disabled?: boolean;
11+
variant?: "default" | "primary" | "secondary" | "input" | "dotted" | "tertiary" | "white" | "outline" | "danger-outline" | "default-outline" | "danger" | null | undefined;
12+
title?:any;
13+
children: any;
14+
};
15+
export default function DataTableToolTipButton({ onClick, disabled, variant, title, children }: Props) {
16+
const [rotate, setRotate] = useState(false);
17+
//const [disabled, setDisabled] = useState(false);
18+
const [hovered, setHovered] = useState(false);
19+
20+
return (
21+
<Tooltip>
22+
<TooltipTrigger
23+
asChild={true}
24+
>
25+
<Button
26+
className={"h-[42px]"}
27+
variant={variant}
28+
disabled={disabled == true ? true : disabled}
29+
onClick={onClick}
30+
>
31+
{children}
32+
</Button>
33+
</TooltipTrigger>
34+
35+
<TooltipContent
36+
sideOffset={10}
37+
className={"px-3 py-2"}
38+
onPointerDownOutside={(event) => {
39+
if (hovered) event.preventDefault();
40+
}}
41+
>
42+
<span className={"text-xs text-neutral-300"}>
43+
{title}
44+
</span>
45+
</TooltipContent>
46+
</Tooltip>
47+
);
48+
}

src/modules/groups/AssignPeerToGroupModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,6 @@ const PeersTableColumns: ColumnDef<Peer>[] = [
367367
header: ({ column }) => {
368368
return <DataTableHeader column={column}>OS</DataTableHeader>;
369369
},
370-
cell: ({ row }) => <PeerOSCell os={row.original.os} />,
370+
cell: ({ row }) => <PeerOSCell os={row.original.os} serial={row.original.serial_number} />,
371371
},
372372
];

src/modules/peer/AccessiblePeersTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const AccessiblePeersColumns: ColumnDef<Peer>[] = [
6969
header: ({ column }) => {
7070
return <DataTableHeader column={column}>OS</DataTableHeader>;
7171
},
72-
cell: ({ row }) => <PeerOSCell os={row.original.os} />,
72+
cell: ({ row }) => <PeerOSCell os={row.original.os} serial={row.original.serial_number}/>,
7373
},
7474
];
7575

src/modules/peers/PeerOSCell.tsx

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import AppleLogo from "@/assets/os-icons/apple.svg";
1313
import FreeBSDLogo from "@/assets/os-icons/FreeBSD.png";
1414
import { getOperatingSystem } from "@/hooks/useOperatingSystem";
1515
import { OperatingSystem } from "@/interfaces/OperatingSystem";
16+
import { Barcode, Laptop } from "lucide-react";
1617

17-
export function PeerOSCell({ os }: { os: string }) {
18+
export function PeerOSCell({ os, serial }: { os: string, serial: string }) {
1819
return (
1920
<TooltipProvider>
2021
<Tooltip delayDuration={1}>
@@ -34,13 +35,47 @@ export function PeerOSCell({ os }: { os: string }) {
3435
</div>
3536
</TooltipTrigger>
3637
<TooltipContent>
37-
<div className={"text-neutral-300 flex flex-col gap-1"}>{os}</div>
38+
<ListItem
39+
icon={<Laptop size={14} />}
40+
label={"OS"}
41+
value={os}
42+
/>
43+
<ListItem
44+
icon={<Barcode size={14} />}
45+
label={"Serial"}
46+
value={serial}
47+
/>
3848
</TooltipContent>
3949
</Tooltip>
4050
</TooltipProvider>
4151
);
4252
}
4353

54+
const ListItem = ({
55+
icon,
56+
label,
57+
value,
58+
}: {
59+
icon: React.ReactNode;
60+
label: string;
61+
value: string | React.ReactNode;
62+
}) => {
63+
return (
64+
<div
65+
className={
66+
"flex justify-between gap-5 border-b border-nb-gray-920 py-2 px-4 last:border-b-0 text-xs"
67+
}
68+
>
69+
<div className={"flex items-center gap-2 text-nb-gray-100 font-medium"}>
70+
{icon}
71+
{label}
72+
</div>
73+
<div className={"text-nb-gray-400"}>{value}</div>
74+
</div>
75+
);
76+
};
77+
78+
4479
export function OSLogo({ os }: { os: string }) {
4580
const icon = useMemo(() => {
4681
return getOperatingSystem(os);

src/modules/peers/PeersTable.tsx

Lines changed: 79 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import {
1414
ColumnDef,
1515
RowSelectionState,
1616
SortingState,
17+
VisibilityState,
1718
} from "@tanstack/react-table";
1819
import { uniqBy } from "lodash";
1920
import { ExternalLinkIcon } from "lucide-react";
2021
import { usePathname } from "next/navigation";
2122
import React, { useState } from "react";
2223
import { useSWRConfig } from "swr";
2324
import PeerIcon from "@/assets/icons/PeerIcon";
25+
import DataTableToolTipButton from "@/components/table/DataTableToolTipButton";
2426
import PeerProvider from "@/contexts/PeerProvider";
2527
import { useLoggedInUser } from "@/contexts/UsersProvider";
2628
import { useLocalStorage } from "@/hooks/useLocalStorage";
@@ -40,6 +42,7 @@ import PeerVersionCell from "@/modules/peers/PeerVersionCell";
4042
const PeersTableColumns: ColumnDef<Peer>[] = [
4143
{
4244
id: "select",
45+
enableHiding: false,
4346
header: ({ table }) => (
4447
<div className={"min-w-[20px] max-w-[20px]"}>
4548
<Checkbox
@@ -60,10 +63,10 @@ const PeersTableColumns: ColumnDef<Peer>[] = [
6063
</div>
6164
),
6265
enableSorting: false,
63-
enableHiding: false,
6466
},
6567
{
6668
accessorKey: "name",
69+
enableHiding: false,
6770
header: ({ column }) => {
6871
return <DataTableHeader column={column}>Name</DataTableHeader>;
6972
},
@@ -82,6 +85,7 @@ const PeersTableColumns: ColumnDef<Peer>[] = [
8285
accessorFn: (peer) => peer.connected,
8386
},
8487
{
88+
id: "ip",
8589
accessorKey: "ip",
8690
sortingFn: "text",
8791
},
@@ -94,18 +98,30 @@ const PeersTableColumns: ColumnDef<Peer>[] = [
9498
accessorFn: (peer) => (peer.user ? peer.user?.email : "Unknown"),
9599
},
96100
{
101+
id: "dns_label",
102+
enableHiding: false,
97103
accessorKey: "dns_label",
98104
header: ({ column }) => {
99105
return <DataTableHeader column={column}>Address</DataTableHeader>;
100106
},
101107
cell: ({ row }) => <PeerAddressCell peer={row.original} />,
102108
},
103109
{
110+
id: "group_name_strings",
104111
accessorKey: "group_name_strings",
105112
accessorFn: (peer) => peer.groups?.map((g) => g?.name || "").join(", "),
106113
sortingFn: "text",
107114
},
108115
{
116+
// used for exact group matching
117+
id: "exact_group_name_strings",
118+
accessorKey: "exact_group_name_strings",
119+
accessorFn: (peer) => peer.groups?.map((g) => g?.name || "").join("|"),
120+
sortingFn: "text",
121+
filterFn: "equals"
122+
},
123+
{
124+
id: "group_names",
109125
accessorKey: "group_names",
110126
accessorFn: (peer) => peer.groups?.map((g) => g?.name || ""),
111127
sortingFn: "text",
@@ -124,6 +140,7 @@ const PeersTableColumns: ColumnDef<Peer>[] = [
124140
),
125141
},
126142
{
143+
id: "last_seen",
127144
accessorKey: "last_seen",
128145
header: ({ column }) => {
129146
return <DataTableHeader column={column}>Last seen</DataTableHeader>;
@@ -132,13 +149,23 @@ const PeersTableColumns: ColumnDef<Peer>[] = [
132149
cell: ({ row }) => <PeerLastSeenCell peer={row.original} />,
133150
},
134151
{
152+
id: "os",
135153
accessorKey: "os",
136154
header: ({ column }) => {
137155
return <DataTableHeader column={column}>OS</DataTableHeader>;
138156
},
139-
cell: ({ row }) => <PeerOSCell os={row.original.os} />,
157+
cell: ({ row }) => <PeerOSCell os={row.original.os} serial={row.original.serial_number} />,
158+
},
159+
{
160+
id: "serial",
161+
header: ({ column }) => {
162+
return <DataTableHeader column={column}>Serial number</DataTableHeader>;
163+
},
164+
accessorFn: (peer) => peer.serial_number,
165+
sortingFn: "text",
140166
},
141167
{
168+
id: "version",
142169
accessorKey: "version",
143170
header: ({ column }) => {
144171
return <DataTableHeader column={column}>Version</DataTableHeader>;
@@ -165,8 +192,10 @@ const PeersTableColumns: ColumnDef<Peer>[] = [
165192
},
166193
{
167194
id: "actions",
195+
enableHiding: false,
168196
accessorKey: "id",
169197
header: "",
198+
170199
cell: ({ row }) => (
171200
<PeerProvider peer={row.original}>
172201
<PeerActionCell />
@@ -213,6 +242,25 @@ export default function PeersTable({ peers, isLoading, headingTarget }: Props) {
213242

214243
const [selectedRows, setSelectedRows] = useState<RowSelectionState>({});
215244

245+
const colVisibility: VisibilityState = {
246+
select: !isUser,
247+
actions: !isUser,
248+
groups: !isUser,
249+
connected: false,
250+
approval_required: false,
251+
252+
// hidden, but usefull for lookup
253+
serial: false,
254+
group_name_strings: false,
255+
exact_group_name_strings: false,
256+
group_names: false,
257+
ip: false,
258+
user_name: false,
259+
user_email: false,
260+
}
261+
262+
const [resultingColumnVisibility, setColumnVisibility] = useState(colVisibility);
263+
216264
const resetSelectedRows = () => {
217265
if (Object.keys(selectedRows).length > 0) {
218266
setSelectedRows({});
@@ -226,28 +274,19 @@ export default function PeersTable({ peers, isLoading, headingTarget }: Props) {
226274
onCanceled={() => setSelectedRows({})}
227275
/>
228276
<DataTable
277+
keepStateInLocalStorage={true}
229278
headingTarget={headingTarget}
230279
rowSelection={selectedRows}
231280
setRowSelection={setSelectedRows}
232281
useRowId={true}
233282
text={"Peers"}
234283
sorting={sorting}
235284
setSorting={setSorting}
285+
setColumnVisibility={setColumnVisibility}
236286
columns={PeersTableColumns}
237287
data={peers}
238-
searchPlaceholder={"Search by name, IP, owner or group..."}
239-
columnVisibility={{
240-
select: !isUser,
241-
connected: false,
242-
approval_required: false,
243-
group_name_strings: false,
244-
group_names: false,
245-
ip: false,
246-
user_name: false,
247-
user_email: false,
248-
actions: !isUser,
249-
groups: !isUser,
250-
}}
288+
searchPlaceholder={"Search by name, IP, Serial, owner or group..."}
289+
columnVisibility={resultingColumnVisibility}
251290
isLoading={isLoading}
252291
getStartedCard={
253292
<GetStartedTest
@@ -393,7 +432,7 @@ export default function PeersTable({ peers, isLoading, headingTarget }: Props) {
393432
table.setPageIndex(0);
394433
let current =
395434
table.getColumn("approval_required")?.getFilterValue() ===
396-
undefined
435+
undefined
397436
? true
398437
: undefined;
399438

@@ -412,7 +451,7 @@ export default function PeersTable({ peers, isLoading, headingTarget }: Props) {
412451
}}
413452
variant={
414453
table.getColumn("approval_required")?.getFilterValue() ===
415-
true
454+
true
416455
? "tertiary"
417456
: "secondary"
418457
}
@@ -446,6 +485,29 @@ export default function PeersTable({ peers, isLoading, headingTarget }: Props) {
446485
/>
447486
)}
448487

488+
<DataTableToolTipButton
489+
disabled={peers?.length == 0}
490+
onClick={() => {
491+
table.setPageIndex(0);
492+
if (table.getColumn("exact_group_name_strings")?.getFilterValue() !== undefined) {
493+
table.resetColumnFilters();
494+
return;
495+
}
496+
table.setColumnFilters([
497+
{
498+
id: "exact_group_name_strings",
499+
value: "All"
500+
}
501+
])
502+
}}
503+
variant={table.getColumn("exact_group_name_strings")?.getFilterValue() !== undefined
504+
? "tertiary"
505+
: "secondary"}
506+
title="filter peers assigned to the uniq group: 'All'"
507+
>
508+
All group only
509+
</DataTableToolTipButton>
510+
449511
<DataTableRefreshButton
450512
isDisabled={peers?.length == 0}
451513
onClick={() => {

0 commit comments

Comments
 (0)