diff --git a/react-ui/src/components/devices/view/boxes/devices.box.view.tsx b/react-ui/src/components/devices/view/boxes/devices.box.view.tsx index 0204f9707feac48810e77c729a1b929bffe55cd5..af7ae4db2652b2822e3d922d7f9d87ed4d816f72 100755 --- a/react-ui/src/components/devices/view/boxes/devices.box.view.tsx +++ b/react-ui/src/components/devices/view/boxes/devices.box.view.tsx @@ -1,5 +1,5 @@ import { useDeviceBoxViewModel } from '@component/devices/view_model/device.box.viewmodel' -import { faPlus } from '@fortawesome/free-solid-svg-icons' +import { faPenToSquare, faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' import { insertMarkTags } from '@helper/text' import { Scrollbar } from '@shared/components/scrollbar/Scrollbar.view' @@ -22,7 +22,8 @@ export const DeviceList = ({ searchRef }: { searchRef: RefObject<HTMLInputElemen closeModal, searchValue, handleSearch, - } = useDeviceBoxViewModel(searchRef) + removeDevice, + } = useDeviceBoxViewModel() const cropUUID = (uuid: string): string => { return uuid.substring(0, 3) + '...' + uuid.substring(uuid.length - 3, uuid.length) @@ -44,8 +45,8 @@ export const DeviceList = ({ searchRef }: { searchRef: RefObject<HTMLInputElemen isSelected && 'bg-gradient-fade py-2' } ${!isSelected && 'text-disabled disabled-hover'}`} onClick={() => handleItemClick(device)}> - <Row className="align-items-center clickable"> - <Col xs={12} sm={5}> + <Row className="align-items-center clickable device-row"> + <Col xs={12} sm={4}> <span dangerouslySetInnerHTML={{ __html: searchValue @@ -76,6 +77,16 @@ export const DeviceList = ({ searchRef }: { searchRef: RefObject<HTMLInputElemen }} /> </Col> + <Col xs={12} sm={1}> + <div className="d-flex icons justify-content-end align-items-center"> + <FontAwesomeIcon icon={faPenToSquare} size="sm" /> + <FontAwesomeIcon + icon={faTrashCan} + size="sm" + onClick={() => removeDevice(device)} + /> + </div> + </Col> </Row> </div> ) @@ -119,7 +130,7 @@ export const DeviceList = ({ searchRef }: { searchRef: RefObject<HTMLInputElemen {/* Fixed header */} <div className="sticky-top bg-white border-bottom border-primary"> <Row className="px-2 py-2 mx-0"> - <Col xs={12} sm={5}> + <Col xs={12} sm={4}> <span className="font-medium">{t('device.table.header.name')}</span> </Col> <Col xs={12} sm={3}> diff --git a/react-ui/src/components/devices/view/boxes/information.box.view.tsx b/react-ui/src/components/devices/view/boxes/information.box.view.tsx index 6df0e33332d1be408d7c1ea4ad0961973ce425b0..e751af7ed3d2e361e7f206b763f0199a752f09d3 100644 --- a/react-ui/src/components/devices/view/boxes/information.box.view.tsx +++ b/react-ui/src/components/devices/view/boxes/information.box.view.tsx @@ -24,7 +24,7 @@ export const DeviceListCollapsable = ({ search }: DeviceListCollapsableProps) => const user = pnds.find(pnd => pnd.id === selected?.device.pid) const username = user?.name || '' const deviceId = selected?.device.id || '' - const json = selected?.json || {} + const json = JSON.parse(selected?.mne?.model || '{}') const metadataKey = Object.keys(json).at(2) as keyof typeof json const metadataObject = (json[metadataKey] as JSON) || {} diff --git a/react-ui/src/components/devices/view/device.scss b/react-ui/src/components/devices/view/device.scss index 7358cdcf45977dd521e27fbde902cee1b3fd12cd..b037e6b162ae49496706cc8c40ae73515c358cb0 100755 --- a/react-ui/src/components/devices/view/device.scss +++ b/react-ui/src/components/devices/view/device.scss @@ -93,3 +93,10 @@ $primary-color: rgba(map-get($theme-colors, "dark"), 0.1); background: linear-gradient(to bottom, rgb(223, 223, 223) 1%, white 100%); } + +.device-row:hover .icons { + color: map-get($theme-colors, "black") !important; + opacity: 100%; + transition: gap 0.3s; + gap: 0.7em; +} diff --git a/react-ui/src/components/devices/view_model/device.box.viewmodel.ts b/react-ui/src/components/devices/view_model/device.box.viewmodel.ts index 39a4bd05117d234da0418c147f6f4ba1bd71c699..ba198a5de5ac75b079c414ba85538322771ea500 100644 --- a/react-ui/src/components/devices/view_model/device.box.viewmodel.ts +++ b/react-ui/src/components/devices/view_model/device.box.viewmodel.ts @@ -1,11 +1,14 @@ -// devices.box.viewmodel.ts +import { NetworkelementFlattenedManagedNetworkElement, NetworkElementServiceDeleteApiArg, useNetworkElementServiceDeleteMutation } from "@api/api"; import { useAppDispatch, useAppSelector } from "@hooks"; -import { RefObject, useCallback, useMemo, useState } from "react"; -import { Device, setSelectedDevice } from "../reducer/device.reducer"; +import { useCallback, useMemo, useState } from "react"; +import { toast } from "react-toastify"; +import { Device, setDevices, setSelectedDevice } from "../reducer/device.reducer"; import { fetchPluginsThunk } from "../routines/plugin.routine"; -export const useDeviceBoxViewModel = (searchRef: RefObject<HTMLInputElement>) => { +export const useDeviceBoxViewModel = () => { const dispatch = useAppDispatch(); + + const [deleteNetworkElement] = useNetworkElementServiceDeleteMutation() const { devices, pnds, selected: selectedDevice } = useAppSelector( (state) => state.device ); @@ -29,6 +32,33 @@ export const useDeviceBoxViewModel = (searchRef: RefObject<HTMLInputElement>) => setAddModal(false); }, []); + const removeDevice = useCallback(async (device: NetworkelementFlattenedManagedNetworkElement) => { + if (!device?.id) { + toast.error("Error: Please reload the page and try it again") + return + } + const pnd = pnds.find(pnd => pnd.id === device.pid) + if (!pnd) { + toast.error("Error: Please reload the page and try it again") + return + } + + const payload: NetworkElementServiceDeleteApiArg = { + mneid: device.id, + timestamp: new Date().getTime().toString(), + pid: pnd.id + } + try { + await deleteNetworkElement(payload).unwrap() + const newDevices = devices.filter(_device => _device.id !== device.id) + dispatch(setDevices(newDevices)) + toast.success("Device successfully deleted") + } catch (error) { + toast.error("Error: Delete was not successful. Please reload the page and try it again") + } + }, []) + + const filteredDevices = useMemo(() => { if (!searchValue) return devices; @@ -40,7 +70,7 @@ export const useDeviceBoxViewModel = (searchRef: RefObject<HTMLInputElement>) => user?.name?.toLowerCase().includes(searchValue.toLowerCase()) ); }); - }, [devices, pnds, searchValue]); // Now depends on searchValue instead of ref + }, [devices, pnds, searchValue]); return { filteredDevices, @@ -52,5 +82,6 @@ export const useDeviceBoxViewModel = (searchRef: RefObject<HTMLInputElement>) => closeModal, searchValue, handleSearch, + removeDevice }; }; \ No newline at end of file diff --git a/react-ui/src/components/devices/view_model/information.box.viewmodel.ts b/react-ui/src/components/devices/view_model/information.box.viewmodel.ts index a328ca1fa6151ee82a8203d11d2b71c3b71e577b..360e29ff58a312c6ef8d27c3ffe983b87bf8bc35 100755 --- a/react-ui/src/components/devices/view_model/information.box.viewmodel.ts +++ b/react-ui/src/components/devices/view_model/information.box.viewmodel.ts @@ -7,11 +7,7 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "react-toastify"; -<<<<<<< HEAD:react-ui/src/components/devices/view_model/information.box.viewmodel.ts export const useInformationViewModel = (searchRef, listRef) => { -======= -export const useDeviceTableViewModel = (searchRef, tableRef) => { ->>>>>>> parent of 9ea90588 (Merge branch 'master' into ui-implement-metadata-screen):react-ui/src/components/devices/view_model/device.table.viewmodel.ts const [searchTerm, setSearchTerm] = useState(''); const dispatch = useAppDispatch(); const { subscribe } = useMenu(); @@ -20,8 +16,12 @@ export const useDeviceTableViewModel = (searchRef, tableRef) => { const registerMenuOptions = () => { + if (!subscribe) { + return + } + const subscription = subscribe({ - target: tableRef.current, + target: listRef.current, actions: [ { key: t('device.table.actions.copy'), @@ -67,7 +67,7 @@ export const useDeviceTableViewModel = (searchRef, tableRef) => { // seperate use effect to rerun this after tableref and subscribe are initialized useEffect(() => { - if (!subscribe || !tableRef.current) { + if (!subscribe || !listRef.current) { return } @@ -76,7 +76,7 @@ export const useDeviceTableViewModel = (searchRef, tableRef) => { return () => { unsubscribe() } - }, [tableRef, subscribe]) + }, [listRef, subscribe]) useEffect(() => { diff --git a/react-ui/src/shared/components/json_viewer/view/json_viewer.scss b/react-ui/src/shared/components/json_viewer/view/json_viewer.scss index 83276647006d372839ad9dac82edb0f68379d502..eef810b30d020e89cd982b3b70708d29eb819bce 100755 --- a/react-ui/src/shared/components/json_viewer/view/json_viewer.scss +++ b/react-ui/src/shared/components/json_viewer/view/json_viewer.scss @@ -63,8 +63,6 @@ td .icon { color: lighten(map-get($map: $theme-colors, $key: "dark"), 20%); gap: 0.5em; opacity: 0%; - - min-width: 3em; } span.highlight {