diff --git a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml index 480c1f2f43..88453360f9 100644 --- a/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml +++ b/deploy/docker-swarm/clickhouse-setup/docker-compose.yaml @@ -146,7 +146,7 @@ services: condition: on-failure query-service: - image: signoz/query-service:0.60.0 + image: signoz/query-service:0.61.0 command: [ "-config=/root/config/prometheus.yml", @@ -186,7 +186,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:0.60.0 + image: signoz/frontend:0.61.0 deploy: restart_policy: condition: on-failure @@ -199,7 +199,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector: - image: signoz/signoz-otel-collector:0.111.13 + image: signoz/signoz-otel-collector:0.111.14 command: [ "--config=/etc/otel-collector-config.yaml", @@ -237,7 +237,7 @@ services: - query-service otel-collector-migrator: - image: signoz/signoz-schema-migrator:0.111.13 + image: signoz/signoz-schema-migrator:0.111.14 deploy: restart_policy: condition: on-failure diff --git a/deploy/docker/clickhouse-setup/docker-compose-core.yaml b/deploy/docker/clickhouse-setup/docker-compose-core.yaml index 69403a4a87..f4c1d9f1ee 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-core.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-core.yaml @@ -69,7 +69,7 @@ services: - --storage.path=/data otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.111.13} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.111.14} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -84,7 +84,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` otel-collector: container_name: signoz-otel-collector - image: signoz/signoz-otel-collector:0.111.13 + image: signoz/signoz-otel-collector:0.111.14 command: [ "--config=/etc/otel-collector-config.yaml", diff --git a/deploy/docker/clickhouse-setup/docker-compose-minimal.yaml b/deploy/docker/clickhouse-setup/docker-compose-minimal.yaml index 4654528ef6..f737b7d440 100644 --- a/deploy/docker/clickhouse-setup/docker-compose-minimal.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose-minimal.yaml @@ -162,7 +162,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.60.0} + image: signoz/query-service:${DOCKER_TAG:-0.61.0} container_name: signoz-query-service command: [ @@ -201,7 +201,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.60.0} + image: signoz/frontend:${DOCKER_TAG:-0.61.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -213,7 +213,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator-sync: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.111.13} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.111.14} container_name: otel-migrator-sync command: - "sync" @@ -228,7 +228,7 @@ services: # condition: service_healthy otel-collector-migrator-async: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.111.13} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.111.14} container_name: otel-migrator-async command: - "async" @@ -245,7 +245,7 @@ services: # condition: service_healthy otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.13} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.14} container_name: signoz-otel-collector command: [ diff --git a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml index b2bef9165f..d90773844e 100644 --- a/deploy/docker/clickhouse-setup/docker-compose.testing.yaml +++ b/deploy/docker/clickhouse-setup/docker-compose.testing.yaml @@ -167,7 +167,7 @@ services: # Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md` query-service: - image: signoz/query-service:${DOCKER_TAG:-0.60.0} + image: signoz/query-service:${DOCKER_TAG:-0.61.0} container_name: signoz-query-service command: [ @@ -208,7 +208,7 @@ services: <<: *db-depend frontend: - image: signoz/frontend:${DOCKER_TAG:-0.60.0} + image: signoz/frontend:${DOCKER_TAG:-0.61.0} container_name: signoz-frontend restart: on-failure depends_on: @@ -220,7 +220,7 @@ services: - ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf otel-collector-migrator: - image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.111.13} + image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.111.14} container_name: otel-migrator command: - "--dsn=tcp://clickhouse:9000" @@ -234,7 +234,7 @@ services: otel-collector: - image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.13} + image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.111.14} container_name: signoz-otel-collector command: [ diff --git a/ee/query-service/app/api/api.go b/ee/query-service/app/api/api.go index 5efa43149a..5d7d6d2ffa 100644 --- a/ee/query-service/app/api/api.go +++ b/ee/query-service/app/api/api.go @@ -183,17 +183,10 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddlew Methods(http.MethodGet) // v3 - router.HandleFunc("/api/v3/licenses", - am.ViewAccess(ah.listLicensesV3)). - Methods(http.MethodGet) - - router.HandleFunc("/api/v3/licenses", - am.AdminAccess(ah.applyLicenseV3)). - Methods(http.MethodPost) - - router.HandleFunc("/api/v3/licenses", - am.AdminAccess(ah.refreshLicensesV3)). - Methods(http.MethodPut) + router.HandleFunc("/api/v3/licenses", am.ViewAccess(ah.listLicensesV3)).Methods(http.MethodGet) + router.HandleFunc("/api/v3/licenses", am.AdminAccess(ah.applyLicenseV3)).Methods(http.MethodPost) + router.HandleFunc("/api/v3/licenses", am.AdminAccess(ah.refreshLicensesV3)).Methods(http.MethodPut) + router.HandleFunc("/api/v3/licenses/active", am.ViewAccess(ah.getActiveLicenseV3)).Methods(http.MethodGet) // v4 router.HandleFunc("/api/v4/query_range", am.ViewAccess(ah.queryRangeV4)).Methods(http.MethodPost) diff --git a/ee/query-service/app/api/license.go b/ee/query-service/app/api/license.go index f0272202be..7138e29f80 100644 --- a/ee/query-service/app/api/license.go +++ b/ee/query-service/app/api/license.go @@ -122,6 +122,23 @@ func (ah *APIHandler) listLicensesV3(w http.ResponseWriter, r *http.Request) { ah.Respond(w, convertLicenseV3ToListLicenseResponse(licenses)) } +func (ah *APIHandler) getActiveLicenseV3(w http.ResponseWriter, r *http.Request) { + activeLicense, err := ah.LM().GetRepo().GetActiveLicenseV3(r.Context()) + if err != nil { + RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, nil) + return + } + // return 404 not found if there is no active license + if activeLicense == nil { + RespondError(w, &model.ApiError{Typ: model.ErrorNotFound, Err: fmt.Errorf("no active license found")}, nil) + return + } + + // TODO deprecate this when we move away from key for stripe + activeLicense.Data["key"] = activeLicense.Key + render.Success(w, http.StatusOK, activeLicense.Data) +} + // this function is called by zeus when inserting licenses in the query-service func (ah *APIHandler) applyLicenseV3(w http.ResponseWriter, r *http.Request) { var licenseKey ApplyLicenseRequest diff --git a/frontend/package.json b/frontend/package.json index 705a8cc3d5..bbfd1c8564 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -40,8 +40,8 @@ "@monaco-editor/react": "^4.3.1", "@radix-ui/react-tabs": "1.0.4", "@radix-ui/react-tooltip": "1.0.7", - "@sentry/react": "7.102.1", - "@sentry/webpack-plugin": "2.16.0", + "@sentry/react": "8.41.0", + "@sentry/webpack-plugin": "2.22.6", "@signozhq/design-tokens": "1.1.4", "@uiw/react-md-editor": "3.23.5", "@visx/group": "3.3.0", diff --git a/frontend/public/Images/feature-graphic-correlation.svg b/frontend/public/Images/feature-graphic-correlation.svg new file mode 100644 index 0000000000..1bbeefb264 --- /dev/null +++ b/frontend/public/Images/feature-graphic-correlation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/locales/en-GB/failedPayment.json b/frontend/public/locales/en-GB/failedPayment.json new file mode 100644 index 0000000000..a624e47c7d --- /dev/null +++ b/frontend/public/locales/en-GB/failedPayment.json @@ -0,0 +1,12 @@ +{ + "workspaceSuspended": "Your workspace is locked", + "gotQuestions": "Got Questions?", + "contactUs": "Contact Us", + "actionHeader": "Pay to continue", + "actionDescription": "Pay now to keep enjoying all the great features you’ve been using.", + "yourDataIsSafe": "Your data is safe with us until", + "actNow": "Act now to avoid any disruptions and continue where you left off.", + "contactAdmin": "Contact your admin to proceed with the upgrade.", + "continueMyJourney": "Settle your bill to continue", + "somethingWentWrong": "Something went wrong" +} diff --git a/frontend/public/locales/en-GB/infraMonitoring.json b/frontend/public/locales/en-GB/infraMonitoring.json index edf1a5f8c8..304053b993 100644 --- a/frontend/public/locales/en-GB/infraMonitoring.json +++ b/frontend/public/locales/en-GB/infraMonitoring.json @@ -2,6 +2,7 @@ "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.", "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" } diff --git a/frontend/public/locales/en-GB/titles.json b/frontend/public/locales/en-GB/titles.json index c7e8736aba..c74d82f028 100644 --- a/frontend/public/locales/en-GB/titles.json +++ b/frontend/public/locales/en-GB/titles.json @@ -37,6 +37,7 @@ "PASSWORD_RESET": "SigNoz | Password Reset", "LIST_LICENSES": "SigNoz | List of Licenses", "WORKSPACE_LOCKED": "SigNoz | Workspace Locked", + "WORKSPACE_SUSPENDED": "SigNoz | Workspace Suspended", "SUPPORT": "SigNoz | Support", "DEFAULT": "Open source Observability Platform | SigNoz", "ALERT_HISTORY": "SigNoz | Alert Rule History", diff --git a/frontend/public/locales/en/failedPayment.json b/frontend/public/locales/en/failedPayment.json new file mode 100644 index 0000000000..a624e47c7d --- /dev/null +++ b/frontend/public/locales/en/failedPayment.json @@ -0,0 +1,12 @@ +{ + "workspaceSuspended": "Your workspace is locked", + "gotQuestions": "Got Questions?", + "contactUs": "Contact Us", + "actionHeader": "Pay to continue", + "actionDescription": "Pay now to keep enjoying all the great features you’ve been using.", + "yourDataIsSafe": "Your data is safe with us until", + "actNow": "Act now to avoid any disruptions and continue where you left off.", + "contactAdmin": "Contact your admin to proceed with the upgrade.", + "continueMyJourney": "Settle your bill to continue", + "somethingWentWrong": "Something went wrong" +} diff --git a/frontend/public/locales/en/infraMonitoring.json b/frontend/public/locales/en/infraMonitoring.json index edf1a5f8c8..304053b993 100644 --- a/frontend/public/locales/en/infraMonitoring.json +++ b/frontend/public/locales/en/infraMonitoring.json @@ -2,6 +2,7 @@ "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.", "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" } diff --git a/frontend/public/locales/en/titles.json b/frontend/public/locales/en/titles.json index 978d1d3b13..4d903b7a40 100644 --- a/frontend/public/locales/en/titles.json +++ b/frontend/public/locales/en/titles.json @@ -45,6 +45,7 @@ "PASSWORD_RESET": "SigNoz | Password Reset", "LIST_LICENSES": "SigNoz | List of Licenses", "WORKSPACE_LOCKED": "SigNoz | Workspace Locked", + "WORKSPACE_SUSPENDED": "SigNoz | Workspace Suspended", "SUPPORT": "SigNoz | Support", "LOGS_SAVE_VIEWS": "SigNoz | Logs Saved Views", "TRACES_SAVE_VIEWS": "SigNoz | Traces Saved Views", diff --git a/frontend/src/AppRoutes/Private.tsx b/frontend/src/AppRoutes/Private.tsx index 5b70b8ea6f..77ec267922 100644 --- a/frontend/src/AppRoutes/Private.tsx +++ b/frontend/src/AppRoutes/Private.tsx @@ -10,6 +10,7 @@ import useLicense from 'hooks/useLicense'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; import { isEmpty, isNull } from 'lodash-es'; +import { useAppContext } from 'providers/App/App'; import { ReactChild, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; @@ -20,6 +21,7 @@ import { AppState } from 'store/reducers'; import { getInitialUserTokenRefreshToken } from 'store/utils'; import AppActions from 'types/actions'; import { UPDATE_USER_IS_FETCH } from 'types/actions/app'; +import { LicenseState, LicenseStatus } from 'types/api/licensesV3/getActive'; import { Organization } from 'types/api/user/getOrganization'; import AppReducer from 'types/reducer/app'; import { isCloudUser } from 'utils/app'; @@ -49,6 +51,8 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element { isFetchingOrgPreferences, } = useSelector((state) => state.app); + const { activeLicenseV3, isFetchingActiveLicenseV3 } = useAppContext(); + const mapRoutes = useMemo( () => new Map( @@ -249,6 +253,33 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element { } }, [isFetchingLicensesData]); + const navigateToWorkSpaceSuspended = (route: any): void => { + const { path } = route; + + if (path && path !== ROUTES.WORKSPACE_SUSPENDED) { + history.push(ROUTES.WORKSPACE_SUSPENDED); + + dispatch({ + type: UPDATE_USER_IS_FETCH, + payload: { + isUserFetching: false, + }, + }); + } + }; + + useEffect(() => { + if (!isFetchingActiveLicenseV3 && activeLicenseV3) { + const shouldSuspendWorkspace = + activeLicenseV3.status === LicenseStatus.SUSPENDED && + activeLicenseV3.state === LicenseState.PAYMENT_FAILED; + + if (shouldSuspendWorkspace) { + navigateToWorkSpaceSuspended(currentRoute); + } + } + }, [isFetchingActiveLicenseV3, activeLicenseV3]); + useEffect(() => { if (org && org.length > 0 && org[0].id !== undefined) { setOrgData(org[0]); diff --git a/frontend/src/AppRoutes/index.tsx b/frontend/src/AppRoutes/index.tsx index 9d26d15a11..9fd759f40c 100644 --- a/frontend/src/AppRoutes/index.tsx +++ b/frontend/src/AppRoutes/index.tsx @@ -22,6 +22,7 @@ import history from 'lib/history'; import { identity, pick, pickBy } from 'lodash-es'; import posthog from 'posthog-js'; import AlertRuleProvider from 'providers/Alert'; +import { AppProvider } from 'providers/App/App'; import { DashboardProvider } from 'providers/Dashboard/Dashboard'; import { QueryBuilderProvider } from 'providers/QueryBuilder'; import { Suspense, useEffect, useState } from 'react'; @@ -128,18 +129,6 @@ function App(): JSX.Element { setRoutes(newRoutes); } - - const isInfraMonitoringEnabled = - allFlags.find((flag) => flag.name === FeatureKeys.HOSTS_INFRA_MONITORING) - ?.active || false; - - if (!isInfraMonitoringEnabled) { - const newRoutes = routes.filter( - (route) => route?.path !== ROUTES.INFRASTRUCTURE_MONITORING_HOSTS, - ); - - setRoutes(newRoutes); - } }); const isOnBasicPlan = @@ -303,42 +292,44 @@ function App(): JSX.Element { }, []); return ( - - - - - - - - - - - - }> - - {routes.map(({ path, component, exact }) => ( - - ))} + + + + + + + + + + + + + }> + + {routes.map(({ path, component, exact }) => ( + + ))} - - - - - - - - - - - - - - + + + + + + + + + + + + + + + ); } diff --git a/frontend/src/AppRoutes/pageComponents.ts b/frontend/src/AppRoutes/pageComponents.ts index 5d4729d9a3..e623357ab5 100644 --- a/frontend/src/AppRoutes/pageComponents.ts +++ b/frontend/src/AppRoutes/pageComponents.ts @@ -206,6 +206,13 @@ export const WorkspaceBlocked = Loadable( import(/* webpackChunkName: "WorkspaceLocked" */ 'pages/WorkspaceLocked'), ); +export const WorkspaceSuspended = Loadable( + () => + import( + /* webpackChunkName: "WorkspaceSuspended" */ 'pages/WorkspaceSuspended/WorkspaceSuspended' + ), +); + export const ShortcutsPage = Loadable( () => import(/* webpackChunkName: "ShortcutsPage" */ 'pages/Shortcuts'), ); diff --git a/frontend/src/AppRoutes/routes.ts b/frontend/src/AppRoutes/routes.ts index 55119db63e..480d03561b 100644 --- a/frontend/src/AppRoutes/routes.ts +++ b/frontend/src/AppRoutes/routes.ts @@ -53,6 +53,7 @@ import { UnAuthorized, UsageExplorerPage, WorkspaceBlocked, + WorkspaceSuspended, } from './pageComponents'; const routes: AppRoutes[] = [ @@ -364,6 +365,13 @@ const routes: AppRoutes[] = [ isPrivate: true, key: 'WORKSPACE_LOCKED', }, + { + path: ROUTES.WORKSPACE_SUSPENDED, + exact: true, + component: WorkspaceSuspended, + isPrivate: true, + key: 'WORKSPACE_SUSPENDED', + }, { path: ROUTES.SHORTCUTS, exact: true, diff --git a/frontend/src/api/infraMonitoring/getHostLists.ts b/frontend/src/api/infraMonitoring/getHostLists.ts index ce2ef9b544..870f87f9d6 100644 --- a/frontend/src/api/infraMonitoring/getHostLists.ts +++ b/frontend/src/api/infraMonitoring/getHostLists.ts @@ -48,6 +48,8 @@ export interface HostListResponse { records: HostData[]; groups: null; total: number; + sentAnyHostMetricsData: boolean; + isSendingK8SAgentMetrics: boolean; }; } diff --git a/frontend/src/api/licensesV3/getActive.ts b/frontend/src/api/licensesV3/getActive.ts new file mode 100644 index 0000000000..48dd0a3a43 --- /dev/null +++ b/frontend/src/api/licensesV3/getActive.ts @@ -0,0 +1,18 @@ +import { ApiV3Instance as axios } from 'api'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { LicenseV3EventQueueResModel } from 'types/api/licensesV3/getActive'; + +const getActive = async (): Promise< + SuccessResponse | ErrorResponse +> => { + const response = await axios.get('/licenses/active'); + + return { + statusCode: 200, + error: null, + message: response.data.status, + payload: response.data.data, + }; +}; + +export default getActive; diff --git a/frontend/src/components/HostMetricsDetail/Containers/Containers.styles.scss b/frontend/src/components/HostMetricsDetail/Containers/Containers.styles.scss index 6542d748ea..925ec30f2a 100644 --- a/frontend/src/components/HostMetricsDetail/Containers/Containers.styles.scss +++ b/frontend/src/components/HostMetricsDetail/Containers/Containers.styles.scss @@ -1,7 +1,24 @@ .host-containers { - max-width: 600px; - margin: 150px auto; - padding: 0 16px; + gap: 24px; + height: 60vh; + 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 { display: flex; @@ -17,6 +34,7 @@ width: 400px; font-family: 'Inter'; margin-top: 12px; + font-weight: 300; } .infra-container-working-msg { diff --git a/frontend/src/components/HostMetricsDetail/Containers/Containers.tsx b/frontend/src/components/HostMetricsDetail/Containers/Containers.tsx index e838b4aa42..4dc01ff5e8 100644 --- a/frontend/src/components/HostMetricsDetail/Containers/Containers.tsx +++ b/frontend/src/components/HostMetricsDetail/Containers/Containers.tsx @@ -3,6 +3,8 @@ import './Containers.styles.scss'; import { Space, Typography } from 'antd'; import { useTranslation } from 'react-i18next'; +import WaitlistFragment from '../WaitlistFragment/WaitlistFragment'; + const { Text } = Typography; function Containers(): JSX.Element { @@ -10,24 +12,30 @@ function Containers(): JSX.Element { return ( -
- infra-container +
+
+
+ infra-container - - {t('containers_visualization_message')} - -
+ + {t('containers_visualization_message')} + +
-
- - broom - {t('working_message')} - +
+ + broom + {t('working_message')} + +
+
+ +
); diff --git a/frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx b/frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx index 7a3ab8c0ab..ee61e687cb 100644 --- a/frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx +++ b/frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx @@ -11,6 +11,7 @@ import { Typography, } from 'antd'; import { RadioChangeEvent } from 'antd/lib'; +import logEvent from 'api/common/logEvent'; import { QueryParams } from 'constants/query'; import { initialQueryBuilderFormValuesMap, @@ -118,6 +119,13 @@ function HostMetricsDetails({ initialFilters, ); + useEffect(() => { + logEvent('Infra Monitoring: Hosts list details page visited', { + host: host?.hostName, + }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + useEffect(() => { setLogFilters(initialFilters); setTracesFilters(initialFilters); @@ -143,6 +151,7 @@ function HostMetricsDetails({ const handleTimeChange = useCallback( (interval: Time | CustomTimeType, dateTimeRange?: [number, number]): void => { setSelectedInterval(interval as Time); + if (interval === 'custom' && dateTimeRange) { setModalTimeRange({ startTime: Math.floor(dateTimeRange[0] / 1000), @@ -156,7 +165,13 @@ function HostMetricsDetails({ 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', ); + logEvent('Infra Monitoring: Hosts list details logs filters applied', { + host: host?.hostName, + }); + return { op: 'AND', 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( (item) => item.key?.key === 'host.name', ); + + logEvent('Infra Monitoring: Hosts list details traces filters applied', { + host: host?.hostName, + }); + return { op: 'AND', 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()); } + logEvent('Infra Monitoring: Hosts list details explore clicked', { + host: host?.hostName, + view: selectedView, + }); + if (selectedView === VIEW_TYPES.LOGS) { const filtersWithoutPagination = { ...logFilters, diff --git a/frontend/src/components/HostMetricsDetail/Processes/Processes.styles.scss b/frontend/src/components/HostMetricsDetail/Processes/Processes.styles.scss index e2f182d04d..b1e4fda451 100644 --- a/frontend/src/components/HostMetricsDetail/Processes/Processes.styles.scss +++ b/frontend/src/components/HostMetricsDetail/Processes/Processes.styles.scss @@ -1,7 +1,24 @@ .host-processes { - max-width: 600px; - margin: 150px auto; - padding: 0 16px; + gap: 24px; + height: 60vh; + 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 { display: flex; @@ -17,6 +34,7 @@ width: 400px; font-family: 'Inter'; margin-top: 12px; + font-weight: 300; } .infra-container-working-msg { diff --git a/frontend/src/components/HostMetricsDetail/Processes/Processes.tsx b/frontend/src/components/HostMetricsDetail/Processes/Processes.tsx index cd5baf14ca..19da46a0e2 100644 --- a/frontend/src/components/HostMetricsDetail/Processes/Processes.tsx +++ b/frontend/src/components/HostMetricsDetail/Processes/Processes.tsx @@ -3,6 +3,8 @@ import './Processes.styles.scss'; import { Space, Typography } from 'antd'; import { useTranslation } from 'react-i18next'; +import WaitlistFragment from '../WaitlistFragment/WaitlistFragment'; + const { Text } = Typography; function Processes(): JSX.Element { @@ -10,23 +12,29 @@ function Processes(): JSX.Element { return ( -
- infra-container - - {t('processes_visualization_message')} - -
+
+
+
+ infra-container + + {t('processes_visualization_message')} + +
-
- - broom - {t('working_message')} - +
+ + broom + {t('working_message')} + +
+
+ +
); diff --git a/frontend/src/components/HostMetricsDetail/WaitlistFragment/WaitListFragment.styles.scss b/frontend/src/components/HostMetricsDetail/WaitlistFragment/WaitListFragment.styles.scss new file mode 100644 index 0000000000..3cad900e6b --- /dev/null +++ b/frontend/src/components/HostMetricsDetail/WaitlistFragment/WaitListFragment.styles.scss @@ -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); + } +} diff --git a/frontend/src/components/HostMetricsDetail/WaitlistFragment/WaitlistFragment.tsx b/frontend/src/components/HostMetricsDetail/WaitlistFragment/WaitlistFragment.tsx new file mode 100644 index 0000000000..08c15db2b7 --- /dev/null +++ b/frontend/src/components/HostMetricsDetail/WaitlistFragment/WaitlistFragment.tsx @@ -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((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 ( +
+ + {t('waitlist_message')} + + + +
+ ); +} diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss index 070d440781..72ceaef26a 100644 --- a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss +++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.styles.scss @@ -3,7 +3,7 @@ position: absolute; right: -2px; margin: 6px 0; - width: 160px; + width: 240px; border-radius: 4px; @@ -24,7 +24,7 @@ .back-btn { display: flex; align-items: center; - gap: 6px; + gap: 4px; padding: 12px; border: none !important; box-shadow: none !important; @@ -32,14 +32,16 @@ .icon { flex-shrink: 0; } + .text { - color: var(--bg-vanilla-400); + color: var(--bg-slate-50); font-family: Inter; - font-size: 13px; + font-size: 11px; font-style: normal; - font-weight: 400; + font-weight: 500; line-height: 20px; /* 142.857% */ letter-spacing: 0.14px; + text-transform: uppercase; } } @@ -252,6 +254,75 @@ } } + .add-new-column-container { + display: flex; + flex-direction: column; + + .add-new-column-header { + display: flex; + flex-direction: column; + padding: 8px; + gap: 8px; + + .back-icon { + cursor: pointer; + } + + .title { + display: flex; + gap: 4px; + align-items: center; + color: var(--bg-slate-50); + text-transform: uppercase; + font-size: 11px; + font-weight: 500; + line-height: 18px; + letter-spacing: 0.88px; + } + } + + .add-new-column-content { + display: flex; + flex-direction: column; + + padding-bottom: 16px; + + min-height: 240px; + max-height: 400px; + + .loading-container { + padding: 8px; + } + + .column-format-new-options { + overflow-y: auto; + overflow-x: hidden; + + .column-name { + padding: 4px 8px; + border-radius: 1px; + color: var(--bg-vanilla-400, #c0c1c3); + font-family: Inter; + font-size: 13px; + font-style: normal; + font-weight: 400; + line-height: 20px; /* 142.857% */ + letter-spacing: -0.07px; + + &.selected { + background-color: var(--bg-ink-200); + cursor: pointer; + } + } + + &::-webkit-scrollbar { + height: 1rem; + width: 0.2rem; + } + } + } + } + .selected-item-content-container { .add-new-column-header { padding: 8px; @@ -314,6 +385,22 @@ cursor: pointer; + &.default-column { + color: var(--bg-vanilla-400, #c0c1c3); + cursor: not-allowed; + } + + &.no-columns-selected { + color: var(--bg-slate-100); + font-size: 12px; + cursor: not-allowed; + } + + &.add-new-column-btn { + color: var(--bg-vanilla-400, #c0c1c3); + cursor: pointer; + } + .name { flex: 1; overflow: hidden; @@ -428,6 +515,30 @@ } } + .add-new-column-container { + .add-new-column-header { + .title { + color: var(--bg-ink-100); + } + } + + .add-new-column-content { + .column-format-new-options { + .column-name { + color: var(--bg-ink-400); + + &.selected { + background-color: var(--bg-vanilla-400); + } + } + } + + .loading-container { + color: var(--bg-ink-400); + } + } + } + .font-size-container { .title { color: var(--bg-ink-100); diff --git a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx index 527c77c6af..740ceaf5b7 100644 --- a/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx +++ b/frontend/src/components/LogsFormatOptionsMenu/LogsFormatOptionsMenu.tsx @@ -3,13 +3,14 @@ /* eslint-disable jsx-a11y/click-events-have-key-events */ import './LogsFormatOptionsMenu.styles.scss'; -import { Button, Divider, Input, InputNumber, Tooltip, Typography } from 'antd'; +import { Button, Input, InputNumber, Tooltip, Typography } from 'antd'; +import { DefaultOptionType } from 'antd/es/select'; import cx from 'classnames'; import { LogViewMode } from 'container/LogsTable'; import { FontSize, OptionsMenuConfig } from 'container/OptionsMenu/types'; import useDebouncedFn from 'hooks/useDebouncedFunction'; import { Check, ChevronLeft, ChevronRight, Minus, Plus, X } from 'lucide-react'; -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; interface LogsFormatOptionsMenuProps { title: string; @@ -35,7 +36,13 @@ export default function LogsFormatOptionsMenu({ false, ); - const [addNewColumn, setAddNewColumn] = useState(false); + const [showAddNewColumnContainer, setShowAddNewColumnContainer] = useState( + false, + ); + + const [selectedValue, setSelectedValue] = useState(null); + const listRef = useRef(null); + const initialMouseEnterRef = useRef(false); const onChange = useCallback( (key: LogViewMode) => { @@ -49,7 +56,7 @@ export default function LogsFormatOptionsMenu({ const handleMenuItemClick = (key: LogViewMode): void => { setSelectedItem(key); onChange(key); - setAddNewColumn(false); + setShowAddNewColumnContainer(false); }; const incrementMaxLinesPerRow = (): void => { @@ -75,7 +82,8 @@ export default function LogsFormatOptionsMenu({ }, 300); const handleToggleAddNewColumn = (): void => { - setAddNewColumn(!addNewColumn); + addColumn?.onSearch?.(''); + setShowAddNewColumnContainer(!showAddNewColumnContainer); }; const handleLinesPerRowChange = (maxLinesPerRow: number | null): void => { @@ -100,9 +108,106 @@ export default function LogsFormatOptionsMenu({ } }, [fontSizeValue]); + function handleColumnSelection( + currentIndex: number, + optionsData: DefaultOptionType[], + ): void { + const currentItem = optionsData[currentIndex]; + const itemLength = optionsData.length; + if (addColumn && addColumn?.onSelect) { + addColumn?.onSelect(selectedValue, { + label: currentItem.label, + disabled: false, + }); + + // if the last element is selected then select the previous one + if (currentIndex === itemLength - 1) { + // there should be more than 1 element in the list + if (currentIndex - 1 >= 0) { + const prevValue = optionsData[currentIndex - 1]?.value || null; + setSelectedValue(prevValue as string | null); + } else { + // if there is only one element then just select and do nothing + setSelectedValue(null); + } + } else { + // selecting any random element from the list except the last one + const nextIndex = currentIndex + 1; + + const nextValue = optionsData[nextIndex]?.value || null; + + setSelectedValue(nextValue as string | null); + } + } + } + + const handleKeyDown = (e: KeyboardEvent): void => { + if (!selectedValue) return; + + const optionsData = addColumn?.options || []; + + const currentIndex = optionsData.findIndex( + (item) => item?.value === selectedValue, + ); + + const itemLength = optionsData.length; + + switch (e.key) { + case 'ArrowUp': { + const newValue = optionsData[Math.max(0, currentIndex - 1)]?.value; + + setSelectedValue(newValue as string | null); + e.preventDefault(); + break; + } + case 'ArrowDown': { + const newValue = + optionsData[Math.min(itemLength - 1, currentIndex + 1)]?.value; + + setSelectedValue(newValue as string | null); + e.preventDefault(); + break; + } + case 'Enter': + e.preventDefault(); + handleColumnSelection(currentIndex, optionsData); + break; + default: + break; + } + }; + + useEffect(() => { + // Scroll the selected item into view + const listNode = listRef.current; + if (listNode && selectedValue) { + const optionsData = addColumn?.options || []; + const currentIndex = optionsData.findIndex( + (item) => item?.value === selectedValue, + ); + const itemNode = listNode.children[currentIndex] as HTMLElement; + if (itemNode) { + itemNode.scrollIntoView({ + behavior: 'smooth', + block: 'nearest', + }); + } + } + }, [selectedValue]); + + useEffect(() => { + window.addEventListener('keydown', handleKeyDown); + return (): void => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, [selectedValue]); + return (
{ // this is to restrict click events to propogate to parent event.stopPropagation(); @@ -158,8 +263,72 @@ export default function LogsFormatOptionsMenu({
- ) : ( - <> + ) : null} + + {showAddNewColumnContainer && ( +
+
+
+
+ +
+ Add New Column +
+ + +
+ +
+ {addColumn?.isFetching && ( +
Loading ...
+ )} + +
+ {addColumn?.options?.map(({ label, value }, index) => ( +
{ + if (!initialMouseEnterRef.current) { + setSelectedValue(value as string | null); + } + + initialMouseEnterRef.current = true; + }} + onMouseMove={(): void => { + // this is added to handle the mouse move explicit event and not the re-rendered on mouse enter event + setSelectedValue(value as string | null); + }} + onClick={(eve): void => { + eve.stopPropagation(); + handleColumnSelection(index, addColumn?.options || []); + }} + > +
+ + {label} + +
+
+ ))} +
+
+
+ )} + + {!isFontSizeOptionsOpen && !showAddNewColumnContainer && ( +
Font Size
)}
); diff --git a/frontend/src/components/QuickFilters/FilterRenderers/Checkbox/Checkbox.styles.scss b/frontend/src/components/QuickFilters/FilterRenderers/Checkbox/Checkbox.styles.scss index c46d9975f4..34bdd0508e 100644 --- a/frontend/src/components/QuickFilters/FilterRenderers/Checkbox/Checkbox.styles.scss +++ b/frontend/src/components/QuickFilters/FilterRenderers/Checkbox/Checkbox.styles.scss @@ -8,6 +8,7 @@ display: flex; align-items: center; justify-content: space-between; + cursor: pointer; .left-action { display: flex; diff --git a/frontend/src/components/QuickFilters/FilterRenderers/Checkbox/Checkbox.tsx b/frontend/src/components/QuickFilters/FilterRenderers/Checkbox/Checkbox.tsx index dcf3cc8f3e..bd8b9b1d38 100644 --- a/frontend/src/components/QuickFilters/FilterRenderers/Checkbox/Checkbox.tsx +++ b/frontend/src/components/QuickFilters/FilterRenderers/Checkbox/Checkbox.tsx @@ -396,23 +396,22 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element { return (
-
+
{ + if (isOpen) { + setIsOpen(false); + setVisibleItemsCount(10); + } else { + setIsOpen(true); + } + }} + >
{isOpen ? ( - { - setIsOpen(false); - setVisibleItemsCount(10); - }} - /> + ) : ( - setIsOpen(true)} - cursor="pointer" - /> + )} {filter.title}
@@ -420,7 +419,11 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element { {isOpen && ( { + e.stopPropagation(); + e.preventDefault(); + handleClearFilterAttribute(); + }} > Clear All diff --git a/frontend/src/constants/features.ts b/frontend/src/constants/features.ts index 12de61c0d5..9a2550ec0b 100644 --- a/frontend/src/constants/features.ts +++ b/frontend/src/constants/features.ts @@ -23,5 +23,4 @@ export enum FeatureKeys { PREMIUM_SUPPORT = 'PREMIUM_SUPPORT', QUERY_BUILDER_SEARCH_V2 = 'QUERY_BUILDER_SEARCH_V2', ANOMALY_DETECTION = 'ANOMALY_DETECTION', - HOSTS_INFRA_MONITORING = 'HOSTS_INFRA_MONITORING', } diff --git a/frontend/src/constants/reactQueryKeys.ts b/frontend/src/constants/reactQueryKeys.ts index d25c180eaa..36cf99157e 100644 --- a/frontend/src/constants/reactQueryKeys.ts +++ b/frontend/src/constants/reactQueryKeys.ts @@ -20,4 +20,5 @@ export const REACT_QUERY_KEY = { DUPLICATE_ALERT_RULE: 'DUPLICATE_ALERT_RULE', GET_HOST_LIST: 'GET_HOST_LIST', UPDATE_ALERT_RULE: 'UPDATE_ALERT_RULE', + GET_ACTIVE_LICENSE_V3: 'GET_ACTIVE_LICENSE_V3', }; diff --git a/frontend/src/constants/routes.ts b/frontend/src/constants/routes.ts index 0760c57074..7b2911dbd6 100644 --- a/frontend/src/constants/routes.ts +++ b/frontend/src/constants/routes.ts @@ -55,6 +55,7 @@ const ROUTES = { LOGS_SAVE_VIEWS: '/logs/saved-views', TRACES_SAVE_VIEWS: '/traces/saved-views', WORKSPACE_LOCKED: '/workspace-locked', + WORKSPACE_SUSPENDED: '/workspace-suspended', SHORTCUTS: '/shortcuts', INTEGRATIONS: '/integrations', MESSAGING_QUEUES: '/messaging-queues', diff --git a/frontend/src/container/AppLayout/AppLayout.styles.scss b/frontend/src/container/AppLayout/AppLayout.styles.scss index 98ca9084f2..6bd06caded 100644 --- a/frontend/src/container/AppLayout/AppLayout.styles.scss +++ b/frontend/src/container/AppLayout/AppLayout.styles.scss @@ -108,6 +108,13 @@ text-align: center; } +.payment-failed-banner { + padding: 8px; + background-color: var(--bg-sakura-500); + color: white; + text-align: center; +} + .upgrade-link { padding: 0px; padding-right: 4px; diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 4d9e68f98a..264213b180 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -5,25 +5,30 @@ import './AppLayout.styles.scss'; import * as Sentry from '@sentry/react'; import { Flex } from 'antd'; +import manageCreditCardApi from 'api/billing/manage'; import getUserLatestVersion from 'api/user/getLatestVersion'; import getUserVersion from 'api/user/getVersion'; import cx from 'classnames'; import ChatSupportGateway from 'components/ChatSupportGateway/ChatSupportGateway'; import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar'; +import { SOMETHING_WENT_WRONG } from 'constants/api'; import { FeatureKeys } from 'constants/features'; import ROUTES from 'constants/routes'; import SideNav from 'container/SideNav'; import TopNav from 'container/TopNav'; +import dayjs from 'dayjs'; import { useIsDarkMode } from 'hooks/useDarkMode'; import useFeatureFlags from 'hooks/useFeatureFlag'; import useLicense from 'hooks/useLicense'; import { useNotifications } from 'hooks/useNotifications'; import history from 'lib/history'; +import { isNull } from 'lodash-es'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; +import { useAppContext } from 'providers/App/App'; import { ReactNode, useEffect, useMemo, useRef, useState } from 'react'; import { Helmet } from 'react-helmet-async'; import { useTranslation } from 'react-i18next'; -import { useQueries } from 'react-query'; +import { useMutation, useQueries } from 'react-query'; import { useDispatch, useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; import { Dispatch } from 'redux'; @@ -35,9 +40,16 @@ import { UPDATE_LATEST_VERSION, UPDATE_LATEST_VERSION_ERROR, } from 'types/actions/app'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { CheckoutSuccessPayloadProps } from 'types/api/billing/checkout'; +import { LicenseEvent } from 'types/api/licensesV3/getActive'; import AppReducer from 'types/reducer/app'; import { isCloudUser } from 'utils/app'; -import { getFormattedDate, getRemainingDays } from 'utils/timeUtils'; +import { + getFormattedDate, + getFormattedDateWithMinutes, + getRemainingDays, +} from 'utils/timeUtils'; import { ChildrenContainer, Layout, LayoutContent } from './styles'; import { getRouteKey } from './utils'; @@ -48,8 +60,42 @@ function AppLayout(props: AppLayoutProps): JSX.Element { (state) => state.app, ); + const { activeLicenseV3, isFetchingActiveLicenseV3 } = useAppContext(); const { notifications } = useNotifications(); + const [ + showPaymentFailedWarning, + setShowPaymentFailedWarning, + ] = useState(false); + + const handleBillingOnSuccess = ( + data: ErrorResponse | SuccessResponse, + ): void => { + if (data?.payload?.redirectURL) { + const newTab = document.createElement('a'); + newTab.href = data.payload.redirectURL; + newTab.target = '_blank'; + newTab.rel = 'noopener noreferrer'; + newTab.click(); + } + }; + + const handleBillingOnError = (): void => { + notifications.error({ + message: SOMETHING_WENT_WRONG, + }); + }; + + const { + mutate: manageCreditCard, + isLoading: isLoadingManageBilling, + } = useMutation(manageCreditCardApi, { + onSuccess: (data) => { + handleBillingOnSuccess(data); + }, + onError: handleBillingOnError, + }); + const isDarkMode = useIsDarkMode(); const { data: licenseData, isFetching } = useLicense(); @@ -212,6 +258,16 @@ function AppLayout(props: AppLayoutProps): JSX.Element { } }, [licenseData, isFetching]); + useEffect(() => { + if ( + !isFetchingActiveLicenseV3 && + !isNull(activeLicenseV3) && + activeLicenseV3?.event_queue?.event === LicenseEvent.FAILED_PAYMENT + ) { + setShowPaymentFailedWarning(true); + } + }, [activeLicenseV3, isFetchingActiveLicenseV3]); + useEffect(() => { // after logging out hide the trial expiry banner if (!isLoggedIn) { @@ -225,6 +281,14 @@ function AppLayout(props: AppLayoutProps): JSX.Element { } }; + const handleFailedPayment = (): void => { + manageCreditCard({ + licenseKey: activeLicenseV3?.key || '', + successURL: window.location.href, + cancelURL: window.location.href, + }); + }; + const isLogsView = (): boolean => routeKey === 'LOGS' || routeKey === 'LOGS_EXPLORER' || @@ -269,7 +333,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element { {pageTitle} - {showTrialExpiryBanner && ( + {showTrialExpiryBanner && !showPaymentFailedWarning && (
You are in free trial period. Your free trial will end on{' '} @@ -289,6 +353,36 @@ function AppLayout(props: AppLayoutProps): JSX.Element { )}
)} + {!showTrialExpiryBanner && showPaymentFailedWarning && ( +
+ Your bill payment has failed. Your workspace will get suspended on{' '} + + {getFormattedDateWithMinutes( + dayjs(activeLicenseV3?.event_queue?.scheduled_at).unix() || Date.now(), + )} + . + + {role === 'ADMIN' ? ( + + {' '} + Please{' '} + { + if (!isLoadingManageBilling) { + handleFailedPayment(); + } + }} + > + pay the bill + + to continue using SigNoz features. + + ) : ( + ' Please contact your administrator to pay the bill.' + )} +
+ )} {isToDisplayLayout && !renderFullScreen && ( diff --git a/frontend/src/container/InfraMonitoringHosts/HostsEmptyOrIncorrectMetrics.tsx b/frontend/src/container/InfraMonitoringHosts/HostsEmptyOrIncorrectMetrics.tsx new file mode 100644 index 0000000000..64a0550126 --- /dev/null +++ b/frontend/src/container/InfraMonitoringHosts/HostsEmptyOrIncorrectMetrics.tsx @@ -0,0 +1,52 @@ +import { Typography } from 'antd'; + +export default function HostsEmptyOrIncorrectMetrics({ + noData, + incorrectData, +}: { + noData: boolean; + incorrectData: boolean; +}): JSX.Element { + return ( +
+
+ eyes emoji + + {noData && ( +
+ + No host metrics data received yet. + + + + Infrastructure monitoring requires the{' '} + + OpenTelemetry system metrics + + . Please refer to{' '} + + this + {' '} + to learn how to send host metrics to SigNoz. + +
+ )} + + {incorrectData && ( + + To see host metrics, upgrade to the latest version of SigNoz k8s-infra + chart. Please contact support if you need help. + + )} +
+
+ ); +} diff --git a/frontend/src/container/InfraMonitoringHosts/HostsList.tsx b/frontend/src/container/InfraMonitoringHosts/HostsList.tsx index e7ae5912b2..2dc9b26662 100644 --- a/frontend/src/container/InfraMonitoringHosts/HostsList.tsx +++ b/frontend/src/container/InfraMonitoringHosts/HostsList.tsx @@ -2,6 +2,7 @@ import './InfraMonitoring.styles.scss'; import { LoadingOutlined } from '@ant-design/icons'; import { + Skeleton, Spin, Table, TablePaginationConfig, @@ -9,17 +10,17 @@ import { Typography, } from 'antd'; import { SorterResult } from 'antd/es/table/interface'; +import logEvent from 'api/common/logEvent'; import { HostListPayload } from 'api/infraMonitoring/getHostLists'; import HostMetricDetail from 'components/HostMetricsDetail'; -import NoLogs from 'container/NoLogs/NoLogs'; 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 { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; -import { DataSource } from 'types/common/queryBuilder'; import { GlobalReducer } from 'types/reducer/globalTime'; +import HostsEmptyOrIncorrectMetrics from './HostsEmptyOrIncorrectMetrics'; import HostsListControls from './HostsListControls'; import { formatDataForTable, @@ -28,6 +29,7 @@ import { HostRowData, } from './utils'; +// eslint-disable-next-line sonarjs/cognitive-complexity function HostsList(): JSX.Element { const { maxTime, minTime } = useSelector( (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 || [], [ data, ]); @@ -81,9 +93,6 @@ function HostsList(): JSX.Element { const columns = useMemo(() => getHostsListColumns(), []); - const isDataPresent = - !isLoading && !isFetching && !isError && hostMetricsData.length === 0; - const handleTableChange: TableProps['onChange'] = useCallback( ( pagination: TablePaginationConfig, @@ -112,11 +121,19 @@ function HostsList(): JSX.Element { if (isNewFilterAdded) { setFilters(value); setCurrentPage(1); + + logEvent('Infra Monitoring: Hosts list filters applied', { + filters: value, + }); } }, [filters], ); + useEffect(() => { + logEvent('Infra Monitoring: Hosts list page visited', {}); + }, []); + const selectedHostData = useMemo(() => { if (!selectedHostName) return null; return ( @@ -126,29 +143,85 @@ function HostsList(): JSX.Element { const handleRowClick = (record: HostRowData): void => { setSelectedHostName(record.hostName); + + logEvent('Infra Monitoring: Hosts list item clicked', { + host: record.hostName, + }); }; const handleCloseHostDetail = (): void => { 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 (
{isError && {data?.error || 'Something went wrong'}} - {isDataPresent && filters.items.length === 0 && ( - + {showHostsEmptyState && ( + )} - {!isFetching && - !isLoading && - formattedHostMetricsData.length === 0 && - filters.items.length > 0 && ( -
No hosts match the applied filters.
- )} + {showNoFilteredHostsMessage && ( +
+
+ thinking-emoji - {!isError && ( + + This query had no results. Edit your query and try again! + +
+
+ )} + + {(isFetching || isLoading) && ( +
+ + + +
+ )} + + {showHostsTable && ( tr > th { diff --git a/frontend/src/container/SideNav/NavItem/NavItem.tsx b/frontend/src/container/SideNav/NavItem/NavItem.tsx index 97a8cdb0ed..012708cc30 100644 --- a/frontend/src/container/SideNav/NavItem/NavItem.tsx +++ b/frontend/src/container/SideNav/NavItem/NavItem.tsx @@ -16,7 +16,7 @@ export default function NavItem({ isActive: boolean; onClick: (event: React.MouseEvent) => void; }): JSX.Element { - const { label, icon, isBeta } = item; + const { label, icon, isBeta, isNew } = item; return (
)} + + {isNew && ( +
+ + New + +
+ )} ); diff --git a/frontend/src/container/SideNav/SideNav.styles.scss b/frontend/src/container/SideNav/SideNav.styles.scss index 1a148e2469..21263ed063 100644 --- a/frontend/src/container/SideNav/SideNav.styles.scss +++ b/frontend/src/container/SideNav/SideNav.styles.scss @@ -184,10 +184,16 @@ display: none; } - .nav-item-beta { + .nav-item-beta, + .nav-item-new { display: none; } + .sidenav-new-tag { + background-color: rgba(37, 225, 146, 0.1); + color: var(--text-forest-500); + } + &:hover { flex: 0 0 240px; max-width: 240px; @@ -221,7 +227,8 @@ display: block; } - .nav-item-beta { + .nav-item-beta, + .nav-item-new { display: block; } } diff --git a/frontend/src/container/SideNav/SideNav.tsx b/frontend/src/container/SideNav/SideNav.tsx index ee6bbe45d1..f3bdbe0870 100644 --- a/frontend/src/container/SideNav/SideNav.tsx +++ b/frontend/src/container/SideNav/SideNav.tsx @@ -120,21 +120,6 @@ function SideNav({ setMenuItems(items); } - const isInfraMonitoringEnabled = - featureResponse.data?.find( - (feature) => feature.name === FeatureKeys.HOSTS_INFRA_MONITORING, - )?.active || false; - - if (!isInfraMonitoringEnabled) { - let items = [...menuItems]; - - items = items.filter( - (item) => item.key !== ROUTES.INFRASTRUCTURE_MONITORING_HOSTS, - ); - - setMenuItems(items); - } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [featureResponse.data]); diff --git a/frontend/src/container/SideNav/menuItems.tsx b/frontend/src/container/SideNav/menuItems.tsx index 8b3706e563..0c7230cb50 100644 --- a/frontend/src/container/SideNav/menuItems.tsx +++ b/frontend/src/container/SideNav/menuItems.tsx @@ -3,6 +3,7 @@ import ROUTES from 'constants/routes'; import { BarChart2, BellDot, + Boxes, BugIcon, Cloudy, DraftingCompass, @@ -11,7 +12,6 @@ import { LayoutGrid, ListMinus, MessageSquare, - PackagePlus, Receipt, Route, ScrollText, @@ -82,6 +82,12 @@ const menuItems: SidebarItem[] = [ label: 'Logs', icon: , }, + { + key: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS, + label: 'Infra Monitoring', + icon: , + isNew: true, + }, { key: ROUTES.ALL_DASHBOARD, label: 'Dashboards', @@ -91,7 +97,6 @@ const menuItems: SidebarItem[] = [ key: ROUTES.MESSAGING_QUEUES, label: 'Messaging Queues', icon: , - isBeta: true, }, { key: ROUTES.LIST_ALL_ALERT, @@ -119,12 +124,6 @@ const menuItems: SidebarItem[] = [ label: 'Billing', icon: , }, - { - key: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS, - label: 'Infra Monitoring', - icon: , - isBeta: true, - }, { key: ROUTES.SETTINGS, label: 'Settings', diff --git a/frontend/src/container/SideNav/sideNav.types.ts b/frontend/src/container/SideNav/sideNav.types.ts index ab9e991702..af2877171f 100644 --- a/frontend/src/container/SideNav/sideNav.types.ts +++ b/frontend/src/container/SideNav/sideNav.types.ts @@ -13,6 +13,7 @@ export interface SidebarItem { key: string | number; label?: ReactNode; isBeta?: boolean; + isNew?: boolean; } export enum SecondaryMenuItemKey { diff --git a/frontend/src/container/TopNav/Breadcrumbs/index.tsx b/frontend/src/container/TopNav/Breadcrumbs/index.tsx index 9efd50d2c3..92f9bd37bb 100644 --- a/frontend/src/container/TopNav/Breadcrumbs/index.tsx +++ b/frontend/src/container/TopNav/Breadcrumbs/index.tsx @@ -27,6 +27,7 @@ const breadcrumbNameMap: Record = { [ROUTES.BILLING]: 'Billing', [ROUTES.SUPPORT]: 'Support', [ROUTES.WORKSPACE_LOCKED]: 'Workspace Locked', + [ROUTES.WORKSPACE_SUSPENDED]: 'Workspace Suspended', [ROUTES.MESSAGING_QUEUES]: 'Messaging Queues', }; diff --git a/frontend/src/container/TopNav/DateTimeSelection/config.ts b/frontend/src/container/TopNav/DateTimeSelection/config.ts index b46c60bab0..0b8aa90bff 100644 --- a/frontend/src/container/TopNav/DateTimeSelection/config.ts +++ b/frontend/src/container/TopNav/DateTimeSelection/config.ts @@ -122,6 +122,7 @@ export const routesToSkip = [ ROUTES.BILLING, ROUTES.SUPPORT, ROUTES.WORKSPACE_LOCKED, + ROUTES.WORKSPACE_SUSPENDED, ROUTES.LOGS, ROUTES.MY_SETTINGS, ROUTES.LIST_LICENSES, diff --git a/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts b/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts index 7624cda283..408ed6c11e 100644 --- a/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts +++ b/frontend/src/container/TopNav/DateTimeSelectionV2/config.ts @@ -196,6 +196,7 @@ export const routesToSkip = [ ROUTES.BILLING, ROUTES.SUPPORT, ROUTES.WORKSPACE_LOCKED, + ROUTES.WORKSPACE_SUSPENDED, ROUTES.LOGS, ROUTES.MY_SETTINGS, ROUTES.LIST_LICENSES, diff --git a/frontend/src/hooks/useActiveLicenseV3/useActiveLicenseV3.tsx b/frontend/src/hooks/useActiveLicenseV3/useActiveLicenseV3.tsx new file mode 100644 index 0000000000..72fb4aa1b6 --- /dev/null +++ b/frontend/src/hooks/useActiveLicenseV3/useActiveLicenseV3.tsx @@ -0,0 +1,25 @@ +import getActive from 'api/licensesV3/getActive'; +import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; +import { useQuery, UseQueryResult } from 'react-query'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { ErrorResponse, SuccessResponse } from 'types/api'; +import { LicenseV3ResModel } from 'types/api/licensesV3/getActive'; +import AppReducer from 'types/reducer/app'; + +const useActiveLicenseV3 = (): UseLicense => { + const { user } = useSelector((state) => state.app); + + return useQuery({ + queryFn: getActive, + queryKey: [REACT_QUERY_KEY.GET_ACTIVE_LICENSE_V3, user?.email], + enabled: !!user?.email, + }); +}; + +type UseLicense = UseQueryResult< + SuccessResponse | ErrorResponse, + unknown +>; + +export default useActiveLicenseV3; diff --git a/frontend/src/pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil.ts b/frontend/src/pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil.ts index 144b573c5f..e52b4e2d03 100644 --- a/frontend/src/pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil.ts +++ b/frontend/src/pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil.ts @@ -517,7 +517,7 @@ export const consumerOffsetWidgetData = getWidgetQueryBuilder( timeAggregation: 'avg', }, ], - title: 'Consumer Offest', + title: 'Consumer Offset', description: 'Current offset of each consumer group for each topic partition', }), ); diff --git a/frontend/src/pages/WorkspaceSuspended/WorkspaceSuspended.styles.scss b/frontend/src/pages/WorkspaceSuspended/WorkspaceSuspended.styles.scss new file mode 100644 index 0000000000..49cba71f0b --- /dev/null +++ b/frontend/src/pages/WorkspaceSuspended/WorkspaceSuspended.styles.scss @@ -0,0 +1,162 @@ +$light-theme: 'lightMode'; +$dark-theme: 'darkMode'; + +@keyframes gradientFlow { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } +} + +.workspace-suspended { + &__modal { + .ant-modal-mask { + backdrop-filter: blur(2px); + } + } + + &__tabs { + margin-top: 148px; + + .ant-tabs { + &-nav { + &::before { + border-color: var(--bg-slate-500); + + .#{$light-theme} & { + border-color: var(--bg-vanilla-300); + } + } + } + &-nav-wrap { + justify-content: center; + } + } + } + + &__modal { + &__header { + display: flex; + justify-content: space-between; + align-items: center; + + &__actions { + display: flex; + align-items: center; + gap: 16px; + } + } + .ant-modal-content { + border-radius: 4px; + border: 1px solid var(--bg-slate-400); + background: linear-gradient( + 139deg, + rgba(18, 19, 23, 0.8) 0%, + rgba(18, 19, 23, 0.9) 98.68% + ); + box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2); + backdrop-filter: blur(20px); + + .#{$light-theme} & { + border: 1px solid var(--bg-vanilla-300); + background: var(--bg-vanilla-100); + } + } + + .ant-modal-header { + background: transparent; + } + + .ant-list { + &-item { + border-color: var(--bg-slate-500); + + .#{$light-theme} & { + border-color: var(--bg-vanilla-300); + } + + &-meta { + align-items: center !important; + + &-title { + margin-bottom: 0 !important; + } + + &-avatar { + display: flex; + } + } + } + } + &__title { + font-weight: 400; + color: var(--text-vanilla-400); + + .#{$light-theme} & { + color: var(--text-ink-200); + } + } + &__cta { + margin-top: 54px; + } + } + &__container { + padding-top: 64px; + } + &__details { + width: 80%; + margin: 0 auto; + color: var(--text-vanilla-400, #c0c1c3); + text-align: center; + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; /* 150% */ + + .#{$light-theme} & { + color: var(--text-ink-200); + } + + &__highlight { + color: var(--text-vanilla-100, #fff); + font-style: normal; + font-weight: 700; + line-height: 24px; + + .#{$light-theme} & { + color: var(--text-ink-100); + } + } + } + &__title { + background: linear-gradient( + 99deg, + #ead8fd 0%, + #7a97fa 33%, + #fd5ab2 66%, + #ead8fd 100% + ); + background-size: 300% 300%; + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + animation: gradientFlow 24s ease infinite; + margin-bottom: 18px; + } + + &__creative { + display: flex; + justify-content: center; + align-items: center; + margin-top: 54px; + + img { + width: -webkit-fill-available; + } + } +} diff --git a/frontend/src/pages/WorkspaceSuspended/WorkspaceSuspended.tsx b/frontend/src/pages/WorkspaceSuspended/WorkspaceSuspended.tsx new file mode 100644 index 0000000000..073ef35636 --- /dev/null +++ b/frontend/src/pages/WorkspaceSuspended/WorkspaceSuspended.tsx @@ -0,0 +1,179 @@ +import './WorkspaceSuspended.styles.scss'; + +import { + Alert, + Button, + Col, + Modal, + Row, + Skeleton, + Space, + Typography, +} from 'antd'; +import manageCreditCardApi from 'api/billing/manage'; +import ROUTES from 'constants/routes'; +import dayjs from 'dayjs'; +import { useNotifications } from 'hooks/useNotifications'; +import history from 'lib/history'; +import { useAppContext } from 'providers/App/App'; +import { useCallback, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from 'react-query'; +import { useSelector } from 'react-redux'; +import { AppState } from 'store/reducers'; +import { LicenseState, LicenseStatus } from 'types/api/licensesV3/getActive'; +import AppReducer from 'types/reducer/app'; +import { getFormattedDateWithMinutes } from 'utils/timeUtils'; + +function WorkspaceSuspended(): JSX.Element { + const { role } = useSelector((state) => state.app); + const isAdmin = role === 'ADMIN'; + const { notifications } = useNotifications(); + const { activeLicenseV3, isFetchingActiveLicenseV3 } = useAppContext(); + + const { t } = useTranslation(['failedPayment']); + + const { mutate: manageCreditCard, isLoading } = useMutation( + manageCreditCardApi, + { + onSuccess: (data) => { + if (data.payload?.redirectURL) { + const newTab = document.createElement('a'); + newTab.href = data.payload.redirectURL; + newTab.target = '_blank'; + newTab.rel = 'noopener noreferrer'; + newTab.click(); + } + }, + onError: () => + notifications.error({ + message: t('somethingWentWrong'), + }), + }, + ); + + const handleUpdateCreditCard = useCallback(async () => { + manageCreditCard({ + licenseKey: activeLicenseV3?.key || '', + successURL: window.location.origin, + cancelURL: window.location.origin, + }); + }, [activeLicenseV3?.key, manageCreditCard]); + + useEffect(() => { + if (!isFetchingActiveLicenseV3 && activeLicenseV3) { + const shouldSuspendWorkspace = + activeLicenseV3.status === LicenseStatus.SUSPENDED && + activeLicenseV3.state === LicenseState.PAYMENT_FAILED; + + if (!shouldSuspendWorkspace) { + history.push(ROUTES.APPLICATION); + } + } + }, [isFetchingActiveLicenseV3, activeLicenseV3]); + return ( +
+ + + {t('workspaceSuspended')} + + + + Got Questions? + + + +
+ } + open + closable={false} + footer={null} + width="65%" + > +
+ {isFetchingActiveLicenseV3 || !activeLicenseV3 ? ( + + ) : ( + <> + +
+ + +
{t('actionHeader')}
+
+ + {t('actionDescription')} +
+ {t('yourDataIsSafe')}{' '} + + {getFormattedDateWithMinutes( + dayjs(activeLicenseV3?.event_queue?.scheduled_at).unix() || + Date.now(), + )} + {' '} + {t('actNow')} +
+
+ + + {!isAdmin && ( + + + + + + )} + {isAdmin && ( + + + + + + )} +
+ correlation-graphic +
+ + )} + + + + ); +} + +export default WorkspaceSuspended; diff --git a/frontend/src/providers/App/App.tsx b/frontend/src/providers/App/App.tsx new file mode 100644 index 0000000000..38110140d7 --- /dev/null +++ b/frontend/src/providers/App/App.tsx @@ -0,0 +1,48 @@ +import useActiveLicenseV3 from 'hooks/useActiveLicenseV3/useActiveLicenseV3'; +import { defaultTo } from 'lodash-es'; +import { + createContext, + PropsWithChildren, + useContext, + useEffect, + useMemo, + useState, +} from 'react'; +import { LicenseV3ResModel } from 'types/api/licensesV3/getActive'; + +interface IAppContext { + activeLicenseV3: LicenseV3ResModel | null; + isFetchingActiveLicenseV3: boolean; +} + +const AppContext = createContext(undefined); + +export function AppProvider({ children }: PropsWithChildren): JSX.Element { + const [activeLicenseV3, setActiveLicenseV3] = useState(); + + const { data, isFetching } = useActiveLicenseV3(); + + useEffect(() => { + if (!isFetching && data?.payload) { + setActiveLicenseV3(data.payload); + } + }, [data, isFetching]); + + const value: IAppContext = useMemo( + () => ({ + activeLicenseV3: defaultTo(activeLicenseV3, null), + isFetchingActiveLicenseV3: isFetching, + }), + [activeLicenseV3, isFetching], + ); + + return {children}; +} + +export const useAppContext = (): IAppContext => { + const context = useContext(AppContext); + if (context === undefined) { + throw new Error('useAppContext must be used within an AppProvider'); + } + return context; +}; diff --git a/frontend/src/types/api/licensesV3/getActive.ts b/frontend/src/types/api/licensesV3/getActive.ts new file mode 100644 index 0000000000..3590b0e40c --- /dev/null +++ b/frontend/src/types/api/licensesV3/getActive.ts @@ -0,0 +1,26 @@ +export enum LicenseEvent { + FAILED_PAYMENT = 'FAILED_PAYMENT', +} + +export enum LicenseStatus { + SUSPENDED = 'SUSPENDED', +} + +export enum LicenseState { + PAYMENT_FAILED = 'PAYMENT_FAILED', +} + +export type LicenseV3EventQueueResModel = { + event: LicenseEvent; + status: string; + scheduled_at: string; + created_at: string; + updated_at: string; +}; + +export type LicenseV3ResModel = { + key: string; + status: LicenseStatus; + state: LicenseState; + event_queue: LicenseV3EventQueueResModel; +}; diff --git a/frontend/src/utils/permission/index.ts b/frontend/src/utils/permission/index.ts index 3d260e1351..6728a4599c 100644 --- a/frontend/src/utils/permission/index.ts +++ b/frontend/src/utils/permission/index.ts @@ -93,6 +93,7 @@ export const routePermission: Record = { GET_STARTED_AWS_MONITORING: ['ADMIN', 'EDITOR', 'VIEWER'], GET_STARTED_AZURE_MONITORING: ['ADMIN', 'EDITOR', 'VIEWER'], WORKSPACE_LOCKED: ['ADMIN', 'EDITOR', 'VIEWER'], + WORKSPACE_SUSPENDED: ['ADMIN', 'EDITOR', 'VIEWER'], BILLING: ['ADMIN', 'EDITOR', 'VIEWER'], SUPPORT: ['ADMIN', 'EDITOR', 'VIEWER'], SOMETHING_WENT_WRONG: ['ADMIN', 'EDITOR', 'VIEWER'], diff --git a/frontend/src/utils/timeUtils.ts b/frontend/src/utils/timeUtils.ts index 5eb795bf45..67e668dc3c 100644 --- a/frontend/src/utils/timeUtils.ts +++ b/frontend/src/utils/timeUtils.ts @@ -19,6 +19,14 @@ export const getFormattedDate = (epochTimestamp: number): string => { return date.format('DD MMM YYYY'); }; +export const getFormattedDateWithMinutes = (epochTimestamp: number): string => { + // Convert epoch timestamp to a date + const date = dayjs.unix(epochTimestamp); + + // Format the date as "18 Nov 2013" + return date.format('DD MMM YYYY HH:mm'); +}; + export const getRemainingDays = (billingEndDate: number): number => { // Convert Epoch timestamps to Date objects const startDate = new Date(); // Convert seconds to milliseconds diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 34fb7fbb57..8f1214a21a 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2979,7 +2979,7 @@ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.14": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -3499,105 +3499,110 @@ resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.20.0.tgz#03554155b45d8b529adf635b2f6ad1165d70d8b4" integrity sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg== -"@sentry-internal/feedback@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-7.102.1.tgz#747f88c2881c76fddd16bce57cc4bc17b4c2af93" - integrity sha512-vY4hpLLMNLjICtWiizc7KeGbWOTUMGrF7C+9dPCztZww3CLgzWy9A7DvPj5hodRiYzpdRnAMl8yQnMFbYXh7bA== +"@sentry-internal/browser-utils@8.41.0": + version "8.41.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/browser-utils/-/browser-utils-8.41.0.tgz#9dc30a8c88aa6e1e542e5acae29ceabd1b377cc4" + integrity sha512-nU7Bn3jEUmf1QXRUT3j2ewUBlFJpe9vnAnjqpeVPDWTsVI52BwVNcJHuE37PrGs66OZ1ZkGMfKnQk43oCAa+oQ== dependencies: - "@sentry/core" "7.102.1" - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" + "@sentry/core" "8.41.0" + "@sentry/types" "8.41.0" -"@sentry-internal/replay-canvas@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-7.102.1.tgz#f098814ce21fdf95ef6d440d7ff8a6d3bfe73054" - integrity sha512-GUX4RWI10uRjdjeyvCLtAAhWRVqnAnG6+yNxWfqUQ3qMA7B7XxG43KT2UhSnulmErNzODQ6hA68rGPwwYeRIww== +"@sentry-internal/feedback@8.41.0": + version "8.41.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/feedback/-/feedback-8.41.0.tgz#9c3c95e6f7738a0d00fcb89061c284baef313ba2" + integrity sha512-bw+BrSNw8abOnu/IpD8YSbYubXkkT8jyNS7TM4e4UPZMuXcbtia7/r5d7kAiUfKv/sV5PNMlZLOk+EYJeLTANg== dependencies: - "@sentry/core" "7.102.1" - "@sentry/replay" "7.102.1" - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" + "@sentry/core" "8.41.0" + "@sentry/types" "8.41.0" -"@sentry-internal/tracing@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.102.1.tgz#5c39c8f04a4a1a665fb6d368e1cd13605152f18b" - integrity sha512-RkFlFyAC0fQOvBbBqnq0CLmFW5m3JJz9pKbZd5vXPraWAlniKSb1bC/4DF9SlNx0FN1LWG+IU3ISdpzwwTeAGg== +"@sentry-internal/replay-canvas@8.41.0": + version "8.41.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay-canvas/-/replay-canvas-8.41.0.tgz#9da984adc54fcd8ebe07cbbc13132fa78396dd01" + integrity sha512-lpgOBHWr1ZNxidD72A2pfoUMjIpwonOPYoQZWAHr86Oa3eIVQOyfklZlHW+gKPFl2/IEl9Lbtcke0JiDp3dkIQ== dependencies: - "@sentry/core" "7.102.1" - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" + "@sentry-internal/replay" "8.41.0" + "@sentry/core" "8.41.0" + "@sentry/types" "8.41.0" -"@sentry/babel-plugin-component-annotate@2.16.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.16.0.tgz#c831713b85516fb3f9da2985836ddf444dc634e6" - integrity sha512-+uy1qPkA5MSNgJ0L9ur/vNTydfdHwHnBX2RQ+0thsvkqf90fU788YjkkXwUiBBNuqNyI69JiOW6frixAWy7oUg== - -"@sentry/browser@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-7.102.1.tgz#30d3da587b2b6542b3d9e39d923ed28a2704d454" - integrity sha512-7BOfPBiM7Kp6q/iy0JIbsBTxIASV+zWXByqqjuEMWGj3X2u4oRIfm3gv4erPU/l+CORQUVQZLSPGoIoM1gbB/A== +"@sentry-internal/replay@8.41.0": + version "8.41.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/replay/-/replay-8.41.0.tgz#b1112a52a0cf1727589ad4d42a8fac9f98f6d733" + integrity sha512-ByXEY7JI95y4Qr9fS3d28l9uuVU5Qa0HgL+xDmYElNx7CXz3Q9hFN6ibgUeC3h8BO5pDULxWNgAppl7FRY8N5w== dependencies: - "@sentry-internal/feedback" "7.102.1" - "@sentry-internal/replay-canvas" "7.102.1" - "@sentry-internal/tracing" "7.102.1" - "@sentry/core" "7.102.1" - "@sentry/replay" "7.102.1" - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" + "@sentry-internal/browser-utils" "8.41.0" + "@sentry/core" "8.41.0" + "@sentry/types" "8.41.0" -"@sentry/bundler-plugin-core@2.16.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.16.0.tgz#0c33e7a054fb56e43bd160ac141f71dfebf6dda5" - integrity sha512-dhgIZsIR3L9KnE2OO5JJm6hPtStAjEPYKQsZzxRr69uVhd9xAvfXeXr0afKVNVEcIDksas6yMgHqwQ2wOXFIAg== +"@sentry/babel-plugin-component-annotate@2.22.6": + version "2.22.6" + resolved "https://registry.yarnpkg.com/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.22.6.tgz#829d6caf2c95c1c46108336de4e1049e6521435e" + integrity sha512-V2g1Y1I5eSe7dtUVMBvAJr8BaLRr4CLrgNgtPaZyMT4Rnps82SrZ5zqmEkLXPumlXhLUWR6qzoMNN2u+RXVXfQ== + +"@sentry/browser@8.41.0": + version "8.41.0" + resolved "https://registry.yarnpkg.com/@sentry/browser/-/browser-8.41.0.tgz#f594012e6377a92db72127ef39aee812efe3a3b7" + integrity sha512-FfAU55eYwW2lG4M3dEw2472RvHrD5YWSfHCZvuRf/4skX38kFvKghZQ+epL+CVHTzvIRHOrbj8qQK6YLTGl9ew== + dependencies: + "@sentry-internal/browser-utils" "8.41.0" + "@sentry-internal/feedback" "8.41.0" + "@sentry-internal/replay" "8.41.0" + "@sentry-internal/replay-canvas" "8.41.0" + "@sentry/core" "8.41.0" + "@sentry/types" "8.41.0" + +"@sentry/bundler-plugin-core@2.22.6": + version "2.22.6" + resolved "https://registry.yarnpkg.com/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.22.6.tgz#a1ea1fd43700a3ece9e7db016997e79a2782b87d" + integrity sha512-1esQdgSUCww9XAntO4pr7uAM5cfGhLsgTK9MEwAKNfvpMYJi9NUTYa3A7AZmdA8V6107Lo4OD7peIPrDRbaDCg== dependencies: "@babel/core" "^7.18.5" - "@sentry/babel-plugin-component-annotate" "2.16.0" - "@sentry/cli" "^2.22.3" + "@sentry/babel-plugin-component-annotate" "2.22.6" + "@sentry/cli" "^2.36.1" dotenv "^16.3.1" find-up "^5.0.0" glob "^9.3.2" - magic-string "0.27.0" + magic-string "0.30.8" unplugin "1.0.1" -"@sentry/cli-darwin@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.28.6.tgz#83f9127de77e2a2d25eb143d90720b3e9042adc1" - integrity sha512-KRf0VvTltHQ5gA7CdbUkaIp222LAk/f1+KqpDzO6nB/jC/tL4sfiy6YyM4uiH6IbVEudB8WpHCECiatmyAqMBA== +"@sentry/cli-darwin@2.39.1": + version "2.39.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.39.1.tgz#75c338a53834b4cf72f57599f4c72ffb36cf0781" + integrity sha512-kiNGNSAkg46LNGatfNH5tfsmI/kCAaPA62KQuFZloZiemTNzhy9/6NJP8HZ/GxGs8GDMxic6wNrV9CkVEgFLJQ== -"@sentry/cli-linux-arm64@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.28.6.tgz#6bb660e5d8145270e287a9a21201d2f9576b0634" - integrity sha512-caMDt37FI752n4/3pVltDjlrRlPFCOxK4PHvoZGQ3KFMsai0ZhE/0CLBUMQqfZf0M0r8KB2x7wqLm7xSELjefQ== +"@sentry/cli-linux-arm64@2.39.1": + version "2.39.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.39.1.tgz#27db44700c33fcb1e8966257020b43f8494373e6" + integrity sha512-5VbVJDatolDrWOgaffsEM7znjs0cR8bHt9Bq0mStM3tBolgAeSDHE89NgHggfZR+DJ2VWOy4vgCwkObrUD6NQw== -"@sentry/cli-linux-arm@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.28.6.tgz#73d466004ac445d9258e83a7b3d4e0ee6604e0bd" - integrity sha512-ANG7U47yEHD1g3JrfhpT4/MclEvmDZhctWgSP5gVw5X4AlcI87E6dTqccnLgvZjiIAQTaJJAZuSHVVF3Jk403w== +"@sentry/cli-linux-arm@2.39.1": + version "2.39.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.39.1.tgz#451683fa9a5a60b1359d104ec71334ed16f4b63c" + integrity sha512-DkENbxyRxUrfLnJLXTA4s5UL/GoctU5Cm4ER1eB7XN7p9WsamFJd/yf2KpltkjEyiTuplv0yAbdjl1KX3vKmEQ== -"@sentry/cli-linux-i686@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.28.6.tgz#f7175ca639ee05cf12d808f7fc31d59d6e2ee3b9" - integrity sha512-Tj1+GMc6lFsDRquOqaGKXFpW9QbmNK4TSfynkWKiJxdTEn5jSMlXXfr0r9OQrxu3dCCqEHkhEyU63NYVpgxIPw== +"@sentry/cli-linux-i686@2.39.1": + version "2.39.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.39.1.tgz#9965a81f97a94e8b6d1d15589e43fee158e35201" + integrity sha512-pXWVoKXCRrY7N8vc9H7mETiV9ZCz+zSnX65JQCzZxgYrayQPJTc+NPRnZTdYdk5RlAupXaFicBI2GwOCRqVRkg== -"@sentry/cli-linux-x64@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.28.6.tgz#df0af8d6c8c8c880eb7345c715a4dfa509544a40" - integrity sha512-Dt/Xz784w/z3tEObfyJEMmRIzn0D5qoK53H9kZ6e0yNvJOSKNCSOq5cQk4n1/qeG0K/6SU9dirmvHwFUiVNyYg== +"@sentry/cli-linux-x64@2.39.1": + version "2.39.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.39.1.tgz#31fe008b02f92769543dc9919e2a5cbc4cda7889" + integrity sha512-IwayNZy+it7FWG4M9LayyUmG1a/8kT9+/IEm67sT5+7dkMIMcpmHDqL8rWcPojOXuTKaOBBjkVdNMBTXy0mXlA== -"@sentry/cli-win32-i686@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.28.6.tgz#0df19912d1823b6ec034b4c4c714c7601211c926" - integrity sha512-zkpWtvY3kt+ogVaAbfFr2MEkgMMHJNJUnNMO8Ixce9gh38sybIkDkZNFnVPBXMClJV0APa4QH0EwumYBFZUMuQ== +"@sentry/cli-win32-i686@2.39.1": + version "2.39.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.39.1.tgz#609e8790c49414011445e397130560c777850b35" + integrity sha512-NglnNoqHSmE+Dz/wHeIVRnV2bLMx7tIn3IQ8vXGO5HWA2f8zYJGktbkLq1Lg23PaQmeZLPGlja3gBQfZYSG10Q== -"@sentry/cli-win32-x64@2.28.6": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.28.6.tgz#2344a206be3b555ec6540740f93a181199962804" - integrity sha512-TG2YzZ9JMeNFzbicdr5fbtsusVGACbrEfHmPgzWGDeLUP90mZxiMTjkXsE1X/5jQEQjB2+fyfXloba/Ugo51hA== +"@sentry/cli-win32-x64@2.39.1": + version "2.39.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.39.1.tgz#1a874a5570c6d162b35d9d001c96e5389d07d2cb" + integrity sha512-xv0R2CMf/X1Fte3cMWie1NXuHmUyQPDBfCyIt6k6RPFPxAYUgcqgMPznYwVMwWEA1W43PaOkSn3d8ZylsDaETw== -"@sentry/cli@^2.22.3": - version "2.28.6" - resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.28.6.tgz#645f31b9e742e7bf7668c8f867149359e79b8123" - integrity sha512-o2Ngz7xXuhwHxMi+4BFgZ4qjkX0tdZeOSIZkFAGnTbRhQe5T8bxq6CcQRLdPhqMgqvDn7XuJ3YlFtD3ZjHvD7g== +"@sentry/cli@^2.36.1": + version "2.39.1" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.39.1.tgz#916bb5b7567ccf7fdf94ef6cf8a2b9ab78370d29" + integrity sha512-JIb3e9vh0+OmQ0KxmexMXg9oZsR/G7HMwxt5BUIKAXZ9m17Xll4ETXTRnRUBT3sf7EpNGAmlQk1xEmVN9pYZYQ== dependencies: https-proxy-agent "^5.0.0" node-fetch "^2.6.7" @@ -3605,61 +3610,42 @@ proxy-from-env "^1.1.0" which "^2.0.2" optionalDependencies: - "@sentry/cli-darwin" "2.28.6" - "@sentry/cli-linux-arm" "2.28.6" - "@sentry/cli-linux-arm64" "2.28.6" - "@sentry/cli-linux-i686" "2.28.6" - "@sentry/cli-linux-x64" "2.28.6" - "@sentry/cli-win32-i686" "2.28.6" - "@sentry/cli-win32-x64" "2.28.6" + "@sentry/cli-darwin" "2.39.1" + "@sentry/cli-linux-arm" "2.39.1" + "@sentry/cli-linux-arm64" "2.39.1" + "@sentry/cli-linux-i686" "2.39.1" + "@sentry/cli-linux-x64" "2.39.1" + "@sentry/cli-win32-i686" "2.39.1" + "@sentry/cli-win32-x64" "2.39.1" -"@sentry/core@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/core/-/core-7.102.1.tgz#855d37b6bba9986a9380864c823e696d3fc5aa01" - integrity sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg== +"@sentry/core@8.41.0": + version "8.41.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.41.0.tgz#e8a25cacd25fe4358f3179e85f3c2697427fe5a6" + integrity sha512-3v7u3t4LozCA5SpZY4yqUN2U3jSrkXNoLgz6L2SUUiydyCuSwXZIFEwpLJfgQyidpNDifeQbBI5E1O910XkPsA== dependencies: - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" + "@sentry/types" "8.41.0" -"@sentry/react@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/react/-/react-7.102.1.tgz#c4ef94be7ee7ee4267d513ddccd29ce63f16e48f" - integrity sha512-X4j2DgbktlEifnd21YJKCayAmff5hnaS+9MNz9OonEwD0ARi0ks7bo0wtWHMjPK20992MO+JwczVg/1BXJYDdQ== +"@sentry/react@8.41.0": + version "8.41.0" + resolved "https://registry.yarnpkg.com/@sentry/react/-/react-8.41.0.tgz#f99c700dcbd189661d0552a17fb7b817c30c901d" + integrity sha512-/7LEWDNdICYO5s4ie8ztgpmD/GRJ1+1nHlSKvcwjf83COzT1eGvVeuYTiXFAPmXA29sY+lV1RajziwgySadjIQ== dependencies: - "@sentry/browser" "7.102.1" - "@sentry/core" "7.102.1" - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" + "@sentry/browser" "8.41.0" + "@sentry/core" "8.41.0" + "@sentry/types" "8.41.0" hoist-non-react-statics "^3.3.2" -"@sentry/replay@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/replay/-/replay-7.102.1.tgz#d6c17332d14dc312b124bbbda8f35d6a982b893c" - integrity sha512-HR/j9dGIvbrId8fh8mQlODx7JrhRmawEd9e9P3laPtogWCg/5TI+XPb2VGSaXOX9VWtb/6Z2UjHsaGjgg6YcuA== - dependencies: - "@sentry-internal/tracing" "7.102.1" - "@sentry/core" "7.102.1" - "@sentry/types" "7.102.1" - "@sentry/utils" "7.102.1" +"@sentry/types@8.41.0": + version "8.41.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.41.0.tgz#db40c93bcedad26569c5dfe10a4b31253c349a10" + integrity sha512-eqdnGr9k9H++b9CjVUoTNUVahPVWeNnMy0YGkqS5+cjWWC+x43p56202oidGFmWo6702ub/xwUNH6M5PC4kq6A== -"@sentry/types@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.102.1.tgz#18c35f32ecbd12afb9860ca2de7bfff542d10b27" - integrity sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA== - -"@sentry/utils@7.102.1": - version "7.102.1" - resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-7.102.1.tgz#45ddcdf2e700d40160347bbdf4233aff3179d398" - integrity sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg== +"@sentry/webpack-plugin@2.22.6": + version "2.22.6" + resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-2.22.6.tgz#8c9d27d5cd89153a5b6e08cc9dcb3048b122ffbc" + integrity sha512-BiLhAzQYAz/9kCXKj2LeUKWf/9GBVn2dD0DeYK89s+sjDEaxjbcLBBiLlLrzT7eC9QVj2tUZRKOi6puCfc8ysw== dependencies: - "@sentry/types" "7.102.1" - -"@sentry/webpack-plugin@2.16.0": - version "2.16.0" - resolved "https://registry.yarnpkg.com/@sentry/webpack-plugin/-/webpack-plugin-2.16.0.tgz#4764577edb10c9575a8b4ce03135493f995f56b9" - integrity sha512-BeKLmtK4OD9V3j92fm/lm6yp+++s2U5Uf17HwNFGt39PEOq+wUDISsx0dhXA5Qls2Bg3WhguDK71blCaVefMeg== - dependencies: - "@sentry/bundler-plugin-core" "2.16.0" + "@sentry/bundler-plugin-core" "2.22.6" unplugin "1.0.1" uuid "^9.0.0" @@ -11067,12 +11053,12 @@ lz-string@^1.4.4: resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz" integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== -magic-string@0.27.0: - version "0.27.0" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.27.0.tgz#e4a3413b4bab6d98d2becffd48b4a257effdbbf3" - integrity sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA== +magic-string@0.30.8: + version "0.30.8" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.8.tgz#14e8624246d2bedba70d5462aa99ac9681844613" + integrity sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ== dependencies: - "@jridgewell/sourcemap-codec" "^1.4.13" + "@jridgewell/sourcemap-codec" "^1.4.15" make-dir@^2.1.0: version "2.1.0" diff --git a/go.mod b/go.mod index 1099a0316e..9ab2cd98ec 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/ClickHouse/clickhouse-go/v2 v2.25.0 github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd - github.com/SigNoz/signoz-otel-collector v0.111.13 + github.com/SigNoz/signoz-otel-collector v0.111.14 github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 github.com/SigNoz/zap_otlp/zap_otlp_sync v0.0.0-20230822164844-1b861a431974 github.com/antonmedv/expr v1.15.3 diff --git a/go.sum b/go.sum index 6fc98cd705..b46384371a 100644 --- a/go.sum +++ b/go.sum @@ -70,8 +70,8 @@ github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd h1:Bk43AsDYe0fhkb github.com/SigNoz/govaluate v0.0.0-20240203125216-988004ccc7fd/go.mod h1:nxRcH/OEdM8QxzH37xkGzomr1O0JpYBRS6pwjsWW6Pc= github.com/SigNoz/prometheus v1.12.0 h1:+BXeIHyMOOWWa+xjhJ+x80JFva7r1WzWIfIhQ5PUmIE= github.com/SigNoz/prometheus v1.12.0/go.mod h1:EqNM27OwmPfqMUk+E+XG1L9rfDFcyXnzzDrg0EPOfxA= -github.com/SigNoz/signoz-otel-collector v0.111.13 h1:pWaRrt4mGQyl2DZUYRTr9A+KnvcCeGBwX65PFV2duMg= -github.com/SigNoz/signoz-otel-collector v0.111.13/go.mod h1:vRDT10om89DHybN7SRMlt8IN9+/pgh1D57pNHPr2LM4= +github.com/SigNoz/signoz-otel-collector v0.111.14 h1:nvRucNK/TTtZKM3Dsr/UNx+LwkjaGwx0yPlMvGw/4j0= +github.com/SigNoz/signoz-otel-collector v0.111.14/go.mod h1:vRDT10om89DHybN7SRMlt8IN9+/pgh1D57pNHPr2LM4= github.com/SigNoz/zap_otlp v0.1.0 h1:T7rRcFN87GavY8lDGZj0Z3Xv6OhJA6Pj3I9dNPmqvRc= github.com/SigNoz/zap_otlp v0.1.0/go.mod h1:lcHvbDbRgvDnPxo9lDlaL1JK2PyOyouP/C3ynnYIvyo= github.com/SigNoz/zap_otlp/zap_otlp_encoder v0.0.0-20230822164844-1b861a431974 h1:PKVgdf83Yw+lZJbFtNGBgqXiXNf3+kOXW2qZ7Ms7OaY= diff --git a/pkg/query-service/app/clickhouseReader/reader.go b/pkg/query-service/app/clickhouseReader/reader.go index f63b4d3453..256ab98d68 100644 --- a/pkg/query-service/app/clickhouseReader/reader.go +++ b/pkg/query-service/app/clickhouseReader/reader.go @@ -2540,7 +2540,7 @@ func (r *ClickHouseReader) GetTotalLogs(ctx context.Context) (uint64, error) { var totalLogs uint64 - queryStr := fmt.Sprintf("SELECT count() from %s.%s;", r.logsDB, r.logsTable) + queryStr := fmt.Sprintf("SELECT count() from %s.%s;", r.logsDB, r.logsTableName) r.db.QueryRow(ctx, queryStr).Scan(&totalLogs) return totalLogs, nil diff --git a/pkg/query-service/app/dashboards/model.go b/pkg/query-service/app/dashboards/model.go index d4b11d8502..99aa53ef9c 100644 --- a/pkg/query-service/app/dashboards/model.go +++ b/pkg/query-service/app/dashboards/model.go @@ -472,6 +472,11 @@ func GetDashboardsInfo(ctx context.Context) (*model.DashboardsInfo, error) { if isDashboardWithTSV2(dashboard.Data) { count = count + 1 } + + if dashboardInfo.DashboardsWithTraceChQuery > 0 { + dashboardsInfo.DashboardNamesWithTraceChQuery = append(dashboardsInfo.DashboardNamesWithTraceChQuery, dashboardName) + } + // check if dashboard is a has a log operator with contains } @@ -505,6 +510,8 @@ func isDashboardWithTracesClickhouseQuery(data map[string]interface{}) bool { if err != nil { return false } + + // also check if the query is actually active str := string(jsonData) result := strings.Contains(str, "signoz_traces.distributed_signoz_index_v2") || strings.Contains(str, "signoz_traces.distributed_signoz_spans") || diff --git a/pkg/query-service/model/response.go b/pkg/query-service/model/response.go index d0d4a51b60..c4e743ce92 100644 --- a/pkg/query-service/model/response.go +++ b/pkg/query-service/model/response.go @@ -658,6 +658,7 @@ type DashboardsInfo struct { QueriesWithTSV2 int `json:"queriesWithTSV2"` DashboardsWithLogsChQuery int `json:"dashboardsWithLogsChQuery"` DashboardsWithTraceChQuery int `json:"dashboardsWithTraceChQuery"` + DashboardNamesWithTraceChQuery []string `json:"dashboardNamesWithTraceChQuery"` LogsPanelsWithAttrContainsOp int `json:"logsPanelsWithAttrContainsOp"` } diff --git a/pkg/query-service/telemetry/telemetry.go b/pkg/query-service/telemetry/telemetry.go index 0de96a0f69..3361a69331 100644 --- a/pkg/query-service/telemetry/telemetry.go +++ b/pkg/query-service/telemetry/telemetry.go @@ -347,6 +347,7 @@ func createTelemetry() { "dashboardsWithTSV2": dashboardsInfo.QueriesWithTSV2, "dashboardWithLogsChQuery": dashboardsInfo.DashboardsWithLogsChQuery, "dashboardWithTraceChQuery": dashboardsInfo.DashboardsWithTraceChQuery, + "dashboardNamesWithTraceChQuery": dashboardsInfo.DashboardNamesWithTraceChQuery, "totalAlerts": alertsInfo.TotalAlerts, "alertsWithTSV2": alertsInfo.AlertsWithTSV2, "logsBasedAlerts": alertsInfo.LogsBasedAlerts,