From 073cad014811fa1103ce3f5342f63f21296cc231 Mon Sep 17 00:00:00 2001 From: selankon Date: Thu, 31 Oct 2024 16:36:05 +0100 Subject: [PATCH 1/5] chore(rx): order portsByRoleAndDevice --- plugins/lime-plugin-rx/src/sections/wired.tsx | 201 ++++++++++++++++-- 1 file changed, 179 insertions(+), 22 deletions(-) diff --git a/plugins/lime-plugin-rx/src/sections/wired.tsx b/plugins/lime-plugin-rx/src/sections/wired.tsx index 26b66da0..007778fb 100644 --- a/plugins/lime-plugin-rx/src/sections/wired.tsx +++ b/plugins/lime-plugin-rx/src/sections/wired.tsx @@ -9,40 +9,197 @@ import { PortsIcon } from "plugins/lime-plugin-rx/src/icons/portsIcon"; import { useNodeStatus } from "plugins/lime-plugin-rx/src/rxQueries"; import { SwitchStatus } from "plugins/lime-plugin-rx/src/rxTypes"; +const liro1 = [ + { + device: "eth0.1", + num: 5, + role: "wan", + link: "down", + }, + { + device: "eth0.1", + num: 0, + role: "cpu", + link: "up", + }, + { + device: "eth1.2", + num: 4, + role: "lan", + link: "up", + }, + { + device: "eth1.2", + num: 6, + role: "cpu", + link: "up", + }, +]; + +const mocked = [ + { + device: "eth1", + num: "wan", + role: "wan", + }, + { + device: "eth0", + num: "lan", + role: "lan", + }, + { + device: "eth2", + num: "wan", + role: "wan", + }, + { + device: "eth30", + num: "lan", + role: "lan", + }, + { + device: "eth214", + num: "wan", + role: "wan", + }, + { + device: "eth05", + num: "lan", + role: "lan", + }, +]; + +const tplink = [ + { + device: "eth0.1", + num: 1, + role: "lan", + link: "down", + }, + { + device: "eth0.1", + num: 2, + role: "lan", + link: "up", + }, + { + device: "eth0.1", + num: 3, + role: "lan", + link: "down", + }, + { + device: "eth0.1", + num: 4, + role: "lan", + link: "down", + }, + { + device: "eth0.1", + num: 0, + role: "cpu", + link: "up", + }, + // Modifactions + { + device: "eth0.3", + num: 0, + role: "lan", + link: "up", + }, + { + device: "eth0.3", + num: 1, + role: "lan", + link: "up", + }, + { + device: "eth0.2", + num: 0, + role: "wan", + link: "up", + }, + + { + device: "eth0.2", + num: 1, + role: "wan", + link: "up", + }, +]; + +type PortsByRoleAndDevice = { + [port: string]: { [device: string]: SwitchStatus[] }; +}; + const Ports = ({ switches }: { switches: SwitchStatus[] }) => { - const ports = switches.reduce((acc, obj) => { - const { role } = obj; - if (!acc[role]) { - acc[role] = []; - } - acc[role].push(obj); - return acc; - }, {}); + const portsByRoleAndDevice: PortsByRoleAndDevice = switches.reduce( + (acc, obj) => { + const { role, device } = obj; + + if (!acc[role]) { + acc[role] = {}; + } + if (!acc[role][device]) { + acc[role][device] = []; + } + acc[role][device].push(obj); + + return acc; + }, + {} + ); + return (
- {Object.keys(ports).map((role) => { + {Object.entries(portsByRoleAndDevice).map(([role, devices]) => { if (role.toLowerCase() === "cpu") return null; return (
+ {/*Print role name*/}

{role.toUpperCase()}

-

{ports[role][0].device.toLowerCase()}

- {ports[role].map((port) => { - const link = - port.link?.toLowerCase() === "up" - ? "fill-primary-dark" - : "fill-disabled"; - return ( -
- + {Object.entries(devices).map( + ([device, ports], k) => ( + // Print device name +
+

{device.toLowerCase()}

+
+ {/*Print port group*/} + {ports.map((port) => { + let link = "fill-disabled"; + if ( + port.link?.toLowerCase() === + "up" + ) + link = "fill-primary-dark"; + return ( +
+ +
+ ); + })} +
- ); - })} + ) + )}
); From 583b07dadcfe7be4806890a097cf17d4e9773047 Mon Sep 17 00:00:00 2001 From: selankon Date: Mon, 4 Nov 2024 08:48:10 +0100 Subject: [PATCH 2/5] chore(rx): implement change role ux --- plugins/lime-plugin-rx/src/sections/wired.tsx | 161 +++++++++++------- 1 file changed, 102 insertions(+), 59 deletions(-) diff --git a/plugins/lime-plugin-rx/src/sections/wired.tsx b/plugins/lime-plugin-rx/src/sections/wired.tsx index 007778fb..c93e4573 100644 --- a/plugins/lime-plugin-rx/src/sections/wired.tsx +++ b/plugins/lime-plugin-rx/src/sections/wired.tsx @@ -1,4 +1,8 @@ import { Trans } from "@lingui/macro"; +import { useEffect, useState } from "preact/hooks"; + +import Modal, { ModalProps } from "components/Modal/Modal"; +import { useDisclosure } from "components/Modal/useDisclosure"; import { IconsClassName, @@ -128,78 +132,117 @@ const tplink = [ }, ]; -type PortsByRoleAndDevice = { - [port: string]: { [device: string]: SwitchStatus[] }; +type PortsByDevice = { + [device: string]: SwitchStatus[]; }; -const Ports = ({ switches }: { switches: SwitchStatus[] }) => { - const portsByRoleAndDevice: PortsByRoleAndDevice = switches.reduce( - (acc, obj) => { - const { role, device } = obj; +enum SupportedPortRoles { + WAN = "wan", + LAN = "lan", + MESH = "mesh", +} - if (!acc[role]) { - acc[role] = {}; - } - if (!acc[role][device]) { - acc[role][device] = []; - } - acc[role][device].push(obj); +const ChangeRoleConfirmationModal = ({ + newRole, + ...rest +}: { newRole: string } & ModalProps) => { + return ( + Changing role to {newRole}}> +
+

+ + Changing the role of a port may cause network + interruptions. Are you sure you want to continue? + +

+
+
+ ); +}; - return acc; +const PortRoleSelector = ({ port }: { port: SwitchStatus }) => { + const [newRole, setNewRole] = useState(""); + const { open, onOpen, onClose } = useDisclosure({ + onClose() { + setNewRole(""); }, - {} + }); + + const changeRole = async () => { + // await two seconds and then resolve + return new Promise((resolve) => setTimeout(resolve, 2000)); + }; + + useEffect(() => { + if (newRole) { + onOpen(); + } + }, [newRole]); + + const role = port.role.toLowerCase(); + if (role === "cpu") { + return null; + } + let link = "fill-disabled"; + if (port.link?.toLowerCase() === "up") link = "fill-primary-dark"; + return ( +
+ + + +
); +}; +const Ports = ({ switches: s }: { switches: SwitchStatus[] }) => { + const switches = tplink; + const ports: PortsByDevice = switches.reduce((acc, obj) => { + const { device } = obj; + if (!acc[device]) { + acc[device] = []; + } + acc[device].push(obj); + return acc; + }, {}); return (
- {Object.entries(portsByRoleAndDevice).map(([role, devices]) => { - if (role.toLowerCase() === "cpu") return null; + {Object.entries(ports).map(([device, ports], k) => { return ( -
- {/*Print role name*/} -

{role.toUpperCase()}

-
- {Object.entries(devices).map( - ([device, ports], k) => ( - // Print device name -
-

{device.toLowerCase()}

-
- {/*Print port group*/} - {ports.map((port) => { - let link = "fill-disabled"; - if ( - port.link?.toLowerCase() === - "up" - ) - link = "fill-primary-dark"; - return ( -
- -
- ); - })} -
-
- ) - )} +
+

{device.toUpperCase()}

+
+ {ports.map((port, k) => ( + + ))}
); From 76c0165669c550fb9fd9c8ce023103a2293f75c5 Mon Sep 17 00:00:00 2001 From: selankon Date: Mon, 4 Nov 2024 14:58:14 +0100 Subject: [PATCH 3/5] chore(rx): implement setPortRole mutation --- plugins/lime-plugin-rx/src/rxApi.ts | 15 ++++++++ plugins/lime-plugin-rx/src/rxQueries.ts | 29 +++++++++++++- plugins/lime-plugin-rx/src/rxTypes.ts | 6 +++ plugins/lime-plugin-rx/src/sections/wired.tsx | 38 ++++++++++++------- 4 files changed, 73 insertions(+), 15 deletions(-) diff --git a/plugins/lime-plugin-rx/src/rxApi.ts b/plugins/lime-plugin-rx/src/rxApi.ts index b53fc4f0..04aaaff5 100644 --- a/plugins/lime-plugin-rx/src/rxApi.ts +++ b/plugins/lime-plugin-rx/src/rxApi.ts @@ -1,6 +1,8 @@ import { IGetInternetStatus, StatusResponse, + SupportedPortRoles, + SwitchStatus, } from "plugins/lime-plugin-rx/src/rxTypes"; import api from "utils/uhttpd.service"; @@ -10,3 +12,16 @@ export const getNodeStatus = (): Promise => export const getInternetStatus = (): Promise => api.call("lime-metrics", "get_internet_status", {}); + +export type SetPortRoleArgs = { role: SupportedPortRoles } & Omit< + SwitchStatus, + "role" +>; + +export const setPortRole = async ( + newStatus: SetPortRoleArgs +): Promise => { + console.log("Setting port role", newStatus); + await new Promise((resolve) => setTimeout(resolve, 2000)); + return true; +}; diff --git a/plugins/lime-plugin-rx/src/rxQueries.ts b/plugins/lime-plugin-rx/src/rxQueries.ts index 41a3b298..0b8d9f98 100644 --- a/plugins/lime-plugin-rx/src/rxQueries.ts +++ b/plugins/lime-plugin-rx/src/rxQueries.ts @@ -1,6 +1,18 @@ -import { useQuery } from "@tanstack/react-query"; +import { MutationKey } from "@tanstack/query-core/src/types"; +import { + UseMutationOptions, + useMutation, + useQuery, +} from "@tanstack/react-query"; -import { getInternetStatus, getNodeStatus } from "./rxApi"; +import { SupportedPortRoles } from "plugins/lime-plugin-rx/src/rxTypes"; + +import { + SetPortRoleArgs, + getInternetStatus, + getNodeStatus, + setPortRole, +} from "./rxApi"; const refetchInterval = 2000; @@ -24,3 +36,16 @@ export function useInternetStatus(params?) { ...params, }); } + +export const useSetPortRole = ( + options?: Omit< + UseMutationOptions, + "mutationFn" | "mutationKey" + > +) => { + return useMutation({ + mutationFn: setPortRole, + mutationKey: ["useSetPortRole"] as MutationKey, + ...options, + }); +}; diff --git a/plugins/lime-plugin-rx/src/rxTypes.ts b/plugins/lime-plugin-rx/src/rxTypes.ts index 6a5ff82a..eb7b7889 100644 --- a/plugins/lime-plugin-rx/src/rxTypes.ts +++ b/plugins/lime-plugin-rx/src/rxTypes.ts @@ -53,3 +53,9 @@ export interface IGetInternetStatus { IPv4: { working: boolean }; status: string; } + +export enum SupportedPortRoles { + WAN = "wan", + LAN = "lan", + MESH = "mesh", +} diff --git a/plugins/lime-plugin-rx/src/sections/wired.tsx b/plugins/lime-plugin-rx/src/sections/wired.tsx index c93e4573..3bf55195 100644 --- a/plugins/lime-plugin-rx/src/sections/wired.tsx +++ b/plugins/lime-plugin-rx/src/sections/wired.tsx @@ -10,8 +10,16 @@ import { SectionTitle, } from "plugins/lime-plugin-rx/src/components/components"; import { PortsIcon } from "plugins/lime-plugin-rx/src/icons/portsIcon"; -import { useNodeStatus } from "plugins/lime-plugin-rx/src/rxQueries"; -import { SwitchStatus } from "plugins/lime-plugin-rx/src/rxTypes"; +import { + useNodeStatus, + useSetPortRole, +} from "plugins/lime-plugin-rx/src/rxQueries"; +import { + SupportedPortRoles, + SwitchStatus, +} from "plugins/lime-plugin-rx/src/rxTypes"; + +import queryCache from "utils/queryCache"; const liro1 = [ { @@ -136,12 +144,6 @@ type PortsByDevice = { [device: string]: SwitchStatus[]; }; -enum SupportedPortRoles { - WAN = "wan", - LAN = "lan", - MESH = "mesh", -} - const ChangeRoleConfirmationModal = ({ newRole, ...rest @@ -161,16 +163,23 @@ const ChangeRoleConfirmationModal = ({ }; const PortRoleSelector = ({ port }: { port: SwitchStatus }) => { - const [newRole, setNewRole] = useState(""); + const { mutateAsync } = useSetPortRole({ + onSettled: () => { + queryCache.invalidateQueries({ + queryKey: ["lime-rx", "node-status"], + }); + }, + }); + const [newRole, setNewRole] = useState(); const { open, onOpen, onClose } = useDisclosure({ onClose() { - setNewRole(""); + setNewRole(null); }, }); const changeRole = async () => { - // await two seconds and then resolve - return new Promise((resolve) => setTimeout(resolve, 2000)); + await mutateAsync({ ...port, role: newRole }); + onClose(); }; useEffect(() => { @@ -192,7 +201,10 @@ const PortRoleSelector = ({ port }: { port: SwitchStatus }) => { className={"pl-2 text-center"} value={role} onChange={(e) => { - setNewRole((e.target as HTMLSelectElement).value); + setNewRole( + (e.target as HTMLSelectElement) + .value as SupportedPortRoles + ); }} > From 5b421dc2bb9c0735bc126ac0118828dd712ca81e Mon Sep 17 00:00:00 2001 From: selankon Date: Tue, 12 Nov 2024 12:29:24 -0300 Subject: [PATCH 4/5] chore(rx): show ips --- plugins/lime-plugin-rx/src/sections/system.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/plugins/lime-plugin-rx/src/sections/system.tsx b/plugins/lime-plugin-rx/src/sections/system.tsx index 25315e30..53b2cdd9 100644 --- a/plugins/lime-plugin-rx/src/sections/system.tsx +++ b/plugins/lime-plugin-rx/src/sections/system.tsx @@ -40,6 +40,11 @@ const SystemInfo = () => { const boardData = bd as IGetBoardDataResponse; const secNum = parseInt(node?.uptime, 10); + const ips = + node?.ips.map((ip) => ({ + label: `IPv${ip.version}`, + value: ip.address, + })) || []; const attributes = [ { label: t`Uptime`, @@ -47,7 +52,9 @@ const SystemInfo = () => { }, { label: t`Device`, value: boardData.board_name }, { label: t`Firmware`, value: boardData.release.description }, + ...ips, ]; + return (
From 8d5920521540f7bb0a64844c06ef5e9dea805d5f Mon Sep 17 00:00:00 2001 From: selankon Date: Tue, 3 Dec 2024 10:51:30 -0300 Subject: [PATCH 5/5] chore(rx): port change error handling --- plugins/lime-plugin-rx/src/sections/wired.tsx | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/plugins/lime-plugin-rx/src/sections/wired.tsx b/plugins/lime-plugin-rx/src/sections/wired.tsx index 3bf55195..46a82652 100644 --- a/plugins/lime-plugin-rx/src/sections/wired.tsx +++ b/plugins/lime-plugin-rx/src/sections/wired.tsx @@ -3,6 +3,7 @@ import { useEffect, useState } from "preact/hooks"; import Modal, { ModalProps } from "components/Modal/Modal"; import { useDisclosure } from "components/Modal/useDisclosure"; +import { useToast } from "components/toast/toastProvider"; import { IconsClassName, @@ -149,7 +150,11 @@ const ChangeRoleConfirmationModal = ({ ...rest }: { newRole: string } & ModalProps) => { return ( - Changing role to {newRole}}> + Changing role to {newRole}} + successBtnText={Confirm} + >

@@ -163,23 +168,36 @@ const ChangeRoleConfirmationModal = ({ }; const PortRoleSelector = ({ port }: { port: SwitchStatus }) => { + const { showToast } = useToast(); + const [newRole, setNewRole] = useState(); + const { open, onOpen, onClose } = useDisclosure({ + onClose() { + setNewRole(null); + }, + }); const { mutateAsync } = useSetPortRole({ onSettled: () => { queryCache.invalidateQueries({ queryKey: ["lime-rx", "node-status"], }); + onClose(); }, - }); - const [newRole, setNewRole] = useState(); - const { open, onOpen, onClose } = useDisclosure({ - onClose() { - setNewRole(null); + onError: (error) => { + console.error(error); + showToast({ + type: "error", + text: ( +

+ Error changing role: + {error.message} +
+ ), + }); }, }); const changeRole = async () => { await mutateAsync({ ...port, role: newRole }); - onClose(); }; useEffect(() => {