From b7df0e8000d13b7d034f2aeb9803531ed1e90df0 Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Wed, 19 Nov 2025 11:37:30 +0100 Subject: [PATCH 1/4] console: Fix gateway status panel --- .../containers/gateway-status-panel/index.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/pkg/webui/console/containers/gateway-status-panel/index.js b/pkg/webui/console/containers/gateway-status-panel/index.js index 9350c3277e..35b68f79d2 100644 --- a/pkg/webui/console/containers/gateway-status-panel/index.js +++ b/pkg/webui/console/containers/gateway-status-panel/index.js @@ -12,9 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React, { useMemo } from 'react' -import { useSelector } from 'react-redux' +import React, { useMemo, useEffect } from 'react' +import { useSelector, useDispatch } from 'react-redux' import { defineMessages } from 'react-intl' +import { useParams } from 'react-router-dom' import Panel from '@ttn-lw/components/panel' import Icon, { IconGateway, IconInfoCircle, IconBolt, IconRouterOff } from '@ttn-lw/components/icon' @@ -30,6 +31,8 @@ import PropTypes from '@ttn-lw/lib/prop-types' import sharedMessages from '@ttn-lw/lib/shared-messages' import { getBackendErrorName, isBackend } from '@ttn-lw/lib/errors/utils' +import { startGatewayStatistics, stopGatewayStatistics } from '@console/store/actions/gateways' + import { selectGatewayStatistics, selectGatewayStatisticsError, @@ -104,9 +107,20 @@ EmptyState.propTypes = { } const GatewayStatusPanel = () => { + const { gtwId } = useParams() + const dispatch = useDispatch() const gatewayStats = useSelector(selectGatewayStatistics) const error = useSelector(selectGatewayStatisticsError) const fetching = useSelector(selectGatewayStatisticsIsFetching) + + // Start statistics fetching when component mounts + useEffect(() => { + dispatch(startGatewayStatistics(gtwId)) + return () => { + dispatch(stopGatewayStatistics()) + } + }, [dispatch, gtwId]) + const isDisconnected = Boolean(gatewayStats?.disconnected_at) const isConnected = Boolean(gatewayStats?.connected_at) && !isDisconnected const isFetching = !Boolean(gatewayStats) && fetching From 6736c24fcb46599ec6b8d3769649241a29e9008d Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Tue, 25 Nov 2025 10:45:49 +0100 Subject: [PATCH 2/4] console: Fix gateway status --- .../containers/gateway-status-panel/index.js | 20 +++---------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/pkg/webui/console/containers/gateway-status-panel/index.js b/pkg/webui/console/containers/gateway-status-panel/index.js index 35b68f79d2..f2c0c72813 100644 --- a/pkg/webui/console/containers/gateway-status-panel/index.js +++ b/pkg/webui/console/containers/gateway-status-panel/index.js @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React, { useMemo, useEffect } from 'react' -import { useSelector, useDispatch } from 'react-redux' +import React, { useMemo } from 'react' +import { useSelector } from 'react-redux' import { defineMessages } from 'react-intl' -import { useParams } from 'react-router-dom' import Panel from '@ttn-lw/components/panel' import Icon, { IconGateway, IconInfoCircle, IconBolt, IconRouterOff } from '@ttn-lw/components/icon' @@ -31,8 +30,6 @@ import PropTypes from '@ttn-lw/lib/prop-types' import sharedMessages from '@ttn-lw/lib/shared-messages' import { getBackendErrorName, isBackend } from '@ttn-lw/lib/errors/utils' -import { startGatewayStatistics, stopGatewayStatistics } from '@console/store/actions/gateways' - import { selectGatewayStatistics, selectGatewayStatisticsError, @@ -107,20 +104,9 @@ EmptyState.propTypes = { } const GatewayStatusPanel = () => { - const { gtwId } = useParams() - const dispatch = useDispatch() const gatewayStats = useSelector(selectGatewayStatistics) const error = useSelector(selectGatewayStatisticsError) const fetching = useSelector(selectGatewayStatisticsIsFetching) - - // Start statistics fetching when component mounts - useEffect(() => { - dispatch(startGatewayStatistics(gtwId)) - return () => { - dispatch(stopGatewayStatistics()) - } - }, [dispatch, gtwId]) - const isDisconnected = Boolean(gatewayStats?.disconnected_at) const isConnected = Boolean(gatewayStats?.connected_at) && !isDisconnected const isFetching = !Boolean(gatewayStats) && fetching @@ -131,7 +117,7 @@ const GatewayStatusPanel = () => { ) const hasError = Boolean(error) && Boolean(error.message) - const isUnavailable = hasError && error.message === 'Unavailable' + const isUnavailable = hasError && error.message === 'Unavailable' && !fetching && !gatewayStats const maxRoundTripTime = useMemo( () => From 0d23e104b69b1d4cca7f946a8b51c168c5f23c6e Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Tue, 25 Nov 2025 10:49:53 +0100 Subject: [PATCH 3/4] console: Fix green dot --- pkg/webui/console/containers/gateway-status-panel/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/webui/console/containers/gateway-status-panel/index.js b/pkg/webui/console/containers/gateway-status-panel/index.js index f2c0c72813..1a94d85b18 100644 --- a/pkg/webui/console/containers/gateway-status-panel/index.js +++ b/pkg/webui/console/containers/gateway-status-panel/index.js @@ -118,6 +118,7 @@ const GatewayStatusPanel = () => { const hasError = Boolean(error) && Boolean(error.message) const isUnavailable = hasError && error.message === 'Unavailable' && !fetching && !gatewayStats + const isLoadingWithError = hasError && error.message === 'Unavailable' && !gatewayStats const maxRoundTripTime = useMemo( () => @@ -155,7 +156,7 @@ const GatewayStatusPanel = () => { status={ isDisconnected ? 'bad' - : isFetching || noConnectionYet + : isFetching || noConnectionYet || isLoadingWithError ? 'mediocre' : isConnected ? 'green' From 01c27bb305469360b139b29a85254f59bd3f0c47 Mon Sep 17 00:00:00 2001 From: Darya Plotnytska Date: Tue, 25 Nov 2025 15:22:18 +0100 Subject: [PATCH 4/4] console: Gateway status panel --- .../containers/gateway-connection/index.js | 15 ++----------- .../containers/gateway-status-panel/index.js | 21 ++++++------------- pkg/webui/console/views/gateway/index.js | 12 ++++++++++- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/pkg/webui/console/containers/gateway-connection/index.js b/pkg/webui/console/containers/gateway-connection/index.js index 7b5c52b82c..0e4389af64 100644 --- a/pkg/webui/console/containers/gateway-connection/index.js +++ b/pkg/webui/console/containers/gateway-connection/index.js @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import React, { useEffect, useMemo } from 'react' +import React, { useMemo } from 'react' import classnames from 'classnames' import { FormattedNumber, defineMessages } from 'react-intl' -import { useDispatch, useSelector } from 'react-redux' +import { useSelector } from 'react-redux' import Icon, { IconArrowsSort, IconBroadcast } from '@ttn-lw/components/icon' import Status from '@ttn-lw/components/status' @@ -35,8 +35,6 @@ import { isNotFoundError, isTranslated } from '@ttn-lw/lib/errors/utils' import { selectGsConfig } from '@ttn-lw/lib/selectors/env' import getHostFromUrl from '@ttn-lw/lib/host-from-url' -import { startGatewayStatistics, stopGatewayStatistics } from '@console/store/actions/gateways' - import { selectGatewayById, selectGatewayStatistics, @@ -73,17 +71,8 @@ const GatewayConnection = props => { const lastSeen = useSelector(selectGatewayLastSeen) const isOtherCluster = consoleGsAddress !== gatewayServerAddress - const dispatch = useDispatch() - useConnectionReactor(gtwId) - useEffect(() => { - dispatch(startGatewayStatistics(gtwId)) - return () => { - dispatch(stopGatewayStatistics()) - } - }, [dispatch, gtwId]) - const status = useMemo(() => { const statsNotFound = Boolean(error) && isNotFoundError(error) const isDisconnected = Boolean(statistics) && Boolean(statistics.disconnected_at) diff --git a/pkg/webui/console/containers/gateway-status-panel/index.js b/pkg/webui/console/containers/gateway-status-panel/index.js index 1a94d85b18..c6737010d4 100644 --- a/pkg/webui/console/containers/gateway-status-panel/index.js +++ b/pkg/webui/console/containers/gateway-status-panel/index.js @@ -108,17 +108,16 @@ const GatewayStatusPanel = () => { const error = useSelector(selectGatewayStatisticsError) const fetching = useSelector(selectGatewayStatisticsIsFetching) const isDisconnected = Boolean(gatewayStats?.disconnected_at) - const isConnected = Boolean(gatewayStats?.connected_at) && !isDisconnected const isFetching = !Boolean(gatewayStats) && fetching - const hasStatistics = Boolean(gatewayStats) const noConnectionYet = useMemo( () => isBackend(error) && getBackendErrorName(error).includes('not_connected'), [error], ) + const hasStatistics = Boolean(gatewayStats) + const isConnected = Boolean(gatewayStats?.connected_at) && !isDisconnected && !noConnectionYet const hasError = Boolean(error) && Boolean(error.message) - const isUnavailable = hasError && error.message === 'Unavailable' && !fetching && !gatewayStats - const isLoadingWithError = hasError && error.message === 'Unavailable' && !gatewayStats + const isUnavailable = hasError && error.message === 'Unavailable' const maxRoundTripTime = useMemo( () => @@ -156,7 +155,7 @@ const GatewayStatusPanel = () => { status={ isDisconnected ? 'bad' - : isFetching || noConnectionYet || isLoadingWithError + : isFetching || noConnectionYet ? 'mediocre' : isConnected ? 'green' @@ -191,15 +190,7 @@ const GatewayStatusPanel = () => { )} - {isUnavailable && ( -
-
- - -
-
- )} - {hasStatistics && !isFetching && !noConnectionYet && !isUnavailable && ( + {hasStatistics && !isFetching && !isUnavailable && ( <>
@@ -249,7 +240,7 @@ const GatewayStatusPanel = () => {
)} - {!hasStatistics && !isFetching && !noConnectionYet && !isUnavailable && ( + {(isUnavailable || (!hasStatistics && !isFetching && !noConnectionYet)) && ( )} diff --git a/pkg/webui/console/views/gateway/index.js b/pkg/webui/console/views/gateway/index.js index 2b51e29e78..5997b48837 100644 --- a/pkg/webui/console/views/gateway/index.js +++ b/pkg/webui/console/views/gateway/index.js @@ -45,6 +45,8 @@ import { stopGatewayEventsStream, getGatewaysRightsList, getGatewayClaimInfoByEui, + startGatewayStatistics, + stopGatewayStatistics, } from '@console/store/actions/gateways' import { getGsFrequencyPlans } from '@console/store/actions/configuration' import { trackRecencyFrequencyItem } from '@console/store/actions/recency-frequency-items' @@ -86,12 +88,20 @@ const Gateway = () => { await dispatch(getGsFrequencyPlans()) const { ids } = await dispatch(attachPromise(getGateway(gtwId, selector))) + await dispatch(startGatewayStatistics(gtwId)) await dispatch(attachPromise(getGatewayClaimInfoByEui(ids.eui, true))) }, [gtwId], ) - useEffect(() => () => dispatch(stopGatewayEventsStream(gtwId)), [gtwId, dispatch]) + + useEffect( + () => () => { + dispatch(stopGatewayStatistics()) + dispatch(stopGatewayEventsStream(gtwId)) + }, + [dispatch, gtwId], + ) // Track gateway access. useEffect(() => {