Skip to content

Commit 9d41021

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

File tree

4 files changed

+235
-101
lines changed

4 files changed

+235
-101
lines changed

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/groups/GroupSelector.tsx

Lines changed: 69 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,23 @@ import { Group } from "@/interfaces/Group";
2121

2222
interface MultiSelectProps {
2323
values: string[];
24-
onChange: (items: string[]) => void;
24+
exactValue?: string;
25+
onChange: (items: string[], exactItem?: string) => void;
2526
disabled?: boolean;
2627
popoverWidth?: "auto" | number;
2728
groups: Group[] | undefined;
29+
unassignedCount?: number;
30+
defaultGroupName?: string;
2831
}
2932
export function GroupSelector({
3033
onChange,
3134
values,
35+
exactValue,
3236
disabled = false,
3337
popoverWidth = 400,
3438
groups,
39+
unassignedCount,
40+
defaultGroupName = "All", //defined as a property, no clue if this value may change in the future
3541
}: MultiSelectProps) {
3642
const searchRef = React.useRef<HTMLInputElement>(null);
3743
const [inputRef, { width }] = useElementSize<HTMLButtonElement>();
@@ -40,9 +46,20 @@ export function GroupSelector({
4046
const toggle = (code: string) => {
4147
const isSelected = values.find((c) => c == code) != undefined;
4248
if (isSelected) {
43-
onChange && onChange(values.filter((c) => c != code));
49+
onChange && onChange(values.filter((c) => c != code), undefined);
4450
} else {
45-
onChange && onChange([...values, code]);
51+
onChange && onChange([...values, code], undefined);
52+
setSearch("");
53+
}
54+
};
55+
56+
const toggleExactGroup = (code: string) => {
57+
const isSelected = exactValue == code;
58+
if (isSelected) {
59+
onChange && onChange([], undefined);
60+
setSearch("");
61+
} else {
62+
onChange && onChange([], code);
4663
setSearch("");
4764
}
4865
};
@@ -65,11 +82,13 @@ export function GroupSelector({
6582
<Button variant={"secondary"} disabled={disabled} ref={inputRef}>
6683
<FolderGit2 size={16} className={"shrink-0"} />
6784
<div className={"w-full flex justify-between"}>
68-
{values.length > 0 ? (
69-
<div>{values.length} Group(s)</div>
70-
) : (
71-
"All Groups"
72-
)}
85+
{
86+
exactValue != undefined
87+
? ("Unassigned peers")
88+
: values.length > 0
89+
? (`${values.length} Group(s)`)
90+
: ("All Groups")
91+
}
7392
<div className={"pl-2"}>
7493
<ChevronsUpDown size={18} className={"shrink-0"} />
7594
</div>
@@ -132,7 +151,6 @@ export function GroupSelector({
132151
</div>
133152
</div>
134153
</div>
135-
136154
<ScrollArea
137155
className={
138156
"max-h-[380px] overflow-y-auto flex flex-col gap-1 pl-2 py-2 pr-3"
@@ -141,7 +159,48 @@ export function GroupSelector({
141159
<CommandGroup>
142160
<div className={""}>
143161
<div className={"grid grid-cols-1 gap-1"}>
144-
{orderBy(groups, "name")?.map((item) => {
162+
<CommandItem
163+
className={"p-1"}
164+
onSelect={() => {
165+
toggleExactGroup( defaultGroupName);
166+
searchRef.current?.focus();
167+
}}
168+
onClick={(e) => e.preventDefault()}
169+
>
170+
<div
171+
className={
172+
"text-neutral-500 dark:text-nb-gray-300 font-medium flex items-center gap-3 py-1 px-1 w-full"
173+
}
174+
>
175+
<Checkbox checked={exactValue == defaultGroupName}/>
176+
<div
177+
className={
178+
"flex justify-between items-center w-full"
179+
}
180+
>
181+
<div
182+
className={
183+
"flex items-center gap-2 whitespace-nowrap text-sm"
184+
}
185+
>
186+
<FolderGit2 size={13} className={"shrink-0"} />
187+
<TextWithTooltip text={"Unassigned peers"} />
188+
</div>
189+
<div
190+
className={
191+
"flex items-center gap-2 text-xs text-nb-gray-200/60"
192+
}
193+
>
194+
<MonitorSmartphoneIcon size={13} />
195+
{unassignedCount} Peer(s)
196+
</div>
197+
</div>
198+
</div>
199+
</CommandItem>
200+
<hr />
201+
{orderBy(groups, "name")
202+
?.filter((group) => group.name != defaultGroupName) // Ignore default group
203+
?.map((item) => {
145204
const value = item?.name || "";
146205
if (value === "") return null;
147206
const isSelected =

src/modules/peers/PeerOSCell.tsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
TooltipProvider,
55
TooltipTrigger,
66
} from "@components/Tooltip";
7+
import { Barcode, Laptop } from "lucide-react";
78
import Image from "next/image";
89
import React, { useMemo } from "react";
910
import { FaWindows } from "react-icons/fa6";
@@ -14,7 +15,7 @@ import FreeBSDLogo from "@/assets/os-icons/FreeBSD.png";
1415
import { getOperatingSystem } from "@/hooks/useOperatingSystem";
1516
import { OperatingSystem } from "@/interfaces/OperatingSystem";
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,50 @@ 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+
{ (serial !== undefined) &&
44+
<ListItem
45+
46+
icon={<Barcode size={14} />}
47+
label={"Serial"}
48+
value={serial}
49+
/>
50+
}
3851
</TooltipContent>
3952
</Tooltip>
4053
</TooltipProvider>
4154
);
4255
}
4356

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

0 commit comments

Comments
 (0)