/* eslint-disable sonarjs/no-duplicate-string */ import './Home.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Alert, Button, Popover } from 'antd'; import logEvent from 'api/common/logEvent'; import { HostListPayload } from 'api/infraMonitoring/getHostLists'; import { K8sPodsListPayload } from 'api/infraMonitoring/getK8sPodsList'; import getAllUserPreferences from 'api/preferences/getAllUserPreference'; import updateUserPreferenceAPI from 'api/preferences/updateUserPreference'; import Header from 'components/Header/Header'; import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { LOCALSTORAGE } from 'constants/localStorage'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import ROUTES from 'constants/routes'; import { getHostListsQuery } from 'container/InfraMonitoringHosts/utils'; import { useGetDeploymentsData } from 'hooks/CustomDomain/useGetDeploymentsData'; import { useGetHostList } from 'hooks/infraMonitoring/useGetHostList'; import { useGetK8sPodsList } from 'hooks/infraMonitoring/useGetK8sPodsList'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useGetTenantLicense } from 'hooks/useGetTenantLicense'; import history from 'lib/history'; import cloneDeep from 'lodash-es/cloneDeep'; import { CompassIcon, DotIcon, HomeIcon, Plus, Wrench, X } from 'lucide-react'; import { AnimatePresence } from 'motion/react'; import * as motion from 'motion/react-client'; import Card from 'periscope/components/Card/Card'; import { useAppContext } from 'providers/App/App'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useMutation, useQuery } from 'react-query'; import { LicensePlatform } from 'types/api/licensesV3/getActive'; import { DataSource } from 'types/common/queryBuilder'; import { UserPreference } from 'types/reducer/app'; import { USER_ROLES } from 'types/roles'; import { popupContainer } from 'utils/selectPopupContainer'; import AlertRules from './AlertRules/AlertRules'; import { defaultChecklistItemsState } from './constants'; import Dashboards from './Dashboards/Dashboards'; import DataSourceInfo from './DataSourceInfo/DataSourceInfo'; import HomeChecklist, { ChecklistItem } from './HomeChecklist/HomeChecklist'; import SavedViews from './SavedViews/SavedViews'; import Services from './Services/Services'; import StepsProgress from './StepsProgress/StepsProgress'; const homeInterval = 30 * 60 * 1000; // eslint-disable-next-line sonarjs/cognitive-complexity export default function Home(): JSX.Element { const { user } = useAppContext(); const [startTime, setStartTime] = useState(null); const [endTime, setEndTime] = useState(null); const [updatingUserPreferences, setUpdatingUserPreferences] = useState(false); const [loadingUserPreferences, setLoadingUserPreferences] = useState(true); const { isCommunityUser, isCommunityEnterpriseUser } = useGetTenantLicense(); const [checklistItems, setChecklistItems] = useState( defaultChecklistItemsState, ); const [isWelcomeChecklistSkipped, setIsWelcomeChecklistSkipped] = useState( false, ); const [isBannerDismissed, setIsBannerDismissed] = useState(false); useEffect(() => { const bannerDismissed = localStorage.getItem(LOCALSTORAGE.BANNER_DISMISSED); setIsBannerDismissed(bannerDismissed === 'true'); }, []); useEffect(() => { const now = new Date(); const startTime = new Date(now.getTime() - homeInterval); const endTime = now; setStartTime(startTime.getTime()); setEndTime(endTime.getTime()); }, []); // Detect Logs const { data: logsData, isLoading: isLogsLoading } = useGetQueryRange( { query: initialQueriesMap[DataSource.LOGS], graphType: PANEL_TYPES.TABLE, selectedTime: 'GLOBAL_TIME', globalSelectedInterval: '30m', params: { dataSource: DataSource.LOGS, }, }, DEFAULT_ENTITY_VERSION, { queryKey: [ REACT_QUERY_KEY.GET_QUERY_RANGE, '30m', endTime || Date.now(), startTime || Date.now(), initialQueriesMap[DataSource.LOGS], ], enabled: !!startTime && !!endTime, }, ); // Detect Traces const { data: tracesData, isLoading: isTracesLoading } = useGetQueryRange( { query: initialQueriesMap[DataSource.TRACES], graphType: PANEL_TYPES.TABLE, selectedTime: 'GLOBAL_TIME', globalSelectedInterval: '30m', params: { dataSource: DataSource.TRACES, }, }, DEFAULT_ENTITY_VERSION, { queryKey: [ REACT_QUERY_KEY.GET_QUERY_RANGE, '30m', endTime || Date.now(), startTime || Date.now(), initialQueriesMap[DataSource.TRACES], ], enabled: !!startTime && !!endTime, }, ); // Detect Infra Metrics - Hosts const query = useMemo(() => { const baseQuery = getHostListsQuery(); let queryStartTime = startTime; let queryEndTime = endTime; if (!startTime || !endTime) { const now = new Date(); const startTime = new Date(now.getTime() - homeInterval); const endTime = now; queryStartTime = startTime.getTime(); queryEndTime = endTime.getTime(); } return { ...baseQuery, limit: 10, offset: 0, filters: { items: [], op: 'AND', }, start: queryStartTime, end: queryEndTime, }; }, [startTime, endTime]); const { data: hostData } = useGetHostList(query as HostListPayload, { queryKey: ['hostList', query], enabled: !!query, }); const { data: k8sPodsData } = useGetK8sPodsList(query as K8sPodsListPayload, { queryKey: ['K8sPodsList', query], enabled: !!query, }); const [isLogsIngestionActive, setIsLogsIngestionActive] = useState(false); const [isTracesIngestionActive, setIsTracesIngestionActive] = useState(false); const [isMetricsIngestionActive, setIsMetricsIngestionActive] = useState( false, ); const processUserPreferences = (userPreferences: UserPreference[]): void => { const checklistSkipped = userPreferences?.find( (preference) => preference.key === 'WELCOME_CHECKLIST_DO_LATER', )?.value; const updatedChecklistItems = cloneDeep(checklistItems); const newChecklistItems = updatedChecklistItems.map((item) => { const newItem = { ...item }; newItem.isSkipped = userPreferences?.find( (preference) => preference.key === item.skippedPreferenceKey, )?.value || false; return newItem; }); setChecklistItems(newChecklistItems); setIsWelcomeChecklistSkipped(checklistSkipped || false); }; // Fetch User Preferences const { refetch: refetchUserPreferences } = useQuery({ queryFn: () => getAllUserPreferences(), queryKey: ['getUserPreferences'], enabled: true, refetchOnWindowFocus: false, onSuccess: (response) => { if (response.payload && response.payload.data) { processUserPreferences(response.payload.data); } setLoadingUserPreferences(false); setUpdatingUserPreferences(false); }, onError: () => { setUpdatingUserPreferences(false); setLoadingUserPreferences(false); }, }); const { mutate: updateUserPreference } = useMutation(updateUserPreferenceAPI, { onSuccess: () => { setUpdatingUserPreferences(false); refetchUserPreferences(); }, onError: () => { setUpdatingUserPreferences(false); }, }); const handleWillDoThisLater = (): void => { logEvent('Welcome Checklist: Will do this later clicked', {}); setUpdatingUserPreferences(true); updateUserPreference({ preferenceID: 'WELCOME_CHECKLIST_DO_LATER', value: true, }); }; const handleSkipChecklistItem = (item: ChecklistItem): void => { if (item.skippedPreferenceKey) { setUpdatingUserPreferences(true); updateUserPreference({ preferenceID: item.skippedPreferenceKey, value: true, }); } }; const renderWelcomeChecklistModal = (): JSX.Element => (
); const handleUpdateChecklistDoneItem = useCallback((itemKey: string): void => { setChecklistItems((prevItems) => prevItems.map((item) => item.id === itemKey ? { ...item, completed: true } : item, ), ); }, []); useEffect(() => { const logsDataTotal = parseInt( logsData?.payload?.data?.newResult?.data?.result?.[0]?.series?.[0] ?.values?.[0]?.value || '0', 10, ); if (logsDataTotal > 0) { setIsLogsIngestionActive(true); handleUpdateChecklistDoneItem('SEND_LOGS'); handleUpdateChecklistDoneItem('ADD_DATA_SOURCE'); } }, [logsData, handleUpdateChecklistDoneItem]); useEffect(() => { const tracesDataTotal = parseInt( tracesData?.payload?.data?.newResult?.data?.result?.[0]?.series?.[0] ?.values?.[0]?.value || '0', 10, ); if (tracesDataTotal > 0) { setIsTracesIngestionActive(true); handleUpdateChecklistDoneItem('SEND_TRACES'); handleUpdateChecklistDoneItem('ADD_DATA_SOURCE'); } }, [tracesData, handleUpdateChecklistDoneItem]); useEffect(() => { const hostDataTotal = hostData?.payload?.data?.total ?? 0; const k8sPodsDataTotal = k8sPodsData?.payload?.data?.total ?? 0; if (hostDataTotal > 0 || k8sPodsDataTotal > 0) { setIsMetricsIngestionActive(true); handleUpdateChecklistDoneItem('ADD_DATA_SOURCE'); handleUpdateChecklistDoneItem('SEND_INFRA_METRICS'); } }, [hostData, k8sPodsData, handleUpdateChecklistDoneItem]); const { activeLicense, isFetchingActiveLicense } = useAppContext(); const [isEnabled, setIsEnabled] = useState(false); useEffect(() => { if (isFetchingActiveLicense) { setIsEnabled(false); return; } setIsEnabled(Boolean(activeLicense?.platform === LicensePlatform.CLOUD)); }, [activeLicense, isFetchingActiveLicense]); const { data: deploymentsData } = useGetDeploymentsData(isEnabled); useEffect(() => { logEvent('Homepage: Visited', {}); }, []); const hideBanner = (): void => { localStorage.setItem(LOCALSTORAGE.BANNER_DISMISSED, 'true'); setIsBannerDismissed(true); }; const showBanner = useMemo( () => !isBannerDismissed && (isCommunityUser || isCommunityEnterpriseUser), [isBannerDismissed, isCommunityUser, isCommunityEnterpriseUser], ); return (
{showBanner && (
Big News: SigNoz Community Edition now available with SSO (Google OAuth) and API keys - read more
)}
Home
} rightComponent={
{isWelcomeChecklistSkipped && ( { if (visible) { logEvent('Welcome Checklist: Expanded', {}); } else { logEvent('Welcome Checklist: Minimized', {}); } }} content={renderWelcomeChecklistModal()} getPopupContainer={popupContainer} rootClassName="welcome-checklist-popover" > )}
} />
divider
{isLogsIngestionActive && (
Logs ingestion is active
{ // eslint-disable-next-line sonarjs/no-duplicate-string logEvent('Homepage: Ingestion Active Explore clicked', { source: 'Logs', }); history.push(ROUTES.LOGS_EXPLORER); }} onKeyDown={(e): void => { if (e.key === 'Enter') { logEvent('Homepage: Ingestion Active Explore clicked', { source: 'Logs', }); history.push(ROUTES.LOGS_EXPLORER); } }} > Explore Logs
)} {isTracesIngestionActive && (
Traces ingestion is active
{ logEvent('Homepage: Ingestion Active Explore clicked', { source: 'Traces', }); history.push(ROUTES.TRACES_EXPLORER); }} onKeyDown={(e): void => { if (e.key === 'Enter') { logEvent('Homepage: Ingestion Active Explore clicked', { source: 'Traces', }); history.push(ROUTES.TRACES_EXPLORER); } }} > Explore Traces
)} {isMetricsIngestionActive && (
Metrics ingestion is active
{ logEvent('Homepage: Ingestion Active Explore clicked', { source: 'Metrics', }); history.push(ROUTES.INFRASTRUCTURE_MONITORING_HOSTS); }} onKeyDown={(e): void => { if (e.key === 'Enter') { logEvent('Homepage: Ingestion Active Explore clicked', { source: 'Metrics', }); history.push(ROUTES.INFRASTRUCTURE_MONITORING_HOSTS); } }} > Explore Infra Metrics
)}
{user?.role !== USER_ROLES.VIEWER && (
wrench
Filter and save views with the Explorer
Explore your data, and save useful views for everyone in the team.
dashboard
Create a dashboard
Create a dashboard to visualize your data.
cracker
Add an alert
Create bespoke alerting rules to suit your needs.
)} {(isLogsIngestionActive || isTracesIngestionActive || isMetricsIngestionActive) && ( <> )}
{deploymentsData?.data?.data?.cluster?.region?.name === 'in' && (
We're updating our metric ingestion processing pipeline. Currently, metric names and labels are normalized to replace dots and other special characters with underscores (_). This restriction will soon be removed. Learn more{' '} here . } type="warning" showIcon />
)} {!isWelcomeChecklistSkipped && !loadingUserPreferences && (
not-found
checklist-img
)} {(isLogsIngestionActive || isTracesIngestionActive || isMetricsIngestionActive) && ( <> )}
); }