From d483240dcc998da8d7ade6cc6cd26f67ff21123c Mon Sep 17 00:00:00 2001 From: German Ferrero Date: Thu, 22 Apr 2021 18:20:55 -0300 Subject: [PATCH] feat(reachable-nodes): add screen for reachable/unreachable nodes And complete translations --- i18n/generic.json | 16 +-- i18n/translations/es.json | 22 ++- .../src/deleteNodesMenu.js | 2 +- plugins/lime-plugin-reachable-nodes/index.js | 10 +- .../networkNodes.spec.js | 134 ------------------ .../networkNodes.stories.js | 34 ----- .../reachableNodes.spec.js | 68 +++++++++ .../reachableNodes.stories.js | 29 ++++ .../src/networkNodesApi.js | 9 -- .../src/networkNodesApi.spec.js | 49 ------- .../src/networkNodesMenu.js | 7 - .../src/networkNodesPage.js | 85 ----------- .../src/networkNodesQueries.js | 16 --- .../src/reachableNodesMenu.js | 7 + .../src/reachableNodesPage.js | 83 +++++++++++ .../src/style.less | 15 -- src/components/help/style.less | 2 +- src/config.js | 1 + 18 files changed, 210 insertions(+), 379 deletions(-) delete mode 100644 plugins/lime-plugin-reachable-nodes/networkNodes.spec.js delete mode 100644 plugins/lime-plugin-reachable-nodes/networkNodes.stories.js create mode 100644 plugins/lime-plugin-reachable-nodes/reachableNodes.spec.js create mode 100644 plugins/lime-plugin-reachable-nodes/reachableNodes.stories.js delete mode 100644 plugins/lime-plugin-reachable-nodes/src/networkNodesApi.js delete mode 100644 plugins/lime-plugin-reachable-nodes/src/networkNodesApi.spec.js delete mode 100644 plugins/lime-plugin-reachable-nodes/src/networkNodesMenu.js delete mode 100644 plugins/lime-plugin-reachable-nodes/src/networkNodesPage.js delete mode 100644 plugins/lime-plugin-reachable-nodes/src/networkNodesQueries.js create mode 100644 plugins/lime-plugin-reachable-nodes/src/reachableNodesMenu.js create mode 100644 plugins/lime-plugin-reachable-nodes/src/reachableNodesPage.js delete mode 100644 plugins/lime-plugin-reachable-nodes/src/style.less diff --git a/i18n/generic.json b/i18n/generic.json index e86f61e0..8497de20 100644 --- a/i18n/generic.json +++ b/i18n/generic.json @@ -28,8 +28,6 @@ "confirm_6556b3a6": "Confirm", "confirm_location_2fe5ae11": "confirm location", "congratulations_ffe43bf9": "Congratulations", - "connected_howmany_228826ea": "Connected (%{howMany})", - "connected_nodes_4b7e6f49": "Connected Nodes", "count_days_de0c6a32": { "one": "1 days", "other": "%{count} days" @@ -61,8 +59,6 @@ "delete_a6efa79d": "Delete", "delete_nodes_f63ec0d5": "Delete Nodes", "device_95d26d94": "Device", - "disconnected_howmany_10fc7bd5": "Disconnected (%{howMany})", - "disconnected_nodes_88f80d1e": "Disconnected Nodes", "don_t_show_this_message_again_9950c20": "Don't show this message again", "download_c7ffdfb9": "Download", "downloading_1e41f805": "Downloading", @@ -75,7 +71,6 @@ "full_path_metrics_2859608f": "Full path metrics", "go_64ecd1fd": "Go!", "go_to_community_view_d12b8d67": "Go to Community View", - "go_to_delete_nodes_1203128b": "Go to Delete Nodes", "go_to_node_view_26ba929d": "Go to Node View", "ground_routing_12ab04c9": "Ground Routing", "ground_routing_configuration_3f4fa9c1": "Ground Routing configuration", @@ -83,7 +78,6 @@ "hide_console_9bbb309e": "Hide Console", "host_name_d865cef3": "Host name", "i_don_t_know_the_shared_password_336b198": "I don't know the shared password", - "if_some_of_these_nodes_no_longer_belong_to_the_net_a75d316f": "If some of these nodes no longer belong to the network you can delete them from Delete Nodes.", "interface_177dac54": "Interface", "internet_connection_fda60ffa": "Internet connection", "ip_addresses_440ac240": "IP Addresses", @@ -129,10 +123,11 @@ "radio_2573b256": "Radio", "re_enter_password_49757ed": "Re-enter Password", "re_enter_the_shared_password_20f09406": "Re-enter the shared password", + "reachable_howmany_6f891e31": "Reachable (%{howMany})", + "reachable_nodes_748c93f0": "Reachable Nodes", "reload_3e45154f": "Reload", "reload_page_2d381199": "Reload page", "remote_support_9ba7a3a7": "Remote Support", - "remove_nodes_2dd26e14": "Remove Nodes", "rescan_dff042fc": "Rescan", "retry_ebd5f8ba": "Retry", "revert_702e7694": "Revert", @@ -175,10 +170,11 @@ "there_s_an_active_remote_support_session_4a40a8bb": "There's an active remote support session", "there_s_no_open_session_for_remote_support_click_a_efd0d415": "There's no open session for remote support. Click at Create Session to begin one", "these_are_the_nodes_associated_on_this_radio_3d302167": "These are the nodes associated on this radio", - "these_are_the_nodes_with_which_you_do_not_have_con_ef5cc209": "These are the nodes with which you do not have connectivity, it is possible that they are not turned on or a link to reach them is down.", - "these_are_the_nodes_with_which_you_have_connectivi_ef11819b": "These are the nodes with which you have connectivity, i.e. there is a working path from your node to each of them.", + "these_are_the_nodes_that_can_be_reached_from_your__4c524abe": "These are the nodes that can be reached from your node, i.e. there is a working path from your node to each of them.", + "these_are_the_nodes_that_can_t_be_reached_from_you_dbbf9032": "These are the nodes that can't be reached from your node, it is possible that they are not turned on or a link to reach them is down.", "this_device_does_not_support_secure_rollback_to_pr_1c167a2c": "This device does not support secure rollback to previous version if something goes wrong", "this_device_supports_secure_rollback_to_previous_v_a60ddbcb": "This device supports secure rollback to previous version if something goes wrong", + "this_information_is_synced_periodically_and_can_be_8b74cb8c": "This information is synced periodically and can be outdated by some minutes", "this_node_is_the_gateway_1e20aaff": "This node is the gateway", "this_radio_is_not_associated_with_other_nodes_6722a471": "This radio is not associated with other nodes", "to_internet_494eb85c": "To Internet", @@ -186,6 +182,8 @@ "to_the_previous_configuration_bf087867": "to the previous configuration", "traffic_bfe536d2": "Traffic", "try_reloading_the_app_4e4c3a66": "Try reloading the app", + "unreachable_howmany_e5c8f844": "Unreachable (%{howMany})", + "unreachable_nodes_e6785f10": "Unreachable Nodes", "upgrade_5de364f8": "Upgrade", "upgrade_now_f300d697": "Upgrade Now", "upgrade_to_lastest_firmware_version_9b159910": "Upgrade to lastest firmware version", diff --git a/i18n/translations/es.json b/i18n/translations/es.json index 373694bb..0d216532 100644 --- a/i18n/translations/es.json +++ b/i18n/translations/es.json @@ -194,24 +194,22 @@ "visit_864b4060": "Visitar", "go_to_node_view_26ba929d": "Ir a Vista de Nodo", "network_nodes_4368eb67": "Nodos de la Red", - "connected_howmany_228826ea": "Conectados (%{howMany})", - "connected_nodes_4b7e6f49": "Nodos Conectados", "delete_a6efa79d": "Eliminar", - "delete_nodes_f63ec0d5": "Eliminar Nodos", - "disconnected_howmany_10fc7bd5": "Desconectados (%{howMany})", - "disconnected_nodes_88f80d1e": "Nodos Desconectados", + "delete_nodes_f63ec0d5": "Baja de Nodos", "go_to_community_view_d12b8d67": "Ir a Vista de Comunidad", - "go_to_delete_nodes_1203128b": "Ir a Eliminar Nodos", "count_selected_nodes_19bbd632": { "one": "nodo seleccionado", "other": "nodos seleccionados" }, "successfully_deleted_23ce0a20": "Eliminado/s correctamente", - "these_are_the_nodes_with_which_you_do_not_have_con_ef5cc209": "Son los nodos con los que no tienes conectividad, es posible que no estén encendidos o que algún enlace para llegar a ellos esté caído.", - "these_are_the_nodes_with_which_you_have_connectivi_ef11819b": "Son los nodos con los que tienes conectividad, es decir que hay un camino funcionando entre tu nodo y cada uno de ellos.", - "if_some_of_these_nodes_no_longer_belong_to_the_net_a75d316f": "Si alguno de estos nodos no pertence más a la red puedes eliminarlo desde Eliminar Nodos.", - "remove_nodes_2dd26e14": "Baja de Nodos", - "select_the_nodes_which_no_longer_belong_to_the_net_92f853ef": "Selecciona los nodos que ya no pertenecen a la red y elimínalos de la lista de nodos inalcanzables", - "there_are_no_left_unreachable_nodes_c0bec63d": "No hay nodos inalcanzables" + "select_the_nodes_which_no_longer_belong_to_the_net_92f853ef": "Selecciona los nodos que ya no pertenecen a la red y elimínalos de la lista de nodos no alcanzables", + "there_are_no_left_unreachable_nodes_c0bec63d": "No hay nodos inalcanzables", + "reachable_howmany_6f891e31": "Alcanzables (%{howMany})", + "reachable_nodes_748c93f0": "Nodos Alcanzables", + "these_are_the_nodes_that_can_be_reached_from_your__4c524abe": "Son los nodos con los que no tienes conectividad, es posible que no estén encendidos o que algún enlace para llegar a ellos esté caído.", + "these_are_the_nodes_that_can_t_be_reached_from_you_dbbf9032": "Son los nodos con los que tienes conectividad, es decir que hay un camino funcionando entre tu nodo y cada uno de ellos.", + "this_information_is_synced_periodically_and_can_be_8b74cb8c": "Esta información se sincroniza periódicamente, puede estar desactualizada algunos minutos.", + "unreachable_howmany_e5c8f844": "No Alcanzables (%{howMany})", + "unreachable_nodes_e6785f10": "Nodos No Alcanzables" } diff --git a/plugins/lime-plugin-delete-nodes/src/deleteNodesMenu.js b/plugins/lime-plugin-delete-nodes/src/deleteNodesMenu.js index e3279673..091be2c8 100644 --- a/plugins/lime-plugin-delete-nodes/src/deleteNodesMenu.js +++ b/plugins/lime-plugin-delete-nodes/src/deleteNodesMenu.js @@ -2,7 +2,7 @@ import { h } from 'preact'; import I18n from 'i18n-js'; const Menu = () => ( - {I18n.t('Remove Nodes')} + {I18n.t('Delete Nodes')} ); export default Menu; diff --git a/plugins/lime-plugin-reachable-nodes/index.js b/plugins/lime-plugin-reachable-nodes/index.js index 6159bd1d..f378f77b 100644 --- a/plugins/lime-plugin-reachable-nodes/index.js +++ b/plugins/lime-plugin-reachable-nodes/index.js @@ -1,13 +1,9 @@ -import Page from './src/networkNodesPage'; -import { NetworkNodesMenu } from './src/networkNodesMenu'; -import DeleteNodesPage from './src/containers/deleteNodesPage'; +import Page from './src/reachableNodesPage'; +import { ReachableNodesMenu } from './src/reachableNodesMenu'; export default { name: 'NetworkNodes', page: Page, - menu: NetworkNodesMenu, + menu: ReachableNodesMenu, menuView: 'community', - additionalProtectedRoutes: [ - ['delete-nodes', DeleteNodesPage] - ] }; diff --git a/plugins/lime-plugin-reachable-nodes/networkNodes.spec.js b/plugins/lime-plugin-reachable-nodes/networkNodes.spec.js deleted file mode 100644 index cfc9fb3b..00000000 --- a/plugins/lime-plugin-reachable-nodes/networkNodes.spec.js +++ /dev/null @@ -1,134 +0,0 @@ -import { h } from 'preact'; -import { fireEvent, act, screen } from '@testing-library/preact'; -import '@testing-library/jest-dom'; -import waitForExpect from 'wait-for-expect'; - -import NetworkNodesPage from './src/networkNodesPage'; -import DeleteNodesPage from './src/containers/deleteNodesPage'; -import queryCache from 'utils/queryCache'; -import { getNodes, markNodesAsGone } from './src/networkNodesApi'; -import { render } from 'utils/test_utils'; - -jest.mock('./src/networkNodesApi'); - -beforeEach(() => { - getNodes.mockImplementation(async () => [ - { hostname: 'node1', status: 'connected' }, - { hostname: 'node2', status: 'connected' }, - { hostname: 'node3', status: 'connected' }, - { hostname: 'node4', status: 'disconnected' }, - { hostname: 'node5', status: 'disconnected' }, - { hostname: 'node6', status: 'disconnected' }, - { hostname: 'node7', status: 'disconnected' }, - { hostname: 'node8', status: 'gone' }, - { hostname: 'node9', status: 'gone' }, - ]); - markNodesAsGone.mockImplementation(async () => []); -}); - -afterEach(() => { - act(() => queryCache.clear()); -}); - -describe('network nodes screen', () => { - it('shows one tab for connected nodes and one for discconected nodes with length', async () => { - render(); - expect(await screen.findByRole('tab', { name: /^connected \(3\)/i })).toBeVisible(); - expect(await screen.findByRole('tab', { name: /^disconnected \(4\)/i })).toBeVisible(); - }) - - it('shows one row with the hostname for each connect node', async () => { - render(); - expect(await screen.findByText('node1')).toBeVisible(); - expect(await screen.findByText('node2')).toBeVisible(); - expect(await screen.findByText('node3')).toBeVisible(); - }) - - it('shows one row with the hostname for each disconnect node', async () => { - render(); - const tabDisconnected = await screen.findByRole('tab', { name: /^disconnected \(4\)/i }); - fireEvent.click(tabDisconnected); - expect(await screen.findByText('node4')).toBeVisible(); - expect(await screen.findByText('node5')).toBeVisible(); - expect(await screen.findByText('node6')).toBeVisible(); - expect(await screen.findByText('node7')).toBeVisible(); - }) - - it('shows a link to go to delete nodes page', async () => { - render(); - const tabDisconnected = await screen.findByRole('tab', { name: /^disconnected \(4\)/i }); - fireEvent.click(tabDisconnected); - expect(await screen.findByRole('link', { name: /go to delete nodes/i })).toBeVisible(); - }) - - it('does not show a link to go to delete nodes page if there are no disconnected nodes', async () => { - getNodes.mockImplementation(async () => [ - { hostname: 'node1', status: 'connected' }, - { hostname: 'node2', status: 'connected' }, - { hostname: 'node3', status: 'connected' }, - ]); - render(); - const tabDisconnected = await screen.findByRole('tab', { name: /^disconnected \(0\)/i }); - fireEvent.click(tabDisconnected); - expect(screen.queryByRole('link', { name: /go to delete nodes/i })).toBeNull(); - }) - - it('shows help message when clicking on help button', async () => { - render(); - const helpButton = await screen.findByLabelText('help'); - fireEvent.click(helpButton); - expect(await screen.findByText("Connected Nodes")).toBeVisible(); - expect(await screen.findByText("These are the nodes with which you have connectivity, " - + "i.e. there is a working path from your node to each of them.")).toBeVisible(); - expect(await screen.findByText("Disconnected Nodes")).toBeVisible(); - expect(await screen.findByText("These are the nodes with which you do not have connectivity, " - + "it is possible that they are not turned on or a link to reach them is down.")).toBeVisible(); - }) -}); - - -describe('delete nodes page', () => { - it('shows the list of disconnected nodes only', async () => { - render(); - expect(await screen.findByText('node4')).toBeVisible(); - expect(await screen.findByText('node5')).toBeVisible(); - expect(await screen.findByText('node6')).toBeVisible(); - expect(await screen.findByText('node7')).toBeVisible(); - expect(screen.queryByText('node1')).toBeNull(); - expect(screen.queryByText('node2')).toBeNull(); - expect(screen.queryByText('node3')).toBeNull(); - expect(screen.queryByText('node8')).toBeNull(); - expect(screen.queryByText('node9')).toBeNull(); - }) - - it('calls the markNodesAsGone api when deleting', async () => { - markNodesAsGone.mockImplementation(async () => ['node6']); - render(); - fireEvent.click(await screen.findByText('node6')); - fireEvent.click(await screen.findByRole('button', { name: /delete/i })); - await waitForExpect(() => { - expect(markNodesAsGone).toBeCalledWith(['node6']); - }) - }) - - it('hide nodes from the list after deletion', async () => { - markNodesAsGone.mockImplementation(async () => ['node6', 'node7']); - render(); - fireEvent.click(await screen.findByText('node6')); - fireEvent.click(await screen.findByText('node7')); - fireEvent.click(await screen.findByRole('button', { name: /delete/i })); - expect(await screen.findByText('node4')).toBeVisible(); - expect(await screen.queryByText('node5')).toBeVisible(); - expect(await screen.queryByText('node6')).toBeNull(); - expect(await screen.queryByText('node7')).toBeNull(); - }) - - it('show success message after deletion', async () => { - markNodesAsGone.mockImplementation(async () => ['node6', 'node7']); - render(); - fireEvent.click(await screen.findByText('node6')); - fireEvent.click(await screen.findByText('node7')); - fireEvent.click(await screen.findByRole('button', { name: /delete/i })); - expect(await screen.findByText(/successfully deleted/i)).toBeVisible(); - }) -}) \ No newline at end of file diff --git a/plugins/lime-plugin-reachable-nodes/networkNodes.stories.js b/plugins/lime-plugin-reachable-nodes/networkNodes.stories.js deleted file mode 100644 index 86ada871..00000000 --- a/plugins/lime-plugin-reachable-nodes/networkNodes.stories.js +++ /dev/null @@ -1,34 +0,0 @@ -import { NetworkNodesPage_ } from "./src/networkNodesPage"; -import { DeleteNodesPage_ } from "./src/containers/deleteNodesPage"; - -export default { - title: 'Containers/Network nodes', -}; - -const nodes = [ - { hostname: "ql-czuk", status: "connected" }, - { hostname: "ql-irene", status: "connected" }, - { hostname: "ql-ipem", status: "connected" }, - { hostname: "ql-czuck-bbone", status: "connected" }, - { hostname: "ql-graciela", status: "connected" }, - { hostname: "ql-marisa", status: "connected" }, - { hostname: "ql-anaymarcos", status: "connected" }, - { hostname: "ql-quinteros", status: "connected" }, - { hostname: "ql-guada", status: "connected" }, - { hostname: "ql-refu-bbone", status: "disconnected" }, - { hostname: "si-soniam", status: "disconnected" }, - { hostname: "si-giordano", status: "disconnected" }, - { hostname: "si-mario", status: "disconnected" }, - { hostname: "si-manu", status: "disconnected" }, -]; - -export const networkNodesPage = () => ( - -) - -export const deleteNodesPage = (args) => ( - -) -deleteNodesPage.argTypes = { - onDelete: { action: 'deleted' } -} \ No newline at end of file diff --git a/plugins/lime-plugin-reachable-nodes/reachableNodes.spec.js b/plugins/lime-plugin-reachable-nodes/reachableNodes.spec.js new file mode 100644 index 00000000..be0d3371 --- /dev/null +++ b/plugins/lime-plugin-reachable-nodes/reachableNodes.spec.js @@ -0,0 +1,68 @@ +import { h } from 'preact'; +import { fireEvent, act, screen } from '@testing-library/preact'; +import '@testing-library/jest-dom'; + +import ReachableNodesPage from './src/reachableNodesPage'; +import queryCache from 'utils/queryCache'; +import { getNodes, markNodesAsGone } from 'plugins/lime-plugin-network-nodes/src/networkNodesApi'; +import { render } from 'utils/test_utils'; + +jest.mock('plugins/lime-plugin-network-nodes/src/networkNodesApi'); + +beforeEach(() => { + getNodes.mockImplementation(async () => [ + { hostname: 'node1', status: 'recently_reachable' }, + { hostname: 'node2', status: 'recently_reachable' }, + { hostname: 'node3', status: 'recently_reachable' }, + { hostname: 'node4', status: 'unreachable' }, + { hostname: 'node5', status: 'unreachable' }, + { hostname: 'node6', status: 'unreachable' }, + { hostname: 'node7', status: 'unreachable' }, + { hostname: 'node8', status: 'gone' }, + { hostname: 'node9', status: 'gone' }, + ]); + markNodesAsGone.mockImplementation(async () => []); +}); + +afterEach(() => { + act(() => queryCache.clear()); +}); + +describe('network nodes screen', () => { + it('shows one tab for reachable nodes and one for unreachable nodes with length', async () => { + render(); + expect(await screen.findByRole('tab', { name: /^reachable \(3\)/i })).toBeVisible(); + expect(await screen.findByRole('tab', { name: /^unreachable \(4\)/i })).toBeVisible(); + }) + + it('shows one row with the hostname for each connect node', async () => { + render(); + expect(await screen.findByText('node1')).toBeVisible(); + expect(await screen.findByText('node2')).toBeVisible(); + expect(await screen.findByText('node3')).toBeVisible(); + }) + + it('shows one row with the hostname for each disconnect node', async () => { + render(); + const tabDisconnected = await screen.findByRole('tab', { name: /^unreachable \(4\)/i }); + fireEvent.click(tabDisconnected); + expect(await screen.findByText('node4')).toBeVisible(); + expect(await screen.findByText('node5')).toBeVisible(); + expect(await screen.findByText('node6')).toBeVisible(); + expect(await screen.findByText('node7')).toBeVisible(); + }) + + it('shows help message when clicking on help button', async () => { + render(); + const helpButton = await screen.findByLabelText('help'); + fireEvent.click(helpButton); + expect(await screen.findByText("Reachable Nodes")).toBeVisible(); + expect(await screen.findByText("These are the nodes that can be reached from your node, " + + "i.e. there is a working path from your node to each of them." + + "This information is synced periodically " + + "and can be outdated by some minutes")).toBeVisible(); + expect(await screen.findByText("Unreachable Nodes")).toBeVisible(); + expect(await screen.findByText("These are the nodes that can't be reached from your node, " + + "it is possible that they are not turned on or a link to reach them is down.")).toBeVisible(); + }) +}); diff --git a/plugins/lime-plugin-reachable-nodes/reachableNodes.stories.js b/plugins/lime-plugin-reachable-nodes/reachableNodes.stories.js new file mode 100644 index 00000000..195b3226 --- /dev/null +++ b/plugins/lime-plugin-reachable-nodes/reachableNodes.stories.js @@ -0,0 +1,29 @@ +import { ReachableNodesPage_ } from "./src/reachableNodesPage"; + +export default { + title: 'Containers/ReachableNodes', +}; + +const nodes = [ + { hostname: "ql-czuk", status: "recently_reachable", + ipv4:'10.5.0.3', ipv6: 'fd0d:fe46:8ce8::8bbf:7500', + board: 'LibreRouter v1', fw_version: 'LibreRouterOS 1.4' + }, + { hostname: "ql-irene", status: "recently_reachable" }, + { hostname: "ql-ipem", status: "recently_reachable" }, + { hostname: "ql-czuck-bbone", status: "recently_reachable" }, + { hostname: "ql-graciela", status: "recently_reachable" }, + { hostname: "ql-marisa", status: "recently_reachable" }, + { hostname: "ql-anaymarcos", status: "recently_reachable" }, + { hostname: "ql-quinteros", status: "recently_reachable" }, + { hostname: "ql-guada", status: "recently_reachable" }, + { hostname: "ql-refu-bbone", status: "unreachable" }, + { hostname: "si-soniam", status: "unreachable" }, + { hostname: "si-giordano", status: "unreachable" }, + { hostname: "si-mario", status: "unreachable" }, + { hostname: "si-manu", status: "unreachable" }, +]; + +export const reachableNodesPage = () => ( + +); diff --git a/plugins/lime-plugin-reachable-nodes/src/networkNodesApi.js b/plugins/lime-plugin-reachable-nodes/src/networkNodesApi.js deleted file mode 100644 index 060496ce..00000000 --- a/plugins/lime-plugin-reachable-nodes/src/networkNodesApi.js +++ /dev/null @@ -1,9 +0,0 @@ -import api from 'utils/uhttpd.service'; - -export const getNodes = () => - api.call('network-nodes', 'get_nodes', {}).toPromise() - .then(res => res.nodes); - -export const markNodesAsGone = (hostnames) => - api.call('network-nodes', 'mark_nodes_as_gone', { hostnames: hostnames }).toPromise() - .then(() => hostnames); \ No newline at end of file diff --git a/plugins/lime-plugin-reachable-nodes/src/networkNodesApi.spec.js b/plugins/lime-plugin-reachable-nodes/src/networkNodesApi.spec.js deleted file mode 100644 index bc1e2fef..00000000 --- a/plugins/lime-plugin-reachable-nodes/src/networkNodesApi.spec.js +++ /dev/null @@ -1,49 +0,0 @@ -import { of, throwError } from 'rxjs'; -import api from 'utils/uhttpd.service'; -import waitForExpect from 'wait-for-expect'; - -jest.mock('utils/uhttpd.service') - -import { getNodes, markNodesAsGone } from './networkNodesApi'; - - -beforeEach(() => { - api.call.mockClear(); -}) - -describe('getNodes', () => { - it('calls the expected endpoint', async () => { - api.call.mockImplementation(() => of({ status: 'ok' })) - await getNodes(); - expect(api.call).toBeCalledWith('network-nodes', 'get_nodes', {}); - }) - - it('resolves to network nodes', async () => { - const networkNodes = [ - { hostname: 'node1', status: 'connected' }, - { hostname: 'node2', status: 'disconnected' }, - { hostname: 'node3', status: 'gone' }, - ]; - api.call.mockImplementation(() => of( - { - status: 'ok', - nodes: networkNodes, - })); - let nodes = await getNodes(); - expect(nodes).toEqual(networkNodes); - }); -}); - -describe('markNodesAsGone', () => { - it('calls the expected endpoint', async () => { - api.call.mockImplementation(() => of({ status: 'ok' })) - await markNodesAsGone(['node1']); - expect(api.call).toBeCalledWith('network-nodes', 'mark_nodes_as_gone', { hostnames: ['node1'] }) - }) - - it('resolve to hostnames passed as parameters on success', async() => { - api.call.mockImplementation(() => of({status: 'ok'})) - const result = await markNodesAsGone(['node1', 'node2']) - expect(result).toEqual(['node1', 'node2']) - }) -}); diff --git a/plugins/lime-plugin-reachable-nodes/src/networkNodesMenu.js b/plugins/lime-plugin-reachable-nodes/src/networkNodesMenu.js deleted file mode 100644 index ba59c9d4..00000000 --- a/plugins/lime-plugin-reachable-nodes/src/networkNodesMenu.js +++ /dev/null @@ -1,7 +0,0 @@ -import { h } from 'preact'; - -import I18n from 'i18n-js'; - -export const NetworkNodesMenu = () => ( - {I18n.t('Network Nodes')} -); \ No newline at end of file diff --git a/plugins/lime-plugin-reachable-nodes/src/networkNodesPage.js b/plugins/lime-plugin-reachable-nodes/src/networkNodesPage.js deleted file mode 100644 index 55cd73d1..00000000 --- a/plugins/lime-plugin-reachable-nodes/src/networkNodesPage.js +++ /dev/null @@ -1,85 +0,0 @@ -import { h } from "preact"; -import { useState } from "preact/hooks"; -import style from "./style.less"; -import Tabs from "components/tabs"; -import Loading from "components/loading"; -import { List, ListItem } from "components/list"; -import { useNetworkNodes } from "./networkNodesQueries"; -import Help from "components/help"; -import I18n from 'i18n-js'; - -const DeleteNodesLegend = () => ( -
-
{I18n.t("If some of these nodes no longer belong " + - "to the network you can delete them from Delete Nodes.")}
- -
-) - -const PageHelp = () => ( -
-

-

{I18n.t("Connected Nodes")}
- {I18n.t("These are the nodes with which you have connectivity, " + - "i.e. there is a working path from your node to each of them.")} -

-

-

{I18n.t("Disconnected Nodes")}
- {I18n.t("These are the nodes with which you do not have connectivity, " + - "it is possible that they are not turned on or a link to reach them is down.")} -

-
-); - -const PageTabs = ({ nodes, ...props }) => { - const nConnected = nodes.filter(n => n.status === "connected").length; - const nDisconnected = nodes.filter(n => n.status === "disconnected").length; - const tabs = [ - { key: 'connected', repr: I18n.t('Connected (%{howMany})', { howMany: nConnected }) }, - { key: 'disconnected', repr: I18n.t('Disconnected (%{howMany})', { howMany: nDisconnected }) }, - ]; - return -} - -export const NetworkNodesPage_ = ({ nodes }) => { - const [selectedGroup, setselectedGroup] = useState('connected'); - return ( -
-
- -
- -
-
- - {nodes - .filter(n => n.status === selectedGroup) - .sort((a, b) => a.hostname > b.hostname) - .map( - node => - -
- {node.hostname} -
-
- )} - {selectedGroup === "disconnected" && - nodes.filter(n => n.status == "disconnected").length && - - } -
-
- ) -} - -const NetworkNodesPage = () => { - const { data: nodes, isLoading } = useNetworkNodes(); - - if (isLoading) { - return
- } - - return -} - -export default NetworkNodesPage \ No newline at end of file diff --git a/plugins/lime-plugin-reachable-nodes/src/networkNodesQueries.js b/plugins/lime-plugin-reachable-nodes/src/networkNodesQueries.js deleted file mode 100644 index fb9b8b76..00000000 --- a/plugins/lime-plugin-reachable-nodes/src/networkNodesQueries.js +++ /dev/null @@ -1,16 +0,0 @@ -import { useQuery, useMutation } from 'react-query'; -import { getNodes, markNodesAsGone } from './networkNodesApi'; -import queryCache from 'utils/queryCache'; - -export const useNetworkNodes = () => useQuery(['network-nodes', 'get_nodes'], getNodes) - -export const useMarkNodesAsGone = () => useMutation(markNodesAsGone, { - onSuccess: hostnames => queryCache.setQueryData(['network-nodes', 'get_nodes'], - old => { - const result = old.map( - node => hostnames.indexOf(node.hostname) != -1 ? { ...node, status: "gone" } : node - ) - return result; - } - ) -}) \ No newline at end of file diff --git a/plugins/lime-plugin-reachable-nodes/src/reachableNodesMenu.js b/plugins/lime-plugin-reachable-nodes/src/reachableNodesMenu.js new file mode 100644 index 00000000..55b9138b --- /dev/null +++ b/plugins/lime-plugin-reachable-nodes/src/reachableNodesMenu.js @@ -0,0 +1,7 @@ +import { h } from 'preact'; + +import I18n from 'i18n-js'; + +export const ReachableNodesMenu = () => ( + {I18n.t('Reachable Nodes')} +); \ No newline at end of file diff --git a/plugins/lime-plugin-reachable-nodes/src/reachableNodesPage.js b/plugins/lime-plugin-reachable-nodes/src/reachableNodesPage.js new file mode 100644 index 00000000..92172c6b --- /dev/null +++ b/plugins/lime-plugin-reachable-nodes/src/reachableNodesPage.js @@ -0,0 +1,83 @@ +import { h } from "preact"; +import { useState } from "preact/hooks"; +import Tabs from "components/tabs"; +import Loading from "components/loading"; +import { List } from "components/list"; +import { ExpandableNode } from "plugins/lime-plugin-network-nodes/src/components/expandableNode"; +import { useNetworkNodes } from "plugins/lime-plugin-network-nodes/src/networkNodesQueries"; +import Help from "components/help"; +import I18n from 'i18n-js'; + +const PageHelp = () => ( +
+

+

{I18n.t("Reachable Nodes")}
+ {I18n.t("These are the nodes that can be reached from your node, " + + "i.e. there is a working path from your node to each of them.")} +
+ {I18n.t("This information is synced periodically " + + "and can be outdated by some minutes")} +

+

+

{I18n.t("Unreachable Nodes")}
+ {I18n.t("These are the nodes that can't be reached from your node, " + + "it is possible that they are not turned on or a link to reach them is down.")} +

+
+); + +const PageTabs = ({ nodes, ...props }) => { + const nReachable = nodes.filter(n => n.status === "recently_reachable").length; + const nUnreachable = nodes.filter(n => n.status === "unreachable").length; + const tabs = [ + { key: 'recently_reachable', repr: I18n.t('Reachable (%{howMany})', { howMany: nReachable }) }, + { key: 'unreachable', repr: I18n.t('Unreachable (%{howMany})', { howMany: nUnreachable }) }, + ]; + return +} + +export const ReachableNodesPage_ = ({ nodes }) => { + const [ selectedGroup, setselectedGroup ] = useState('recently_reachable'); + const [ unfoldedNode, setunfoldedNode ] = useState(null); + + function changeUnfolded(hostname) { + if (unfoldedNode == hostname) { + setunfoldedNode(null); + return; + } + setunfoldedNode(hostname); + } + return ( +
+
+ +
+ +
+
+ + {nodes + .filter(n => n.status === selectedGroup) + .sort((a, b) => a.hostname > b.hostname) + .map( + node => + changeUnfolded(node.hostname)}/> + )} + +
+ ) +} + +const ReachableNodesPage = () => { + const { data: nodes, isLoading } = useNetworkNodes(); + + if (isLoading) { + return
+ } + + return +} + +export default ReachableNodesPage diff --git a/plugins/lime-plugin-reachable-nodes/src/style.less b/plugins/lime-plugin-reachable-nodes/src/style.less deleted file mode 100644 index b3ea1445..00000000 --- a/plugins/lime-plugin-reachable-nodes/src/style.less +++ /dev/null @@ -1,15 +0,0 @@ -.deleteNodesLegend { - text-align: center; - flex: auto; - padding: 0.5em; -} - -.nodeItem { - font-size: 2rem; - display: flex; - flex: auto -} - -.helpWrapper { - padding: 1em; -} \ No newline at end of file diff --git a/src/components/help/style.less b/src/components/help/style.less index fa98012f..1cb7fd19 100644 --- a/src/components/help/style.less +++ b/src/components/help/style.less @@ -13,7 +13,7 @@ background: #fff; border: 0.1em solid #F39100; border-radius: 1em; - padding: 2em; + padding: 1.5rem; cursor:auto; } diff --git a/src/config.js b/src/config.js index 972f685c..c483915c 100644 --- a/src/config.js +++ b/src/config.js @@ -11,6 +11,7 @@ import Firmware from '../plugins/lime-plugin-firmware'; import RemoteSupport from '../plugins/lime-plugin-remotesupport'; import NetworkNodes from '../plugins/lime-plugin-network-nodes'; import DeleteNodes from '../plugins/lime-plugin-delete-nodes'; +import ReachableNodes from '../plugins/lime-plugin-reachable-nodes'; // REGISTER PLUGINS export const plugins = [