mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 13:49:00 +08:00
feat: improve empty hosts, incorrect metrics and no filter views (#6530)
* feat: improve empty hosts, incorrect metrics and no filter views * feat: add infra monitoring - host lists - usage events (#6536) * feat: add usasge events * feat: add reqrest early access events --------- Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
This commit is contained in:
parent
486632b64e
commit
2bfd31841e
@ -2,6 +2,7 @@
|
|||||||
"containers_visualization_message": "The ability to visualise containers is in active development and should be available to you soon.",
|
"containers_visualization_message": "The ability to visualise containers is in active development and should be available to you soon.",
|
||||||
"processes_visualization_message": "The ability to visualise processes is in active development and should be available to you soon.",
|
"processes_visualization_message": "The ability to visualise processes is in active development and should be available to you soon.",
|
||||||
"working_message": "We're working to extend infrastructure monitoring to take care of a bunch of different cases. Thank you for your patience.",
|
"working_message": "We're working to extend infrastructure monitoring to take care of a bunch of different cases. Thank you for your patience.",
|
||||||
"waitlist_message": "Join the waitlist for early access or contact support.",
|
"waitlist_message": "Join the waitlist for early access.",
|
||||||
|
"waitlist_success_message": "We have received your request for early access. We will get back to you as soon as we launch the feature.",
|
||||||
"contact_support": "Contact Support"
|
"contact_support": "Contact Support"
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"containers_visualization_message": "The ability to visualise containers is in active development and should be available to you soon.",
|
"containers_visualization_message": "The ability to visualise containers is in active development and should be available to you soon.",
|
||||||
"processes_visualization_message": "The ability to visualise processes is in active development and should be available to you soon.",
|
"processes_visualization_message": "The ability to visualise processes is in active development and should be available to you soon.",
|
||||||
"working_message": "We're working to extend infrastructure monitoring to take care of a bunch of different cases. Thank you for your patience.",
|
"working_message": "We're working to extend infrastructure monitoring to take care of a bunch of different cases. Thank you for your patience.",
|
||||||
"waitlist_message": "Join the waitlist for early access or contact support.",
|
"waitlist_message": "Join the waitlist for early access.",
|
||||||
|
"waitlist_success_message": "We have received your request for early access. We will get back to you as soon as we launch the feature.",
|
||||||
"contact_support": "Contact Support"
|
"contact_support": "Contact Support"
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,8 @@ export interface HostListResponse {
|
|||||||
records: HostData[];
|
records: HostData[];
|
||||||
groups: null;
|
groups: null;
|
||||||
total: number;
|
total: number;
|
||||||
|
sentAnyHostMetricsData: boolean;
|
||||||
|
isSendingK8SAgentMetrics: boolean;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
.host-containers {
|
.host-containers {
|
||||||
max-width: 600px;
|
gap: 24px;
|
||||||
margin: 150px auto;
|
height: 60vh;
|
||||||
padding: 0 16px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.infra-container-card-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-status-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.infra-container-card {
|
.infra-container-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -17,6 +34,7 @@
|
|||||||
width: 400px;
|
width: 400px;
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.infra-container-working-msg {
|
.infra-container-working-msg {
|
||||||
|
@ -3,6 +3,8 @@ import './Containers.styles.scss';
|
|||||||
import { Space, Typography } from 'antd';
|
import { Space, Typography } from 'antd';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import WaitlistFragment from '../WaitlistFragment/WaitlistFragment';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
function Containers(): JSX.Element {
|
function Containers(): JSX.Element {
|
||||||
@ -10,24 +12,30 @@ function Containers(): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" className="host-containers" size={24}>
|
<Space direction="vertical" className="host-containers" size={24}>
|
||||||
<div className="infra-container-card">
|
<div className="infra-container-card-container">
|
||||||
<img
|
<div className="dev-status-container">
|
||||||
src="/Icons/infraContainers.svg"
|
<div className="infra-container-card">
|
||||||
alt="infra-container"
|
<img
|
||||||
width={32}
|
src="/Icons/infraContainers.svg"
|
||||||
height={32}
|
alt="infra-container"
|
||||||
/>
|
width={32}
|
||||||
|
height={32}
|
||||||
|
/>
|
||||||
|
|
||||||
<Text className="infra-container-card-text">
|
<Text className="infra-container-card-text">
|
||||||
{t('containers_visualization_message')}
|
{t('containers_visualization_message')}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="infra-container-working-msg">
|
<div className="infra-container-working-msg">
|
||||||
<Space>
|
<Space>
|
||||||
<img src="/Icons/broom.svg" alt="broom" width={16} height={16} />
|
<img src="/Icons/broom.svg" alt="broom" width={24} height={24} />
|
||||||
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
||||||
</Space>
|
</Space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<WaitlistFragment entityType="containers" />
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { RadioChangeEvent } from 'antd/lib';
|
import { RadioChangeEvent } from 'antd/lib';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import {
|
import {
|
||||||
initialQueryBuilderFormValuesMap,
|
initialQueryBuilderFormValuesMap,
|
||||||
@ -118,6 +119,13 @@ function HostMetricsDetails({
|
|||||||
initialFilters,
|
initialFilters,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
logEvent('Infra Monitoring: Hosts list details page visited', {
|
||||||
|
host: host?.hostName,
|
||||||
|
});
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLogFilters(initialFilters);
|
setLogFilters(initialFilters);
|
||||||
setTracesFilters(initialFilters);
|
setTracesFilters(initialFilters);
|
||||||
@ -143,6 +151,7 @@ function HostMetricsDetails({
|
|||||||
const handleTimeChange = useCallback(
|
const handleTimeChange = useCallback(
|
||||||
(interval: Time | CustomTimeType, dateTimeRange?: [number, number]): void => {
|
(interval: Time | CustomTimeType, dateTimeRange?: [number, number]): void => {
|
||||||
setSelectedInterval(interval as Time);
|
setSelectedInterval(interval as Time);
|
||||||
|
|
||||||
if (interval === 'custom' && dateTimeRange) {
|
if (interval === 'custom' && dateTimeRange) {
|
||||||
setModalTimeRange({
|
setModalTimeRange({
|
||||||
startTime: Math.floor(dateTimeRange[0] / 1000),
|
startTime: Math.floor(dateTimeRange[0] / 1000),
|
||||||
@ -156,7 +165,13 @@ function HostMetricsDetails({
|
|||||||
endTime: Math.floor(maxTime / 1000000000),
|
endTime: Math.floor(maxTime / 1000000000),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: Hosts list details time updated', {
|
||||||
|
host: host?.hostName,
|
||||||
|
interval,
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -171,6 +186,10 @@ function HostMetricsDetails({
|
|||||||
(item) => item.key?.key !== 'id' && item.key?.key !== 'host.name',
|
(item) => item.key?.key !== 'id' && item.key?.key !== 'host.name',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: Hosts list details logs filters applied', {
|
||||||
|
host: host?.hostName,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
items: [
|
items: [
|
||||||
@ -181,6 +200,7 @@ function HostMetricsDetails({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -190,6 +210,11 @@ function HostMetricsDetails({
|
|||||||
const hostNameFilter = prevFilters.items.find(
|
const hostNameFilter = prevFilters.items.find(
|
||||||
(item) => item.key?.key === 'host.name',
|
(item) => item.key?.key === 'host.name',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: Hosts list details traces filters applied', {
|
||||||
|
host: host?.hostName,
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
op: 'AND',
|
op: 'AND',
|
||||||
items: [
|
items: [
|
||||||
@ -199,6 +224,7 @@ function HostMetricsDetails({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -211,6 +237,11 @@ function HostMetricsDetails({
|
|||||||
urlQuery.set(QueryParams.endTime, modalTimeRange.endTime.toString());
|
urlQuery.set(QueryParams.endTime, modalTimeRange.endTime.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: Hosts list details explore clicked', {
|
||||||
|
host: host?.hostName,
|
||||||
|
view: selectedView,
|
||||||
|
});
|
||||||
|
|
||||||
if (selectedView === VIEW_TYPES.LOGS) {
|
if (selectedView === VIEW_TYPES.LOGS) {
|
||||||
const filtersWithoutPagination = {
|
const filtersWithoutPagination = {
|
||||||
...logFilters,
|
...logFilters,
|
||||||
|
@ -1,7 +1,24 @@
|
|||||||
.host-processes {
|
.host-processes {
|
||||||
max-width: 600px;
|
gap: 24px;
|
||||||
margin: 150px auto;
|
height: 60vh;
|
||||||
padding: 0 16px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.infra-container-card-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dev-status-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.infra-container-card {
|
.infra-container-card {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -17,6 +34,7 @@
|
|||||||
width: 400px;
|
width: 400px;
|
||||||
font-family: 'Inter';
|
font-family: 'Inter';
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
font-weight: 300;
|
||||||
}
|
}
|
||||||
|
|
||||||
.infra-container-working-msg {
|
.infra-container-working-msg {
|
||||||
|
@ -3,6 +3,8 @@ import './Processes.styles.scss';
|
|||||||
import { Space, Typography } from 'antd';
|
import { Space, Typography } from 'antd';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
import WaitlistFragment from '../WaitlistFragment/WaitlistFragment';
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
function Processes(): JSX.Element {
|
function Processes(): JSX.Element {
|
||||||
@ -10,23 +12,29 @@ function Processes(): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" className="host-processes" size={24}>
|
<Space direction="vertical" className="host-processes" size={24}>
|
||||||
<div className="infra-container-card">
|
<div className="infra-container-card-container">
|
||||||
<img
|
<div className="dev-status-container">
|
||||||
src="/Icons/infraContainers.svg"
|
<div className="infra-container-card">
|
||||||
alt="infra-container"
|
<img
|
||||||
width={32}
|
src="/Icons/infraContainers.svg"
|
||||||
height={32}
|
alt="infra-container"
|
||||||
/>
|
width={32}
|
||||||
<Text className="infra-container-card-text">
|
height={32}
|
||||||
{t('processes_visualization_message')}
|
/>
|
||||||
</Text>
|
<Text className="infra-container-card-text">
|
||||||
</div>
|
{t('processes_visualization_message')}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="infra-container-working-msg">
|
<div className="infra-container-working-msg">
|
||||||
<Space>
|
<Space>
|
||||||
<img src="/Icons/broom.svg" alt="broom" width={16} height={16} />
|
<img src="/Icons/broom.svg" alt="broom" width={24} height={24} />
|
||||||
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
||||||
</Space>
|
</Space>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<WaitlistFragment entityType="processes" />
|
||||||
</div>
|
</div>
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
.wait-list-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.wait-list-text {
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-waitlist-btn {
|
||||||
|
width: 160px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: var(--slate-500);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,75 @@
|
|||||||
|
import './WaitListFragment.styles.scss';
|
||||||
|
|
||||||
|
import { Color } from '@signozhq/design-tokens';
|
||||||
|
import { Button, Typography } from 'antd';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
|
import { CheckCircle2, HandPlatter } from 'lucide-react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
|
export default function WaitlistFragment({
|
||||||
|
entityType,
|
||||||
|
}: {
|
||||||
|
entityType: string;
|
||||||
|
}): JSX.Element {
|
||||||
|
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
const { t } = useTranslation(['infraMonitoring']);
|
||||||
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
const [isSuccess, setIsSuccess] = useState(false);
|
||||||
|
|
||||||
|
const handleJoinWaitlist = (): void => {
|
||||||
|
if (!user || !user.email) return;
|
||||||
|
|
||||||
|
setIsSubmitting(true);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: Get Early Access Clicked', {
|
||||||
|
entity_type: entityType,
|
||||||
|
userEmail: user.email,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
notifications.success({
|
||||||
|
message: t('waitlist_success_message'),
|
||||||
|
});
|
||||||
|
|
||||||
|
setIsSubmitting(false);
|
||||||
|
setIsSuccess(true);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsSuccess(false);
|
||||||
|
}, 4000);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('Error logging event:', error);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="wait-list-container">
|
||||||
|
<Typography.Text className="wait-list-text">
|
||||||
|
{t('waitlist_message')}
|
||||||
|
</Typography.Text>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className="periscope-btn join-waitlist-btn"
|
||||||
|
type="default"
|
||||||
|
loading={isSubmitting}
|
||||||
|
icon={
|
||||||
|
isSuccess ? (
|
||||||
|
<CheckCircle2 size={16} color={Color.BG_FOREST_500} />
|
||||||
|
) : (
|
||||||
|
<HandPlatter size={16} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
onClick={handleJoinWaitlist}
|
||||||
|
>
|
||||||
|
Get early access
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
import { Typography } from 'antd';
|
||||||
|
|
||||||
|
export default function HostsEmptyOrIncorrectMetrics({
|
||||||
|
noData,
|
||||||
|
incorrectData,
|
||||||
|
}: {
|
||||||
|
noData: boolean;
|
||||||
|
incorrectData: boolean;
|
||||||
|
}): JSX.Element {
|
||||||
|
return (
|
||||||
|
<div className="hosts-empty-state-container">
|
||||||
|
<div className="hosts-empty-state-container-content">
|
||||||
|
<img className="eyes-emoji" src="/Images/eyesEmoji.svg" alt="eyes emoji" />
|
||||||
|
|
||||||
|
{noData && (
|
||||||
|
<div className="no-hosts-message">
|
||||||
|
<Typography.Title level={5} className="no-hosts-message-title">
|
||||||
|
No host metrics data received yet.
|
||||||
|
</Typography.Title>
|
||||||
|
|
||||||
|
<Typography.Text className="no-hosts-message-text">
|
||||||
|
Infrastructure monitoring requires the{' '}
|
||||||
|
<a
|
||||||
|
href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/system/system-metrics.md"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
OpenTelemetry system metrics
|
||||||
|
</a>
|
||||||
|
. Please refer to{' '}
|
||||||
|
<a
|
||||||
|
href="https://signoz.io/docs/userguide/hostmetrics"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
this
|
||||||
|
</a>{' '}
|
||||||
|
to learn how to send host metrics to SigNoz.
|
||||||
|
</Typography.Text>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{incorrectData && (
|
||||||
|
<Typography.Text className="incorrect-metrics-message">
|
||||||
|
To see host metrics, upgrade to the latest version of SigNoz k8s-infra
|
||||||
|
chart. Please contact support if you need help.
|
||||||
|
</Typography.Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -2,6 +2,7 @@ import './InfraMonitoring.styles.scss';
|
|||||||
|
|
||||||
import { LoadingOutlined } from '@ant-design/icons';
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
|
Skeleton,
|
||||||
Spin,
|
Spin,
|
||||||
Table,
|
Table,
|
||||||
TablePaginationConfig,
|
TablePaginationConfig,
|
||||||
@ -9,17 +10,17 @@ import {
|
|||||||
Typography,
|
Typography,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import { SorterResult } from 'antd/es/table/interface';
|
import { SorterResult } from 'antd/es/table/interface';
|
||||||
|
import logEvent from 'api/common/logEvent';
|
||||||
import { HostListPayload } from 'api/infraMonitoring/getHostLists';
|
import { HostListPayload } from 'api/infraMonitoring/getHostLists';
|
||||||
import HostMetricDetail from 'components/HostMetricsDetail';
|
import HostMetricDetail from 'components/HostMetricsDetail';
|
||||||
import NoLogs from 'container/NoLogs/NoLogs';
|
|
||||||
import { useGetHostList } from 'hooks/infraMonitoring/useGetHostList';
|
import { useGetHostList } from 'hooks/infraMonitoring/useGetHostList';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||||
import { DataSource } from 'types/common/queryBuilder';
|
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
|
import HostsEmptyOrIncorrectMetrics from './HostsEmptyOrIncorrectMetrics';
|
||||||
import HostsListControls from './HostsListControls';
|
import HostsListControls from './HostsListControls';
|
||||||
import {
|
import {
|
||||||
formatDataForTable,
|
formatDataForTable,
|
||||||
@ -28,6 +29,7 @@ import {
|
|||||||
HostRowData,
|
HostRowData,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
|
||||||
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||||
function HostsList(): JSX.Element {
|
function HostsList(): JSX.Element {
|
||||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||||
(state) => state.globalTime,
|
(state) => state.globalTime,
|
||||||
@ -69,6 +71,16 @@ function HostsList(): JSX.Element {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const sentAnyHostMetricsData = useMemo(
|
||||||
|
() => data?.payload?.data?.sentAnyHostMetricsData || false,
|
||||||
|
[data],
|
||||||
|
);
|
||||||
|
|
||||||
|
const isSendingIncorrectK8SAgentMetrics = useMemo(
|
||||||
|
() => data?.payload?.data?.isSendingK8SAgentMetrics || false,
|
||||||
|
[data],
|
||||||
|
);
|
||||||
|
|
||||||
const hostMetricsData = useMemo(() => data?.payload?.data?.records || [], [
|
const hostMetricsData = useMemo(() => data?.payload?.data?.records || [], [
|
||||||
data,
|
data,
|
||||||
]);
|
]);
|
||||||
@ -81,9 +93,6 @@ function HostsList(): JSX.Element {
|
|||||||
|
|
||||||
const columns = useMemo(() => getHostsListColumns(), []);
|
const columns = useMemo(() => getHostsListColumns(), []);
|
||||||
|
|
||||||
const isDataPresent =
|
|
||||||
!isLoading && !isFetching && !isError && hostMetricsData.length === 0;
|
|
||||||
|
|
||||||
const handleTableChange: TableProps<HostRowData>['onChange'] = useCallback(
|
const handleTableChange: TableProps<HostRowData>['onChange'] = useCallback(
|
||||||
(
|
(
|
||||||
pagination: TablePaginationConfig,
|
pagination: TablePaginationConfig,
|
||||||
@ -112,11 +121,19 @@ function HostsList(): JSX.Element {
|
|||||||
if (isNewFilterAdded) {
|
if (isNewFilterAdded) {
|
||||||
setFilters(value);
|
setFilters(value);
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: Hosts list filters applied', {
|
||||||
|
filters: value,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[filters],
|
[filters],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
logEvent('Infra Monitoring: Hosts list page visited', {});
|
||||||
|
}, []);
|
||||||
|
|
||||||
const selectedHostData = useMemo(() => {
|
const selectedHostData = useMemo(() => {
|
||||||
if (!selectedHostName) return null;
|
if (!selectedHostName) return null;
|
||||||
return (
|
return (
|
||||||
@ -126,29 +143,85 @@ function HostsList(): JSX.Element {
|
|||||||
|
|
||||||
const handleRowClick = (record: HostRowData): void => {
|
const handleRowClick = (record: HostRowData): void => {
|
||||||
setSelectedHostName(record.hostName);
|
setSelectedHostName(record.hostName);
|
||||||
|
|
||||||
|
logEvent('Infra Monitoring: Hosts list item clicked', {
|
||||||
|
host: record.hostName,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseHostDetail = (): void => {
|
const handleCloseHostDetail = (): void => {
|
||||||
setSelectedHostName(null);
|
setSelectedHostName(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showHostsTable =
|
||||||
|
!isError &&
|
||||||
|
sentAnyHostMetricsData &&
|
||||||
|
!isSendingIncorrectK8SAgentMetrics &&
|
||||||
|
!(formattedHostMetricsData.length === 0 && filters.items.length > 0);
|
||||||
|
|
||||||
|
const showNoFilteredHostsMessage =
|
||||||
|
!isFetching &&
|
||||||
|
!isLoading &&
|
||||||
|
formattedHostMetricsData.length === 0 &&
|
||||||
|
filters.items.length > 0;
|
||||||
|
|
||||||
|
const showHostsEmptyState =
|
||||||
|
!isFetching &&
|
||||||
|
!isLoading &&
|
||||||
|
(!sentAnyHostMetricsData || isSendingIncorrectK8SAgentMetrics);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="hosts-list">
|
<div className="hosts-list">
|
||||||
<HostsListControls handleFiltersChange={handleFiltersChange} />
|
<HostsListControls handleFiltersChange={handleFiltersChange} />
|
||||||
{isError && <Typography>{data?.error || 'Something went wrong'}</Typography>}
|
{isError && <Typography>{data?.error || 'Something went wrong'}</Typography>}
|
||||||
|
|
||||||
{isDataPresent && filters.items.length === 0 && (
|
{showHostsEmptyState && (
|
||||||
<NoLogs dataSource={DataSource.METRICS} />
|
<HostsEmptyOrIncorrectMetrics
|
||||||
|
noData={!sentAnyHostMetricsData}
|
||||||
|
incorrectData={isSendingIncorrectK8SAgentMetrics}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isFetching &&
|
{showNoFilteredHostsMessage && (
|
||||||
!isLoading &&
|
<div className="no-filtered-hosts-message-container">
|
||||||
formattedHostMetricsData.length === 0 &&
|
<div className="no-filtered-hosts-message-content">
|
||||||
filters.items.length > 0 && (
|
<img
|
||||||
<div className="no-hosts-message">No hosts match the applied filters.</div>
|
src="/Icons/emptyState.svg"
|
||||||
)}
|
alt="thinking-emoji"
|
||||||
|
className="empty-state-svg"
|
||||||
|
/>
|
||||||
|
|
||||||
{!isError && (
|
<Typography.Text className="no-filtered-hosts-message">
|
||||||
|
This query had no results. Edit your query and try again!
|
||||||
|
</Typography.Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(isFetching || isLoading) && (
|
||||||
|
<div className="hosts-list-loading-state">
|
||||||
|
<Skeleton.Input
|
||||||
|
className="hosts-list-loading-state-item"
|
||||||
|
size="large"
|
||||||
|
block
|
||||||
|
active
|
||||||
|
/>
|
||||||
|
<Skeleton.Input
|
||||||
|
className="hosts-list-loading-state-item"
|
||||||
|
size="large"
|
||||||
|
block
|
||||||
|
active
|
||||||
|
/>
|
||||||
|
<Skeleton.Input
|
||||||
|
className="hosts-list-loading-state-item"
|
||||||
|
size="large"
|
||||||
|
block
|
||||||
|
active
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showHostsTable && (
|
||||||
<Table
|
<Table
|
||||||
className="hosts-list-table"
|
className="hosts-list-table"
|
||||||
dataSource={isFetching || isLoading ? [] : formattedHostMetricsData}
|
dataSource={isFetching || isLoading ? [] : formattedHostMetricsData}
|
||||||
|
@ -10,10 +10,6 @@
|
|||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-hosts-message {
|
|
||||||
padding: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.hosts-list-controls {
|
.hosts-list-controls {
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
|
|
||||||
@ -210,6 +206,68 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hosts-list-loading-state {
|
||||||
|
padding: 8px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
|
||||||
|
.hosts-list-loading-state-item {
|
||||||
|
height: 48px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-filtered-hosts-message-container {
|
||||||
|
height: 30vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.no-filtered-hosts-message-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: fit-content;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-filtered-hosts-message {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hosts-empty-state-container {
|
||||||
|
padding: 16px;
|
||||||
|
height: 40vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.hosts-empty-state-container-content {
|
||||||
|
padding: 16px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: fit-content;
|
||||||
|
|
||||||
|
.no-hosts-message {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.no-hosts-message-title {
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.lightMode {
|
.lightMode {
|
||||||
.infra-monitoring-container {
|
.infra-monitoring-container {
|
||||||
.ant-table-thead > tr > th {
|
.ant-table-thead > tr > th {
|
||||||
|
@ -16,7 +16,7 @@ export default function NavItem({
|
|||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
onClick: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
onClick: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { label, icon, isBeta } = item;
|
const { label, icon, isBeta, isNew } = item;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -36,6 +36,14 @@ export default function NavItem({
|
|||||||
</Tag>
|
</Tag>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{isNew && (
|
||||||
|
<div className="nav-item-new">
|
||||||
|
<Tag bordered={false} className="sidenav-new-tag">
|
||||||
|
New
|
||||||
|
</Tag>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -184,10 +184,16 @@
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item-beta {
|
.nav-item-beta,
|
||||||
|
.nav-item-new {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidenav-new-tag {
|
||||||
|
background-color: rgba(37, 225, 146, 0.1);
|
||||||
|
color: var(--text-forest-500);
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
flex: 0 0 240px;
|
flex: 0 0 240px;
|
||||||
max-width: 240px;
|
max-width: 240px;
|
||||||
@ -221,7 +227,8 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-item-beta {
|
.nav-item-beta,
|
||||||
|
.nav-item-new {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import {
|
import {
|
||||||
BarChart2,
|
BarChart2,
|
||||||
BellDot,
|
BellDot,
|
||||||
|
Boxes,
|
||||||
BugIcon,
|
BugIcon,
|
||||||
Cloudy,
|
Cloudy,
|
||||||
DraftingCompass,
|
DraftingCompass,
|
||||||
@ -11,7 +12,6 @@ import {
|
|||||||
LayoutGrid,
|
LayoutGrid,
|
||||||
ListMinus,
|
ListMinus,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
PackagePlus,
|
|
||||||
Receipt,
|
Receipt,
|
||||||
Route,
|
Route,
|
||||||
ScrollText,
|
ScrollText,
|
||||||
@ -82,6 +82,12 @@ const menuItems: SidebarItem[] = [
|
|||||||
label: 'Logs',
|
label: 'Logs',
|
||||||
icon: <ScrollText size={16} />,
|
icon: <ScrollText size={16} />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
|
||||||
|
label: 'Infra Monitoring',
|
||||||
|
icon: <Boxes size={16} />,
|
||||||
|
isNew: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: ROUTES.ALL_DASHBOARD,
|
key: ROUTES.ALL_DASHBOARD,
|
||||||
label: 'Dashboards',
|
label: 'Dashboards',
|
||||||
@ -91,7 +97,6 @@ const menuItems: SidebarItem[] = [
|
|||||||
key: ROUTES.MESSAGING_QUEUES,
|
key: ROUTES.MESSAGING_QUEUES,
|
||||||
label: 'Messaging Queues',
|
label: 'Messaging Queues',
|
||||||
icon: <ListMinus size={16} />,
|
icon: <ListMinus size={16} />,
|
||||||
isBeta: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: ROUTES.LIST_ALL_ALERT,
|
key: ROUTES.LIST_ALL_ALERT,
|
||||||
@ -119,12 +124,6 @@ const menuItems: SidebarItem[] = [
|
|||||||
label: 'Billing',
|
label: 'Billing',
|
||||||
icon: <Receipt size={16} />,
|
icon: <Receipt size={16} />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
|
|
||||||
label: 'Infra Monitoring',
|
|
||||||
icon: <PackagePlus size={16} />,
|
|
||||||
isBeta: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: ROUTES.SETTINGS,
|
key: ROUTES.SETTINGS,
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
|
@ -13,6 +13,7 @@ export interface SidebarItem {
|
|||||||
key: string | number;
|
key: string | number;
|
||||||
label?: ReactNode;
|
label?: ReactNode;
|
||||||
isBeta?: boolean;
|
isBeta?: boolean;
|
||||||
|
isNew?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SecondaryMenuItemKey {
|
export enum SecondaryMenuItemKey {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user