mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-05 16:20:47 +08:00
Merge pull request #6593 from SigNoz/release-sync/v0.61.x
Release Sync/v0.61.x
This commit is contained in:
commit
47d8c9e3e7
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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:
|
||||
[
|
||||
|
@ -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:
|
||||
[
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
1
frontend/public/Images/feature-graphic-correlation.svg
Normal file
1
frontend/public/Images/feature-graphic-correlation.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 408 KiB |
12
frontend/public/locales/en-GB/failedPayment.json
Normal file
12
frontend/public/locales/en-GB/failedPayment.json
Normal file
@ -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"
|
||||
}
|
@ -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"
|
||||
}
|
||||
|
@ -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",
|
||||
|
12
frontend/public/locales/en/failedPayment.json
Normal file
12
frontend/public/locales/en/failedPayment.json
Normal file
@ -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"
|
||||
}
|
@ -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"
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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<AppState, AppReducer>((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]);
|
||||
|
@ -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 (
|
||||
<ConfigProvider theme={themeConfig}>
|
||||
<Router history={history}>
|
||||
<CompatRouter>
|
||||
<NotificationProvider>
|
||||
<PrivateRoute>
|
||||
<ResourceProvider>
|
||||
<QueryBuilderProvider>
|
||||
<DashboardProvider>
|
||||
<KeyboardHotkeysProvider>
|
||||
<AlertRuleProvider>
|
||||
<AppLayout>
|
||||
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
|
||||
<Switch>
|
||||
{routes.map(({ path, component, exact }) => (
|
||||
<Route
|
||||
key={`${path}`}
|
||||
exact={exact}
|
||||
path={path}
|
||||
component={component}
|
||||
/>
|
||||
))}
|
||||
<AppProvider>
|
||||
<ConfigProvider theme={themeConfig}>
|
||||
<Router history={history}>
|
||||
<CompatRouter>
|
||||
<NotificationProvider>
|
||||
<PrivateRoute>
|
||||
<ResourceProvider>
|
||||
<QueryBuilderProvider>
|
||||
<DashboardProvider>
|
||||
<KeyboardHotkeysProvider>
|
||||
<AlertRuleProvider>
|
||||
<AppLayout>
|
||||
<Suspense fallback={<Spinner size="large" tip="Loading..." />}>
|
||||
<Switch>
|
||||
{routes.map(({ path, component, exact }) => (
|
||||
<Route
|
||||
key={`${path}`}
|
||||
exact={exact}
|
||||
path={path}
|
||||
component={component}
|
||||
/>
|
||||
))}
|
||||
|
||||
<Route path="*" component={NotFound} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</AppLayout>
|
||||
</AlertRuleProvider>
|
||||
</KeyboardHotkeysProvider>
|
||||
</DashboardProvider>
|
||||
</QueryBuilderProvider>
|
||||
</ResourceProvider>
|
||||
</PrivateRoute>
|
||||
</NotificationProvider>
|
||||
</CompatRouter>
|
||||
</Router>
|
||||
</ConfigProvider>
|
||||
<Route path="*" component={NotFound} />
|
||||
</Switch>
|
||||
</Suspense>
|
||||
</AppLayout>
|
||||
</AlertRuleProvider>
|
||||
</KeyboardHotkeysProvider>
|
||||
</DashboardProvider>
|
||||
</QueryBuilderProvider>
|
||||
</ResourceProvider>
|
||||
</PrivateRoute>
|
||||
</NotificationProvider>
|
||||
</CompatRouter>
|
||||
</Router>
|
||||
</ConfigProvider>
|
||||
</AppProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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'),
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -48,6 +48,8 @@ export interface HostListResponse {
|
||||
records: HostData[];
|
||||
groups: null;
|
||||
total: number;
|
||||
sentAnyHostMetricsData: boolean;
|
||||
isSendingK8SAgentMetrics: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
18
frontend/src/api/licensesV3/getActive.ts
Normal file
18
frontend/src/api/licensesV3/getActive.ts
Normal file
@ -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<LicenseV3EventQueueResModel> | ErrorResponse
|
||||
> => {
|
||||
const response = await axios.get('/licenses/active');
|
||||
|
||||
return {
|
||||
statusCode: 200,
|
||||
error: null,
|
||||
message: response.data.status,
|
||||
payload: response.data.data,
|
||||
};
|
||||
};
|
||||
|
||||
export default getActive;
|
@ -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 {
|
||||
|
@ -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 (
|
||||
<Space direction="vertical" className="host-containers" size={24}>
|
||||
<div className="infra-container-card">
|
||||
<img
|
||||
src="/Icons/infraContainers.svg"
|
||||
alt="infra-container"
|
||||
width={32}
|
||||
height={32}
|
||||
/>
|
||||
<div className="infra-container-card-container">
|
||||
<div className="dev-status-container">
|
||||
<div className="infra-container-card">
|
||||
<img
|
||||
src="/Icons/infraContainers.svg"
|
||||
alt="infra-container"
|
||||
width={32}
|
||||
height={32}
|
||||
/>
|
||||
|
||||
<Text className="infra-container-card-text">
|
||||
{t('containers_visualization_message')}
|
||||
</Text>
|
||||
</div>
|
||||
<Text className="infra-container-card-text">
|
||||
{t('containers_visualization_message')}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div className="infra-container-working-msg">
|
||||
<Space>
|
||||
<img src="/Icons/broom.svg" alt="broom" width={16} height={16} />
|
||||
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
||||
</Space>
|
||||
<div className="infra-container-working-msg">
|
||||
<Space>
|
||||
<img src="/Icons/broom.svg" alt="broom" width={24} height={24} />
|
||||
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WaitlistFragment entityType="containers" />
|
||||
</div>
|
||||
</Space>
|
||||
);
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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 (
|
||||
<Space direction="vertical" className="host-processes" size={24}>
|
||||
<div className="infra-container-card">
|
||||
<img
|
||||
src="/Icons/infraContainers.svg"
|
||||
alt="infra-container"
|
||||
width={32}
|
||||
height={32}
|
||||
/>
|
||||
<Text className="infra-container-card-text">
|
||||
{t('processes_visualization_message')}
|
||||
</Text>
|
||||
</div>
|
||||
<div className="infra-container-card-container">
|
||||
<div className="dev-status-container">
|
||||
<div className="infra-container-card">
|
||||
<img
|
||||
src="/Icons/infraContainers.svg"
|
||||
alt="infra-container"
|
||||
width={32}
|
||||
height={32}
|
||||
/>
|
||||
<Text className="infra-container-card-text">
|
||||
{t('processes_visualization_message')}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div className="infra-container-working-msg">
|
||||
<Space>
|
||||
<img src="/Icons/broom.svg" alt="broom" width={16} height={16} />
|
||||
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
||||
</Space>
|
||||
<div className="infra-container-working-msg">
|
||||
<Space>
|
||||
<img src="/Icons/broom.svg" alt="broom" width={24} height={24} />
|
||||
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
||||
</Space>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<WaitlistFragment entityType="processes" />
|
||||
</div>
|
||||
</Space>
|
||||
);
|
||||
|
@ -0,0 +1,15 @@
|
||||
.wait-list-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.wait-list-text {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.join-waitlist-btn {
|
||||
width: 160px;
|
||||
border-radius: 2px;
|
||||
background: var(--slate-500);
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
import './WaitListFragment.styles.scss';
|
||||
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Typography } from 'antd';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { CheckCircle2, HandPlatter } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
|
||||
export default function WaitlistFragment({
|
||||
entityType,
|
||||
}: {
|
||||
entityType: string;
|
||||
}): JSX.Element {
|
||||
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
const { t } = useTranslation(['infraMonitoring']);
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [isSuccess, setIsSuccess] = useState(false);
|
||||
|
||||
const handleJoinWaitlist = (): void => {
|
||||
if (!user || !user.email) return;
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
logEvent('Infra Monitoring: Get Early Access Clicked', {
|
||||
entity_type: entityType,
|
||||
userEmail: user.email,
|
||||
})
|
||||
.then(() => {
|
||||
notifications.success({
|
||||
message: t('waitlist_success_message'),
|
||||
});
|
||||
|
||||
setIsSubmitting(false);
|
||||
setIsSuccess(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsSuccess(false);
|
||||
}, 4000);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error logging event:', error);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="wait-list-container">
|
||||
<Typography.Text className="wait-list-text">
|
||||
{t('waitlist_message')}
|
||||
</Typography.Text>
|
||||
|
||||
<Button
|
||||
className="periscope-btn join-waitlist-btn"
|
||||
type="default"
|
||||
loading={isSubmitting}
|
||||
icon={
|
||||
isSuccess ? (
|
||||
<CheckCircle2 size={16} color={Color.BG_FOREST_500} />
|
||||
) : (
|
||||
<HandPlatter size={16} />
|
||||
)
|
||||
}
|
||||
onClick={handleJoinWaitlist}
|
||||
>
|
||||
Get early access
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -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);
|
||||
|
@ -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<string | null>(null);
|
||||
const listRef = useRef<HTMLDivElement>(null);
|
||||
const initialMouseEnterRef = useRef<boolean>(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 (
|
||||
<div
|
||||
className={cx('nested-menu-container', addNewColumn ? 'active' : '')}
|
||||
className={cx(
|
||||
'nested-menu-container',
|
||||
showAddNewColumnContainer ? 'active' : '',
|
||||
)}
|
||||
onClick={(event): void => {
|
||||
// this is to restrict click events to propogate to parent
|
||||
event.stopPropagation();
|
||||
@ -158,8 +263,72 @@ export default function LogsFormatOptionsMenu({
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
) : null}
|
||||
|
||||
{showAddNewColumnContainer && (
|
||||
<div className="add-new-column-container">
|
||||
<div className="add-new-column-header">
|
||||
<div className="title">
|
||||
<div className="periscope-btn ghost" onClick={handleToggleAddNewColumn}>
|
||||
<ChevronLeft
|
||||
size={14}
|
||||
className="back-icon"
|
||||
onClick={handleToggleAddNewColumn}
|
||||
/>
|
||||
</div>
|
||||
Add New Column
|
||||
</div>
|
||||
|
||||
<Input
|
||||
tabIndex={0}
|
||||
type="text"
|
||||
autoFocus
|
||||
onFocus={addColumn?.onFocus}
|
||||
onChange={handleSearchValueChange}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="add-new-column-content">
|
||||
{addColumn?.isFetching && (
|
||||
<div className="loading-container"> Loading ... </div>
|
||||
)}
|
||||
|
||||
<div className="column-format-new-options" ref={listRef}>
|
||||
{addColumn?.options?.map(({ label, value }, index) => (
|
||||
<div
|
||||
className={cx('column-name', value === selectedValue && 'selected')}
|
||||
key={value}
|
||||
onMouseEnter={(): void => {
|
||||
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 || []);
|
||||
}}
|
||||
>
|
||||
<div className="name">
|
||||
<Tooltip placement="left" title={label}>
|
||||
{label}
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isFontSizeOptionsOpen && !showAddNewColumnContainer && (
|
||||
<div>
|
||||
<div className="font-size-container">
|
||||
<div className="title">Font Size</div>
|
||||
<Button
|
||||
@ -230,29 +399,10 @@ export default function LogsFormatOptionsMenu({
|
||||
</>
|
||||
|
||||
<div className="selected-item-content-container active">
|
||||
{!addNewColumn && <div className="horizontal-line" />}
|
||||
|
||||
{addNewColumn && (
|
||||
<div className="add-new-column-header">
|
||||
<div className="title">
|
||||
{' '}
|
||||
columns
|
||||
<X size={14} onClick={handleToggleAddNewColumn} />{' '}
|
||||
</div>
|
||||
|
||||
<Input
|
||||
tabIndex={0}
|
||||
type="text"
|
||||
autoFocus
|
||||
onFocus={addColumn?.onFocus}
|
||||
onChange={handleSearchValueChange}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{!showAddNewColumnContainer && <div className="horizontal-line" />}
|
||||
|
||||
<div className="item-content">
|
||||
{!addNewColumn && (
|
||||
{!showAddNewColumnContainer && (
|
||||
<div className="title">
|
||||
columns
|
||||
<Plus size={14} onClick={handleToggleAddNewColumn} />{' '}
|
||||
@ -274,48 +424,17 @@ export default function LogsFormatOptionsMenu({
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{addColumn?.isFetching && (
|
||||
<div className="loading-container"> Loading ... </div>
|
||||
)}
|
||||
|
||||
{addNewColumn &&
|
||||
addColumn &&
|
||||
addColumn.value.length > 0 &&
|
||||
addColumn.options &&
|
||||
addColumn?.options?.length > 0 && (
|
||||
<Divider className="column-divider" />
|
||||
{addColumn && addColumn?.value?.length === 0 && (
|
||||
<div className="column-name no-columns-selected">
|
||||
No columns selected
|
||||
</div>
|
||||
)}
|
||||
|
||||
{addNewColumn && (
|
||||
<div className="column-format-new-options">
|
||||
{addColumn?.options?.map(({ label, value }) => (
|
||||
<div
|
||||
className="column-name"
|
||||
key={value}
|
||||
onClick={(eve): void => {
|
||||
eve.stopPropagation();
|
||||
|
||||
if (addColumn && addColumn?.onSelect) {
|
||||
addColumn?.onSelect(value, { label, disabled: false });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="name">
|
||||
<Tooltip placement="left" title={label}>
|
||||
{label}
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -8,6 +8,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
cursor: pointer;
|
||||
|
||||
.left-action {
|
||||
display: flex;
|
||||
|
@ -396,23 +396,22 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="checkbox-filter">
|
||||
<section className="filter-header-checkbox">
|
||||
<section
|
||||
className="filter-header-checkbox"
|
||||
onClick={(): void => {
|
||||
if (isOpen) {
|
||||
setIsOpen(false);
|
||||
setVisibleItemsCount(10);
|
||||
} else {
|
||||
setIsOpen(true);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<section className="left-action">
|
||||
{isOpen ? (
|
||||
<ChevronDown
|
||||
size={13}
|
||||
cursor="pointer"
|
||||
onClick={(): void => {
|
||||
setIsOpen(false);
|
||||
setVisibleItemsCount(10);
|
||||
}}
|
||||
/>
|
||||
<ChevronDown size={13} cursor="pointer" />
|
||||
) : (
|
||||
<ChevronRight
|
||||
size={13}
|
||||
onClick={(): void => setIsOpen(true)}
|
||||
cursor="pointer"
|
||||
/>
|
||||
<ChevronRight size={13} cursor="pointer" />
|
||||
)}
|
||||
<Typography.Text className="title">{filter.title}</Typography.Text>
|
||||
</section>
|
||||
@ -420,7 +419,11 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
|
||||
{isOpen && (
|
||||
<Typography.Text
|
||||
className="clear-all"
|
||||
onClick={handleClearFilterAttribute}
|
||||
onClick={(e): void => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
handleClearFilterAttribute();
|
||||
}}
|
||||
>
|
||||
Clear All
|
||||
</Typography.Text>
|
||||
|
@ -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',
|
||||
}
|
||||
|
@ -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',
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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<boolean>(false);
|
||||
|
||||
const handleBillingOnSuccess = (
|
||||
data: ErrorResponse | SuccessResponse<CheckoutSuccessPayloadProps, unknown>,
|
||||
): 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 {
|
||||
<title>{pageTitle}</title>
|
||||
</Helmet>
|
||||
|
||||
{showTrialExpiryBanner && (
|
||||
{showTrialExpiryBanner && !showPaymentFailedWarning && (
|
||||
<div className="trial-expiry-banner">
|
||||
You are in free trial period. Your free trial will end on{' '}
|
||||
<span>
|
||||
@ -289,6 +353,36 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!showTrialExpiryBanner && showPaymentFailedWarning && (
|
||||
<div className="payment-failed-banner">
|
||||
Your bill payment has failed. Your workspace will get suspended on{' '}
|
||||
<span>
|
||||
{getFormattedDateWithMinutes(
|
||||
dayjs(activeLicenseV3?.event_queue?.scheduled_at).unix() || Date.now(),
|
||||
)}
|
||||
.
|
||||
</span>
|
||||
{role === 'ADMIN' ? (
|
||||
<span>
|
||||
{' '}
|
||||
Please{' '}
|
||||
<a
|
||||
className="upgrade-link"
|
||||
onClick={(): void => {
|
||||
if (!isLoadingManageBilling) {
|
||||
handleFailedPayment();
|
||||
}
|
||||
}}
|
||||
>
|
||||
pay the bill
|
||||
</a>
|
||||
to continue using SigNoz features.
|
||||
</span>
|
||||
) : (
|
||||
' Please contact your administrator to pay the bill.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Flex className={cx('app-layout', isDarkMode ? 'darkMode' : 'lightMode')}>
|
||||
{isToDisplayLayout && !renderFullScreen && (
|
||||
|
@ -0,0 +1,52 @@
|
||||
import { Typography } from 'antd';
|
||||
|
||||
export default function HostsEmptyOrIncorrectMetrics({
|
||||
noData,
|
||||
incorrectData,
|
||||
}: {
|
||||
noData: boolean;
|
||||
incorrectData: boolean;
|
||||
}): JSX.Element {
|
||||
return (
|
||||
<div className="hosts-empty-state-container">
|
||||
<div className="hosts-empty-state-container-content">
|
||||
<img className="eyes-emoji" src="/Images/eyesEmoji.svg" alt="eyes emoji" />
|
||||
|
||||
{noData && (
|
||||
<div className="no-hosts-message">
|
||||
<Typography.Title level={5} className="no-hosts-message-title">
|
||||
No host metrics data received yet.
|
||||
</Typography.Title>
|
||||
|
||||
<Typography.Text className="no-hosts-message-text">
|
||||
Infrastructure monitoring requires the{' '}
|
||||
<a
|
||||
href="https://github.com/open-telemetry/semantic-conventions/blob/main/docs/system/system-metrics.md"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
OpenTelemetry system metrics
|
||||
</a>
|
||||
. Please refer to{' '}
|
||||
<a
|
||||
href="https://signoz.io/docs/userguide/hostmetrics"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
this
|
||||
</a>{' '}
|
||||
to learn how to send host metrics to SigNoz.
|
||||
</Typography.Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{incorrectData && (
|
||||
<Typography.Text className="incorrect-metrics-message">
|
||||
To see host metrics, upgrade to the latest version of SigNoz k8s-infra
|
||||
chart. Please contact support if you need help.
|
||||
</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -2,6 +2,7 @@ import './InfraMonitoring.styles.scss';
|
||||
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
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<AppState, GlobalReducer>(
|
||||
(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<HostRowData>['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 (
|
||||
<div className="hosts-list">
|
||||
<HostsListControls handleFiltersChange={handleFiltersChange} />
|
||||
{isError && <Typography>{data?.error || 'Something went wrong'}</Typography>}
|
||||
|
||||
{isDataPresent && filters.items.length === 0 && (
|
||||
<NoLogs dataSource={DataSource.METRICS} />
|
||||
{showHostsEmptyState && (
|
||||
<HostsEmptyOrIncorrectMetrics
|
||||
noData={!sentAnyHostMetricsData}
|
||||
incorrectData={isSendingIncorrectK8SAgentMetrics}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!isFetching &&
|
||||
!isLoading &&
|
||||
formattedHostMetricsData.length === 0 &&
|
||||
filters.items.length > 0 && (
|
||||
<div className="no-hosts-message">No hosts match the applied filters.</div>
|
||||
)}
|
||||
{showNoFilteredHostsMessage && (
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
{!isError && (
|
||||
<Typography.Text className="no-filtered-hosts-message">
|
||||
This query had no results. Edit your query and try again!
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(isFetching || isLoading) && (
|
||||
<div className="hosts-list-loading-state">
|
||||
<Skeleton.Input
|
||||
className="hosts-list-loading-state-item"
|
||||
size="large"
|
||||
block
|
||||
active
|
||||
/>
|
||||
<Skeleton.Input
|
||||
className="hosts-list-loading-state-item"
|
||||
size="large"
|
||||
block
|
||||
active
|
||||
/>
|
||||
<Skeleton.Input
|
||||
className="hosts-list-loading-state-item"
|
||||
size="large"
|
||||
block
|
||||
active
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showHostsTable && (
|
||||
<Table
|
||||
className="hosts-list-table"
|
||||
dataSource={isFetching || isLoading ? [] : formattedHostMetricsData}
|
||||
|
@ -10,10 +10,6 @@
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.no-hosts-message {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.hosts-list-controls {
|
||||
padding: 8px;
|
||||
|
||||
@ -210,6 +206,68 @@
|
||||
}
|
||||
}
|
||||
|
||||
.hosts-list-loading-state {
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
||||
.hosts-list-loading-state-item {
|
||||
height: 48px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.no-filtered-hosts-message-container {
|
||||
height: 30vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.no-filtered-hosts-message-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
|
||||
width: fit-content;
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.no-filtered-hosts-message {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.hosts-empty-state-container {
|
||||
padding: 16px;
|
||||
height: 40vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.hosts-empty-state-container-content {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
|
||||
width: fit-content;
|
||||
|
||||
.no-hosts-message {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.no-hosts-message-title {
|
||||
margin-top: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.infra-monitoring-container {
|
||||
.ant-table-thead > tr > th {
|
||||
|
@ -16,7 +16,7 @@ export default function NavItem({
|
||||
isActive: boolean;
|
||||
onClick: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||
}): JSX.Element {
|
||||
const { label, icon, isBeta } = item;
|
||||
const { label, icon, isBeta, isNew } = item;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -36,6 +36,14 @@ export default function NavItem({
|
||||
</Tag>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isNew && (
|
||||
<div className="nav-item-new">
|
||||
<Tag bordered={false} className="sidenav-new-tag">
|
||||
New
|
||||
</Tag>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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: <ScrollText size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
|
||||
label: 'Infra Monitoring',
|
||||
icon: <Boxes size={16} />,
|
||||
isNew: true,
|
||||
},
|
||||
{
|
||||
key: ROUTES.ALL_DASHBOARD,
|
||||
label: 'Dashboards',
|
||||
@ -91,7 +97,6 @@ const menuItems: SidebarItem[] = [
|
||||
key: ROUTES.MESSAGING_QUEUES,
|
||||
label: 'Messaging Queues',
|
||||
icon: <ListMinus size={16} />,
|
||||
isBeta: true,
|
||||
},
|
||||
{
|
||||
key: ROUTES.LIST_ALL_ALERT,
|
||||
@ -119,12 +124,6 @@ const menuItems: SidebarItem[] = [
|
||||
label: 'Billing',
|
||||
icon: <Receipt size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.INFRASTRUCTURE_MONITORING_HOSTS,
|
||||
label: 'Infra Monitoring',
|
||||
icon: <PackagePlus size={16} />,
|
||||
isBeta: true,
|
||||
},
|
||||
{
|
||||
key: ROUTES.SETTINGS,
|
||||
label: 'Settings',
|
||||
|
@ -13,6 +13,7 @@ export interface SidebarItem {
|
||||
key: string | number;
|
||||
label?: ReactNode;
|
||||
isBeta?: boolean;
|
||||
isNew?: boolean;
|
||||
}
|
||||
|
||||
export enum SecondaryMenuItemKey {
|
||||
|
@ -27,6 +27,7 @@ const breadcrumbNameMap: Record<string, string> = {
|
||||
[ROUTES.BILLING]: 'Billing',
|
||||
[ROUTES.SUPPORT]: 'Support',
|
||||
[ROUTES.WORKSPACE_LOCKED]: 'Workspace Locked',
|
||||
[ROUTES.WORKSPACE_SUSPENDED]: 'Workspace Suspended',
|
||||
[ROUTES.MESSAGING_QUEUES]: 'Messaging Queues',
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
25
frontend/src/hooks/useActiveLicenseV3/useActiveLicenseV3.tsx
Normal file
25
frontend/src/hooks/useActiveLicenseV3/useActiveLicenseV3.tsx
Normal file
@ -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<AppState, AppReducer>((state) => state.app);
|
||||
|
||||
return useQuery({
|
||||
queryFn: getActive,
|
||||
queryKey: [REACT_QUERY_KEY.GET_ACTIVE_LICENSE_V3, user?.email],
|
||||
enabled: !!user?.email,
|
||||
});
|
||||
};
|
||||
|
||||
type UseLicense = UseQueryResult<
|
||||
SuccessResponse<LicenseV3ResModel> | ErrorResponse,
|
||||
unknown
|
||||
>;
|
||||
|
||||
export default useActiveLicenseV3;
|
@ -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',
|
||||
}),
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
179
frontend/src/pages/WorkspaceSuspended/WorkspaceSuspended.tsx
Normal file
179
frontend/src/pages/WorkspaceSuspended/WorkspaceSuspended.tsx
Normal file
@ -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<AppState, AppReducer>((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 (
|
||||
<div>
|
||||
<Modal
|
||||
rootClassName="workspace-suspended__modal"
|
||||
title={
|
||||
<div className="workspace-suspended__modal__header">
|
||||
<span className="workspace-suspended__modal__title">
|
||||
{t('workspaceSuspended')}
|
||||
</span>
|
||||
<span className="workspace-suspended__modal__header__actions">
|
||||
<Typography.Text className="workspace-suspended__modal__title">
|
||||
Got Questions?
|
||||
</Typography.Text>
|
||||
<Button
|
||||
type="default"
|
||||
shape="round"
|
||||
size="middle"
|
||||
href="mailto:cloud-support@signoz.io"
|
||||
role="button"
|
||||
>
|
||||
Contact Us
|
||||
</Button>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
open
|
||||
closable={false}
|
||||
footer={null}
|
||||
width="65%"
|
||||
>
|
||||
<div className="workspace-suspended__container">
|
||||
{isFetchingActiveLicenseV3 || !activeLicenseV3 ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
<>
|
||||
<Row justify="center" align="middle">
|
||||
<Col>
|
||||
<Space direction="vertical" align="center">
|
||||
<Typography.Title level={2}>
|
||||
<div className="workspace-suspended__title">{t('actionHeader')}</div>
|
||||
</Typography.Title>
|
||||
<Typography.Paragraph className="workspace-suspended__details">
|
||||
{t('actionDescription')}
|
||||
<br />
|
||||
{t('yourDataIsSafe')}{' '}
|
||||
<span className="workspace-suspended__details__highlight">
|
||||
{getFormattedDateWithMinutes(
|
||||
dayjs(activeLicenseV3?.event_queue?.scheduled_at).unix() ||
|
||||
Date.now(),
|
||||
)}
|
||||
</span>{' '}
|
||||
{t('actNow')}
|
||||
</Typography.Paragraph>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
{!isAdmin && (
|
||||
<Row
|
||||
justify="center"
|
||||
align="middle"
|
||||
className="workspace-suspended__modal__cta"
|
||||
gutter={[16, 16]}
|
||||
>
|
||||
<Col>
|
||||
<Alert
|
||||
message="Contact your admin to proceed with the upgrade."
|
||||
type="info"
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{isAdmin && (
|
||||
<Row
|
||||
justify="center"
|
||||
align="middle"
|
||||
className="workspace-suspended__modal__cta"
|
||||
gutter={[16, 16]}
|
||||
>
|
||||
<Col>
|
||||
<Button
|
||||
type="primary"
|
||||
shape="round"
|
||||
size="middle"
|
||||
loading={isLoading}
|
||||
onClick={handleUpdateCreditCard}
|
||||
>
|
||||
{t('continueMyJourney')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
<div className="workspace-suspended__creative">
|
||||
<img
|
||||
src="/Images/feature-graphic-correlation.svg"
|
||||
alt="correlation-graphic"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WorkspaceSuspended;
|
48
frontend/src/providers/App/App.tsx
Normal file
48
frontend/src/providers/App/App.tsx
Normal file
@ -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<IAppContext | undefined>(undefined);
|
||||
|
||||
export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
||||
const [activeLicenseV3, setActiveLicenseV3] = useState<LicenseV3ResModel>();
|
||||
|
||||
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 <AppContext.Provider value={value}>{children}</AppContext.Provider>;
|
||||
}
|
||||
|
||||
export const useAppContext = (): IAppContext => {
|
||||
const context = useContext(AppContext);
|
||||
if (context === undefined) {
|
||||
throw new Error('useAppContext must be used within an AppProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
26
frontend/src/types/api/licensesV3/getActive.ts
Normal file
26
frontend/src/types/api/licensesV3/getActive.ts
Normal file
@ -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;
|
||||
};
|
@ -93,6 +93,7 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
||||
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'],
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
2
go.mod
2
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
|
||||
|
4
go.sum
4
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=
|
||||
|
@ -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
|
||||
|
@ -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") ||
|
||||
|
@ -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"`
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user