Compare commits

...

4 Commits

Author SHA1 Message Date
Abhi Kumar
159f54e79c chore: updated tooltipplugin tests 2026-04-16 14:12:39 +05:30
Abhi Kumar
b2ba02ccbf chore: added changes for pinning tooltip with a shortcut key 2026-04-16 13:20:15 +05:30
SagarRajput-7
a7ce8b2d24 chore: added eslint, stylelint and asset migration across the codebase (#10915)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
* chore: jest module resolution fix

* chore: remove the snapshot update since no migration is done under this pr

* chore: added eslint and stylelint for the asset migration task

* chore: asset migration - using the src/assets across the codebase, instead of the public dir

* chore: code refactor

* chore: fmt fix

* chore: strengthen the lint rule and migration the gaps found

* chore: addressed feedback comments

* chore: addressed comments and added public reference rule

* chore: addressed comments

* feat: addressed comments
2026-04-16 07:14:44 +00:00
Pandey
b3da6fb251 refactor(alertmanager): move API handlers to signozapiserver (#10941)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
* refactor(alertmanager): move API handlers to signozapiserver

Extract Handler interface in pkg/alertmanager/handler.go and move
the implementation from api.go to signozalertmanager/handler.go.

Register all alertmanager routes (channels, route policies, alerts)
in signozapiserver via handler.New() with OpenAPIDef. Remove
AlertmanagerAPI injection from http_handler.go.

This enables future AuditDef instrumentation on these routes.

* fix(review): rename param, add /api/v1/channels/test endpoint

- Rename `am` to `alertmanagerService` in NewHandlers
- Add /api/v1/channels/test as the canonical test endpoint
- Mark /api/v1/testChannel as deprecated
- Regenerate OpenAPI spec

* fix(review): use camelCase for channel orgId json tag

* fix(review): remove section comments from alertmanager routes

* fix(review): use routepolicies tag without hyphen

* chore: regenerate frontend API clients for alertmanager routes

* fix: add required/nullable/enum tags to alertmanager OpenAPI types

- PostableRoutePolicy: mark expression, name, channels as required
- GettableRoutePolicy: change CreatedAt/UpdatedAt from pointer to value
- Channel: mark name, type, data, orgId as required
- ExpressionKind: add Enum() for rule/policy values
- Regenerate OpenAPI spec and frontend clients

* fix: use typed response for GetAlerts endpoint

* fix: add Receiver request type to channel mutation endpoints

CreateChannel, UpdateChannelByID, TestChannel, and TestChannelDeprecated
all read req.Body as a Receiver config. The OpenAPIDef must declare
the request type so the generated SDK includes the body parameter.

* fix: change CreateChannel access from EditAccess to AdminAccess

Aligns CreateChannel endpoint with the rest of the channel mutation
endpoints (update/delete) which all require admin access. This is
consistent with the frontend where notifications are not accessible
to editors.
2026-04-15 17:31:43 +00:00
120 changed files with 6882 additions and 455 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,6 @@ import (
"github.com/SigNoz/signoz/ee/licensing/httplicensing"
"github.com/SigNoz/signoz/ee/query-service/usage"
"github.com/SigNoz/signoz/pkg/alertmanager"
"github.com/SigNoz/signoz/pkg/global"
"github.com/SigNoz/signoz/pkg/http/middleware"
baseapp "github.com/SigNoz/signoz/pkg/query-service/app"
@@ -49,7 +48,6 @@ func NewAPIHandler(opts APIHandlerOptions, signoz *signoz.SigNoz, config signoz.
CloudIntegrationsController: opts.CloudIntegrationsController,
LogsParsingPipelineController: opts.LogsParsingPipelineController,
FluxInterval: opts.FluxInterval,
AlertmanagerAPI: alertmanager.NewAPI(signoz.Alertmanager),
LicensingAPI: httplicensing.NewLicensingAPI(signoz.Licensing),
Signoz: signoz,
QueryParserAPI: queryparser.NewAPI(signoz.Instrumentation.ToProviderSettings(), signoz.QueryParser),

View File

@@ -1,5 +1,7 @@
node_modules
build
eslint-rules/
stylelint-rules/
*.typegen.ts
i18-generate-hash.js
src/parser/TraceOperatorParser/**

View File

@@ -1,3 +1,10 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const path = require('path');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const rulesDirPlugin = require('eslint-plugin-rulesdir');
// eslint-rules/ always points to frontend/eslint-rules/ regardless of workspace root.
rulesDirPlugin.RULES_DIR = path.join(__dirname, 'eslint-rules');
/**
* ESLint Configuration for SigNoz Frontend
*/
@@ -32,6 +39,7 @@ module.exports = {
sourceType: 'module',
},
plugins: [
'rulesdir', // Local custom rules
'react', // React-specific rules
'@typescript-eslint', // TypeScript linting
'simple-import-sort', // Auto-sort imports
@@ -56,6 +64,9 @@ module.exports = {
},
},
rules: {
// Asset migration — base-path safety
'rulesdir/no-unsupported-asset-pattern': 'error',
// Code quality rules
'prefer-const': 'error', // Enforces const for variables never reassigned
'no-var': 'error', // Disallows var, enforces let/const

View File

@@ -0,0 +1,9 @@
const path = require('path');
module.exports = {
plugins: [path.join(__dirname, 'stylelint-rules/no-unsupported-asset-url.js')],
customSyntax: 'postcss-scss',
rules: {
'local/no-unsupported-asset-url': true,
},
};

View File

@@ -0,0 +1,390 @@
'use strict';
/**
* ESLint rule: no-unsupported-asset-pattern
*
* Enforces that all asset references (SVG, PNG, etc.) go through Vite's module
* pipeline via ES imports (`import fooUrl from '@/assets/...'`) rather than
* hard-coded strings or public/ paths.
*
* Why this matters: when the app is served from a sub-path (base-path deployment),
* Vite rewrites ES import URLs automatically. String literals and public/ references
* bypass that rewrite and break at runtime.
*
* Covers four AST patterns:
* 1. Literal — plain string: "/Icons/logo.svg"
* 2. TemplateLiteral — template string: `/Icons/${name}.svg`
* 3. BinaryExpression — concatenation: "/Icons/" + name + ".svg"
* 4. ImportDeclaration / ImportExpression — static & dynamic imports
*/
const {
hasAssetExtension,
containsAssetExtension,
extractUrlPath,
isAbsolutePath,
isPublicRelative,
isRelativePublicDir,
isValidAssetImport,
isExternalUrl,
} = require('./shared/asset-patterns');
// Known public/ sub-directories that should never appear in dynamic asset paths.
const PUBLIC_DIR_SEGMENTS = ['/Icons/', '/Images/', '/Logos/', '/svgs/'];
/**
* Recursively extracts the static string parts from a binary `+` expression or
* template literal. Returns `[null]` for any dynamic (non-string) node so
* callers can detect that the prefix became unknowable.
*
* Example: `"/Icons/" + iconName + ".svg"` → ["/Icons/", null, ".svg"]
*/
function collectBinaryStringParts(node) {
if (node.type === 'Literal' && typeof node.value === 'string')
return [node.value];
if (node.type === 'BinaryExpression' && node.operator === '+') {
return [
...collectBinaryStringParts(node.left),
...collectBinaryStringParts(node.right),
];
}
if (node.type === 'TemplateLiteral') {
return node.quasis.map((q) => q.value.raw);
}
// Unknown / dynamic node — signals "prefix is no longer fully static"
return [null];
}
module.exports = {
meta: {
type: 'problem',
docs: {
description:
'Disallow Vite-unsafe asset reference patterns that break runtime base-path deployments',
category: 'Asset Migration',
recommended: true,
},
schema: [],
messages: {
absoluteString:
'Absolute asset path "{{ value }}" is not base-path-safe. ' +
"Use an ES import instead: import fooUrl from '@/assets/...' and reference the variable.",
templateLiteral:
'Dynamic asset path with absolute prefix is not base-path-safe. ' +
"Use new URL('./asset.svg', import.meta.url).href for dynamic asset paths.",
absoluteImport:
'Asset imported via absolute path is not supported. ' +
"Use import fooUrl from '@/assets/...' instead.",
publicImport:
"Assets in public/ bypass Vite's module pipeline — their URLs are not base-path-aware and will break when the app is served from a sub-path (e.g. /app/). " +
"Use an ES import instead: import fooUrl from '@/assets/...' so Vite injects the correct base path.",
relativePublicString:
'Relative public-dir path "{{ value }}" is not base-path-safe. ' +
"Use an ES import instead: import fooUrl from '@/assets/...' and reference the variable.",
invalidAssetImport:
"Asset '{{ src }}' must be imported from src/assets/ using either '@/assets/...' " +
'or a relative path into src/assets/.',
},
},
create(context) {
return {
/**
* Catches plain string literals used as asset paths, e.g.:
* src="/Icons/logo.svg" or url("../public/Images/bg.png")
*
* Import declaration sources are skipped here — handled by ImportDeclaration.
* Also unwraps CSS `url(...)` wrappers before checking.
*/
Literal(node) {
if (node.parent && node.parent.type === 'ImportDeclaration') {
return;
}
const value = node.value;
if (typeof value !== 'string') return;
if (isExternalUrl(value)) return;
if (isAbsolutePath(value) && containsAssetExtension(value)) {
context.report({
node,
messageId: 'absoluteString',
data: { value },
});
return;
}
if (isRelativePublicDir(value) && containsAssetExtension(value)) {
context.report({
node,
messageId: 'relativePublicString',
data: { value },
});
return;
}
// Catches relative paths that start with "public/" e.g. 'public/Logos/aws-dark.svg'.
// isRelativePublicDir only covers known sub-dirs (Icons/, Logos/, etc.),
// so this handles the case where the full "public/" prefix is written explicitly.
if (isPublicRelative(value) && containsAssetExtension(value)) {
context.report({
node,
messageId: 'relativePublicString',
data: { value },
});
return;
}
// Also check the path inside a CSS url("...") wrapper
const urlPath = extractUrlPath(value);
if (urlPath && isExternalUrl(urlPath)) return;
if (urlPath && isAbsolutePath(urlPath) && containsAssetExtension(urlPath)) {
context.report({
node,
messageId: 'absoluteString',
data: { value: urlPath },
});
return;
}
if (
urlPath &&
isRelativePublicDir(urlPath) &&
containsAssetExtension(urlPath)
) {
context.report({
node,
messageId: 'relativePublicString',
data: { value: urlPath },
});
return;
}
if (
urlPath &&
isPublicRelative(urlPath) &&
containsAssetExtension(urlPath)
) {
context.report({
node,
messageId: 'relativePublicString',
data: { value: urlPath },
});
}
},
/**
* Catches template literals used as asset paths, e.g.:
* `/Icons/${name}.svg`
* `url('/Images/${bg}.png')`
*/
TemplateLiteral(node) {
const quasis = node.quasis;
if (!quasis || quasis.length === 0) return;
const firstQuasi = quasis[0].value.raw;
if (isExternalUrl(firstQuasi)) return;
const hasAssetExt = quasis.some((q) => containsAssetExtension(q.value.raw));
if (isAbsolutePath(firstQuasi) && hasAssetExt) {
context.report({
node,
messageId: 'templateLiteral',
});
return;
}
if (isRelativePublicDir(firstQuasi) && hasAssetExt) {
context.report({
node,
messageId: 'relativePublicString',
data: { value: firstQuasi },
});
return;
}
// Expression-first template with known public-dir segment: `${base}/Icons/foo.svg`
const hasPublicSegment = quasis.some((q) =>
PUBLIC_DIR_SEGMENTS.some((seg) => q.value.raw.includes(seg)),
);
if (hasPublicSegment && hasAssetExt) {
context.report({
node,
messageId: 'templateLiteral',
});
return;
}
// No-interpolation template (single quasi): treat like a plain string
// and also unwrap any css url(...) wrapper.
if (quasis.length === 1) {
// Check the raw string first (no url() wrapper)
if (isPublicRelative(firstQuasi) && hasAssetExt) {
context.report({
node,
messageId: 'relativePublicString',
data: { value: firstQuasi },
});
return;
}
const urlPath = extractUrlPath(firstQuasi);
if (urlPath && isExternalUrl(urlPath)) return;
if (urlPath && isAbsolutePath(urlPath) && hasAssetExtension(urlPath)) {
context.report({
node,
messageId: 'templateLiteral',
});
return;
}
if (
urlPath &&
isRelativePublicDir(urlPath) &&
hasAssetExtension(urlPath)
) {
context.report({
node,
messageId: 'relativePublicString',
data: { value: urlPath },
});
return;
}
if (urlPath && isPublicRelative(urlPath) && hasAssetExtension(urlPath)) {
context.report({
node,
messageId: 'relativePublicString',
data: { value: urlPath },
});
}
return;
}
// CSS url() with an absolute path inside a multi-quasi template, e.g.:
// `url('/Icons/${name}.svg')`
if (firstQuasi.includes('url(') && hasAssetExt) {
const urlMatch = firstQuasi.match(/^url\(\s*['"]?\//);
if (urlMatch) {
context.report({
node,
messageId: 'templateLiteral',
});
}
}
},
/**
* Catches string concatenation used to build asset paths, e.g.:
* "/Icons/" + name + ".svg"
*
* Collects the leading static parts (before the first dynamic value)
* to determine the path prefix. If any part carries a known asset
* extension, the expression is flagged.
*/
BinaryExpression(node) {
if (node.operator !== '+') return;
const parts = collectBinaryStringParts(node);
// Collect only the leading static parts; stop at the first dynamic (null) part
const prefixParts = [];
for (const part of parts) {
if (part === null) break;
prefixParts.push(part);
}
const staticPrefix = prefixParts.join('');
if (isExternalUrl(staticPrefix)) return;
const hasExt = parts.some(
(part) => part !== null && containsAssetExtension(part),
);
if (isAbsolutePath(staticPrefix) && hasExt) {
context.report({
node,
messageId: 'templateLiteral',
});
return;
}
if (isPublicRelative(staticPrefix) && hasExt) {
context.report({
node,
messageId: 'relativePublicString',
data: { value: staticPrefix },
});
return;
}
if (isRelativePublicDir(staticPrefix) && hasExt) {
context.report({
node,
messageId: 'relativePublicString',
data: { value: staticPrefix },
});
}
},
/**
* Catches static asset imports that don't go through src/assets/, e.g.:
* import logo from '/public/Icons/logo.svg' ← absolute path
* import logo from '../../public/logo.svg' ← relative into public/
* import logo from '../somewhere/logo.svg' ← outside src/assets/
*
* Valid pattern: import fooUrl from '@/assets/...' or relative within src/assets/
*/
ImportDeclaration(node) {
const src = node.source.value;
if (typeof src !== 'string') return;
if (!hasAssetExtension(src)) return;
if (isAbsolutePath(src)) {
context.report({ node, messageId: 'absoluteImport' });
return;
}
if (isPublicRelative(src)) {
context.report({ node, messageId: 'publicImport' });
return;
}
if (!isValidAssetImport(src)) {
context.report({
node,
messageId: 'invalidAssetImport',
data: { src },
});
}
},
/**
* Same checks as ImportDeclaration but for dynamic imports:
* const logo = await import('/Icons/logo.svg')
*
* Only literal sources are checked; fully dynamic expressions are ignored
* since their paths cannot be statically analysed.
*/
ImportExpression(node) {
const src = node.source;
if (!src || src.type !== 'Literal' || typeof src.value !== 'string') return;
const value = src.value;
if (!hasAssetExtension(value)) return;
if (isAbsolutePath(value)) {
context.report({ node, messageId: 'absoluteImport' });
return;
}
if (isPublicRelative(value)) {
context.report({ node, messageId: 'publicImport' });
return;
}
if (!isValidAssetImport(value)) {
context.report({
node,
messageId: 'invalidAssetImport',
data: { src: value },
});
}
},
};
},
};

View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

View File

@@ -0,0 +1,121 @@
'use strict';
const ALLOWED_ASSET_EXTENSIONS = [
'.svg',
'.png',
'.webp',
'.jpg',
'.jpeg',
'.gif',
];
/**
* Returns true if the string ends with an asset extension.
* e.g. "/Icons/foo.svg" → true, "/Icons/foo.svg.bak" → false
*/
function hasAssetExtension(str) {
if (typeof str !== 'string') return false;
return ALLOWED_ASSET_EXTENSIONS.some((ext) => str.endsWith(ext));
}
// Like hasAssetExtension but also matches mid-string with boundary check,
// e.g. "/foo.svg?v=1" → true, "/icons.svg-dir/" → true (- is non-alphanumeric boundary)
function containsAssetExtension(str) {
if (typeof str !== 'string') return false;
return ALLOWED_ASSET_EXTENSIONS.some((ext) => {
const idx = str.indexOf(ext);
if (idx === -1) return false;
const afterIdx = idx + ext.length;
// Broad boundary (any non-alphanumeric) is intentional — the real guard against
// false positives is the upstream conditions (isAbsolutePath, isRelativePublicDir, etc.)
// that must pass before this is reached. "/icons.svg-dir/" → true (- is a boundary).
return afterIdx >= str.length || /[^a-zA-Z0-9]/.test(str[afterIdx]);
});
}
/**
* Extracts the asset path from a CSS url() wrapper.
* Handles single quotes, double quotes, unquoted, and whitespace variations.
* e.g.
* "url('/Icons/foo.svg')" → "/Icons/foo.svg"
* "url( '../assets/bg.png' )" → "../assets/bg.png"
* "url(/Icons/foo.svg)" → "/Icons/foo.svg"
* Returns null if the string is not a url() wrapper.
*/
function extractUrlPath(str) {
if (typeof str !== 'string') return null;
// Match url( [whitespace] [quote?] path [quote?] [whitespace] )
// Capture group: [^'")\s]+ matches path until quote, closing paren, or whitespace
const match = str.match(/^url\(\s*['"]?([^'")\s]+)['"]?\s*\)$/);
return match ? match[1] : null;
}
/**
* Returns true if the string is an absolute path (starts with /).
* Absolute paths in url() bypass <base href> and fail under any URL prefix.
*/
function isAbsolutePath(str) {
if (typeof str !== 'string') return false;
return str.startsWith('/') && !str.startsWith('//');
}
/**
* Returns true if the path imports from the public/ directory.
* Relative imports into public/ cause asset duplication in dist/.
*/
function isPublicRelative(str) {
if (typeof str !== 'string') return false;
return str.includes('/public/') || str.startsWith('public/');
}
/**
* Returns true if the string is a relative reference into a known public-dir folder.
* e.g. "Icons/foo.svg", `Logos/aws-dark.svg`, "Images/bg.png"
* These bypass Vite's module pipeline even without a leading slash.
*/
const PUBLIC_DIR_SEGMENTS = ['Icons/', 'Images/', 'Logos/', 'svgs/'];
function isRelativePublicDir(str) {
if (typeof str !== 'string') return false;
return PUBLIC_DIR_SEGMENTS.some((seg) => str.startsWith(seg));
}
/**
* Returns true if an asset import path is valid (goes through Vite's module pipeline).
* Valid: @/assets/..., any relative path containing /assets/, or node_modules packages.
* Invalid: absolute paths, public/ dir, or relative paths outside src/assets/.
*/
function isValidAssetImport(str) {
if (typeof str !== 'string') return false;
if (str.startsWith('@/assets/')) return true;
if (str.includes('/assets/')) return true;
// Not starting with . or / means it's a node_modules package — always valid
if (!str.startsWith('.') && !str.startsWith('/')) return true;
return false;
}
/**
* Returns true if the string is an external URL.
* Used to avoid false positives on CDN/API URLs with asset extensions.
*/
function isExternalUrl(str) {
if (typeof str !== 'string') return false;
return (
str.startsWith('http://') ||
str.startsWith('https://') ||
str.startsWith('//')
);
}
module.exports = {
ALLOWED_ASSET_EXTENSIONS,
PUBLIC_DIR_SEGMENTS,
hasAssetExtension,
containsAssetExtension,
extractUrlPath,
isAbsolutePath,
isPublicRelative,
isRelativePublicDir,
isValidAssetImport,
isExternalUrl,
};

View File

@@ -10,9 +10,10 @@
"preview": "vite preview",
"prettify": "prettier --write .",
"fmt": "prettier --check .",
"lint": "eslint ./src",
"lint": "eslint ./src && stylelint \"src/**/*.scss\"",
"lint:generated": "eslint ./src/api/generated --fix",
"lint:fix": "eslint ./src --fix",
"lint:styles": "stylelint \"src/**/*.scss\"",
"jest": "jest",
"jest:coverage": "jest --coverage",
"jest:watch": "jest --watch",
@@ -229,6 +230,7 @@
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.3.0",
"eslint-plugin-rulesdir": "0.2.2",
"eslint-plugin-simple-import-sort": "^7.0.0",
"eslint-plugin-sonarjs": "^0.12.0",
"husky": "^7.0.4",
@@ -244,6 +246,7 @@
"orval": "7.18.0",
"portfinder-sync": "^0.0.2",
"postcss": "8.5.6",
"postcss-scss": "4.0.9",
"prettier": "2.2.1",
"prop-types": "15.8.1",
"react-hooks-testing-library": "0.6.0",
@@ -251,6 +254,8 @@
"redux-mock-store": "1.5.4",
"sass": "1.97.3",
"sharp": "0.34.5",
"stylelint": "17.7.0",
"stylelint-scss": "7.0.0",
"svgo": "4.0.0",
"ts-api-utils": "2.4.0",
"ts-jest": "29.4.6",

View File

@@ -0,0 +1,97 @@
/**
* ! Do not edit manually
* * The file has been auto-generated using Orval for SigNoz
* * regenerate with 'yarn generate:api'
* SigNoz
*/
import type {
InvalidateOptions,
QueryClient,
QueryFunction,
QueryKey,
UseQueryOptions,
UseQueryResult,
} from 'react-query';
import { useQuery } from 'react-query';
import type { ErrorType } from '../../../generatedAPIInstance';
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type { GetAlerts200, RenderErrorResponseDTO } from '../sigNoz.schemas';
/**
* This endpoint returns alerts for the organization
* @summary Get alerts
*/
export const getAlerts = (signal?: AbortSignal) => {
return GeneratedAPIInstance<GetAlerts200>({
url: `/api/v1/alerts`,
method: 'GET',
signal,
});
};
export const getGetAlertsQueryKey = () => {
return [`/api/v1/alerts`] as const;
};
export const getGetAlertsQueryOptions = <
TData = Awaited<ReturnType<typeof getAlerts>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof getAlerts>>, TError, TData>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getGetAlertsQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof getAlerts>>> = ({
signal,
}) => getAlerts(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getAlerts>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetAlertsQueryResult = NonNullable<
Awaited<ReturnType<typeof getAlerts>>
>;
export type GetAlertsQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get alerts
*/
export function useGetAlerts<
TData = Awaited<ReturnType<typeof getAlerts>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<Awaited<ReturnType<typeof getAlerts>>, TError, TData>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetAlertsQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get alerts
*/
export const invalidateGetAlerts = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetAlertsQueryKey() },
options,
);
return queryClient;
};

View File

@@ -0,0 +1,646 @@
/**
* ! Do not edit manually
* * The file has been auto-generated using Orval for SigNoz
* * regenerate with 'yarn generate:api'
* SigNoz
*/
import type {
InvalidateOptions,
MutationFunction,
QueryClient,
QueryFunction,
QueryKey,
UseMutationOptions,
UseMutationResult,
UseQueryOptions,
UseQueryResult,
} from 'react-query';
import { useMutation, useQuery } from 'react-query';
import type { BodyType, ErrorType } from '../../../generatedAPIInstance';
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type {
ConfigReceiverDTO,
CreateChannel201,
DeleteChannelByIDPathParameters,
GetChannelByID200,
GetChannelByIDPathParameters,
ListChannels200,
RenderErrorResponseDTO,
UpdateChannelByIDPathParameters,
} from '../sigNoz.schemas';
/**
* This endpoint lists all notification channels for the organization
* @summary List notification channels
*/
export const listChannels = (signal?: AbortSignal) => {
return GeneratedAPIInstance<ListChannels200>({
url: `/api/v1/channels`,
method: 'GET',
signal,
});
};
export const getListChannelsQueryKey = () => {
return [`/api/v1/channels`] as const;
};
export const getListChannelsQueryOptions = <
TData = Awaited<ReturnType<typeof listChannels>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listChannels>>,
TError,
TData
>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getListChannelsQueryKey();
const queryFn: QueryFunction<Awaited<ReturnType<typeof listChannels>>> = ({
signal,
}) => listChannels(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof listChannels>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ListChannelsQueryResult = NonNullable<
Awaited<ReturnType<typeof listChannels>>
>;
export type ListChannelsQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary List notification channels
*/
export function useListChannels<
TData = Awaited<ReturnType<typeof listChannels>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listChannels>>,
TError,
TData
>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getListChannelsQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary List notification channels
*/
export const invalidateListChannels = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getListChannelsQueryKey() },
options,
);
return queryClient;
};
/**
* This endpoint creates a notification channel
* @summary Create notification channel
*/
export const createChannel = (
configReceiverDTO: BodyType<ConfigReceiverDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<CreateChannel201>({
url: `/api/v1/channels`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: configReceiverDTO,
signal,
});
};
export const getCreateChannelMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createChannel>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof createChannel>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
> => {
const mutationKey = ['createChannel'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof createChannel>>,
{ data: BodyType<ConfigReceiverDTO> }
> = (props) => {
const { data } = props ?? {};
return createChannel(data);
};
return { mutationFn, ...mutationOptions };
};
export type CreateChannelMutationResult = NonNullable<
Awaited<ReturnType<typeof createChannel>>
>;
export type CreateChannelMutationBody = BodyType<ConfigReceiverDTO>;
export type CreateChannelMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Create notification channel
*/
export const useCreateChannel = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createChannel>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof createChannel>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
> => {
const mutationOptions = getCreateChannelMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* This endpoint deletes a notification channel by ID
* @summary Delete notification channel
*/
export const deleteChannelByID = ({ id }: DeleteChannelByIDPathParameters) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/channels/${id}`,
method: 'DELETE',
});
};
export const getDeleteChannelByIDMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteChannelByID>>,
TError,
{ pathParams: DeleteChannelByIDPathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof deleteChannelByID>>,
TError,
{ pathParams: DeleteChannelByIDPathParameters },
TContext
> => {
const mutationKey = ['deleteChannelByID'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof deleteChannelByID>>,
{ pathParams: DeleteChannelByIDPathParameters }
> = (props) => {
const { pathParams } = props ?? {};
return deleteChannelByID(pathParams);
};
return { mutationFn, ...mutationOptions };
};
export type DeleteChannelByIDMutationResult = NonNullable<
Awaited<ReturnType<typeof deleteChannelByID>>
>;
export type DeleteChannelByIDMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Delete notification channel
*/
export const useDeleteChannelByID = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteChannelByID>>,
TError,
{ pathParams: DeleteChannelByIDPathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof deleteChannelByID>>,
TError,
{ pathParams: DeleteChannelByIDPathParameters },
TContext
> => {
const mutationOptions = getDeleteChannelByIDMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* This endpoint returns a notification channel by ID
* @summary Get notification channel by ID
*/
export const getChannelByID = (
{ id }: GetChannelByIDPathParameters,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetChannelByID200>({
url: `/api/v1/channels/${id}`,
method: 'GET',
signal,
});
};
export const getGetChannelByIDQueryKey = ({
id,
}: GetChannelByIDPathParameters) => {
return [`/api/v1/channels/${id}`] as const;
};
export const getGetChannelByIDQueryOptions = <
TData = Awaited<ReturnType<typeof getChannelByID>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetChannelByIDPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getChannelByID>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getGetChannelByIDQueryKey({ id });
const queryFn: QueryFunction<Awaited<ReturnType<typeof getChannelByID>>> = ({
signal,
}) => getChannelByID({ id }, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getChannelByID>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetChannelByIDQueryResult = NonNullable<
Awaited<ReturnType<typeof getChannelByID>>
>;
export type GetChannelByIDQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get notification channel by ID
*/
export function useGetChannelByID<
TData = Awaited<ReturnType<typeof getChannelByID>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetChannelByIDPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getChannelByID>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetChannelByIDQueryOptions({ id }, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get notification channel by ID
*/
export const invalidateGetChannelByID = async (
queryClient: QueryClient,
{ id }: GetChannelByIDPathParameters,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetChannelByIDQueryKey({ id }) },
options,
);
return queryClient;
};
/**
* This endpoint updates a notification channel by ID
* @summary Update notification channel
*/
export const updateChannelByID = (
{ id }: UpdateChannelByIDPathParameters,
configReceiverDTO: BodyType<ConfigReceiverDTO>,
) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/channels/${id}`,
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
data: configReceiverDTO,
});
};
export const getUpdateChannelByIDMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateChannelByID>>,
TError,
{
pathParams: UpdateChannelByIDPathParameters;
data: BodyType<ConfigReceiverDTO>;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof updateChannelByID>>,
TError,
{
pathParams: UpdateChannelByIDPathParameters;
data: BodyType<ConfigReceiverDTO>;
},
TContext
> => {
const mutationKey = ['updateChannelByID'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof updateChannelByID>>,
{
pathParams: UpdateChannelByIDPathParameters;
data: BodyType<ConfigReceiverDTO>;
}
> = (props) => {
const { pathParams, data } = props ?? {};
return updateChannelByID(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type UpdateChannelByIDMutationResult = NonNullable<
Awaited<ReturnType<typeof updateChannelByID>>
>;
export type UpdateChannelByIDMutationBody = BodyType<ConfigReceiverDTO>;
export type UpdateChannelByIDMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Update notification channel
*/
export const useUpdateChannelByID = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateChannelByID>>,
TError,
{
pathParams: UpdateChannelByIDPathParameters;
data: BodyType<ConfigReceiverDTO>;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof updateChannelByID>>,
TError,
{
pathParams: UpdateChannelByIDPathParameters;
data: BodyType<ConfigReceiverDTO>;
},
TContext
> => {
const mutationOptions = getUpdateChannelByIDMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* This endpoint tests a notification channel by sending a test notification
* @summary Test notification channel
*/
export const testChannel = (
configReceiverDTO: BodyType<ConfigReceiverDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/channels/test`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: configReceiverDTO,
signal,
});
};
export const getTestChannelMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof testChannel>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof testChannel>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
> => {
const mutationKey = ['testChannel'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof testChannel>>,
{ data: BodyType<ConfigReceiverDTO> }
> = (props) => {
const { data } = props ?? {};
return testChannel(data);
};
return { mutationFn, ...mutationOptions };
};
export type TestChannelMutationResult = NonNullable<
Awaited<ReturnType<typeof testChannel>>
>;
export type TestChannelMutationBody = BodyType<ConfigReceiverDTO>;
export type TestChannelMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Test notification channel
*/
export const useTestChannel = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof testChannel>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof testChannel>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
> => {
const mutationOptions = getTestChannelMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Deprecated: use /api/v1/channels/test instead
* @deprecated
* @summary Test notification channel (deprecated)
*/
export const testChannelDeprecated = (
configReceiverDTO: BodyType<ConfigReceiverDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/testChannel`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: configReceiverDTO,
signal,
});
};
export const getTestChannelDeprecatedMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof testChannelDeprecated>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof testChannelDeprecated>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
> => {
const mutationKey = ['testChannelDeprecated'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof testChannelDeprecated>>,
{ data: BodyType<ConfigReceiverDTO> }
> = (props) => {
const { data } = props ?? {};
return testChannelDeprecated(data);
};
return { mutationFn, ...mutationOptions };
};
export type TestChannelDeprecatedMutationResult = NonNullable<
Awaited<ReturnType<typeof testChannelDeprecated>>
>;
export type TestChannelDeprecatedMutationBody = BodyType<ConfigReceiverDTO>;
export type TestChannelDeprecatedMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @deprecated
* @summary Test notification channel (deprecated)
*/
export const useTestChannelDeprecated = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof testChannelDeprecated>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof testChannelDeprecated>>,
TError,
{ data: BodyType<ConfigReceiverDTO> },
TContext
> => {
const mutationOptions = getTestChannelDeprecatedMutationOptions(options);
return useMutation(mutationOptions);
};

View File

@@ -0,0 +1,482 @@
/**
* ! Do not edit manually
* * The file has been auto-generated using Orval for SigNoz
* * regenerate with 'yarn generate:api'
* SigNoz
*/
import type {
InvalidateOptions,
MutationFunction,
QueryClient,
QueryFunction,
QueryKey,
UseMutationOptions,
UseMutationResult,
UseQueryOptions,
UseQueryResult,
} from 'react-query';
import { useMutation, useQuery } from 'react-query';
import type { BodyType, ErrorType } from '../../../generatedAPIInstance';
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type {
AlertmanagertypesPostableRoutePolicyDTO,
CreateRoutePolicy201,
DeleteRoutePolicyByIDPathParameters,
GetAllRoutePolicies200,
GetRoutePolicyByID200,
GetRoutePolicyByIDPathParameters,
RenderErrorResponseDTO,
UpdateRoutePolicy200,
UpdateRoutePolicyPathParameters,
} from '../sigNoz.schemas';
/**
* This endpoint lists all route policies for the organization
* @summary List route policies
*/
export const getAllRoutePolicies = (signal?: AbortSignal) => {
return GeneratedAPIInstance<GetAllRoutePolicies200>({
url: `/api/v1/route_policies`,
method: 'GET',
signal,
});
};
export const getGetAllRoutePoliciesQueryKey = () => {
return [`/api/v1/route_policies`] as const;
};
export const getGetAllRoutePoliciesQueryOptions = <
TData = Awaited<ReturnType<typeof getAllRoutePolicies>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getAllRoutePolicies>>,
TError,
TData
>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey = queryOptions?.queryKey ?? getGetAllRoutePoliciesQueryKey();
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getAllRoutePolicies>>
> = ({ signal }) => getAllRoutePolicies(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof getAllRoutePolicies>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetAllRoutePoliciesQueryResult = NonNullable<
Awaited<ReturnType<typeof getAllRoutePolicies>>
>;
export type GetAllRoutePoliciesQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary List route policies
*/
export function useGetAllRoutePolicies<
TData = Awaited<ReturnType<typeof getAllRoutePolicies>>,
TError = ErrorType<RenderErrorResponseDTO>
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getAllRoutePolicies>>,
TError,
TData
>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetAllRoutePoliciesQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary List route policies
*/
export const invalidateGetAllRoutePolicies = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetAllRoutePoliciesQueryKey() },
options,
);
return queryClient;
};
/**
* This endpoint creates a route policy
* @summary Create route policy
*/
export const createRoutePolicy = (
alertmanagertypesPostableRoutePolicyDTO: BodyType<AlertmanagertypesPostableRoutePolicyDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<CreateRoutePolicy201>({
url: `/api/v1/route_policies`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: alertmanagertypesPostableRoutePolicyDTO,
signal,
});
};
export const getCreateRoutePolicyMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createRoutePolicy>>,
TError,
{ data: BodyType<AlertmanagertypesPostableRoutePolicyDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof createRoutePolicy>>,
TError,
{ data: BodyType<AlertmanagertypesPostableRoutePolicyDTO> },
TContext
> => {
const mutationKey = ['createRoutePolicy'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof createRoutePolicy>>,
{ data: BodyType<AlertmanagertypesPostableRoutePolicyDTO> }
> = (props) => {
const { data } = props ?? {};
return createRoutePolicy(data);
};
return { mutationFn, ...mutationOptions };
};
export type CreateRoutePolicyMutationResult = NonNullable<
Awaited<ReturnType<typeof createRoutePolicy>>
>;
export type CreateRoutePolicyMutationBody = BodyType<AlertmanagertypesPostableRoutePolicyDTO>;
export type CreateRoutePolicyMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Create route policy
*/
export const useCreateRoutePolicy = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createRoutePolicy>>,
TError,
{ data: BodyType<AlertmanagertypesPostableRoutePolicyDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof createRoutePolicy>>,
TError,
{ data: BodyType<AlertmanagertypesPostableRoutePolicyDTO> },
TContext
> => {
const mutationOptions = getCreateRoutePolicyMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* This endpoint deletes a route policy by ID
* @summary Delete route policy
*/
export const deleteRoutePolicyByID = ({
id,
}: DeleteRoutePolicyByIDPathParameters) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/route_policies/${id}`,
method: 'DELETE',
});
};
export const getDeleteRoutePolicyByIDMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteRoutePolicyByID>>,
TError,
{ pathParams: DeleteRoutePolicyByIDPathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof deleteRoutePolicyByID>>,
TError,
{ pathParams: DeleteRoutePolicyByIDPathParameters },
TContext
> => {
const mutationKey = ['deleteRoutePolicyByID'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof deleteRoutePolicyByID>>,
{ pathParams: DeleteRoutePolicyByIDPathParameters }
> = (props) => {
const { pathParams } = props ?? {};
return deleteRoutePolicyByID(pathParams);
};
return { mutationFn, ...mutationOptions };
};
export type DeleteRoutePolicyByIDMutationResult = NonNullable<
Awaited<ReturnType<typeof deleteRoutePolicyByID>>
>;
export type DeleteRoutePolicyByIDMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Delete route policy
*/
export const useDeleteRoutePolicyByID = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteRoutePolicyByID>>,
TError,
{ pathParams: DeleteRoutePolicyByIDPathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof deleteRoutePolicyByID>>,
TError,
{ pathParams: DeleteRoutePolicyByIDPathParameters },
TContext
> => {
const mutationOptions = getDeleteRoutePolicyByIDMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* This endpoint returns a route policy by ID
* @summary Get route policy by ID
*/
export const getRoutePolicyByID = (
{ id }: GetRoutePolicyByIDPathParameters,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<GetRoutePolicyByID200>({
url: `/api/v1/route_policies/${id}`,
method: 'GET',
signal,
});
};
export const getGetRoutePolicyByIDQueryKey = ({
id,
}: GetRoutePolicyByIDPathParameters) => {
return [`/api/v1/route_policies/${id}`] as const;
};
export const getGetRoutePolicyByIDQueryOptions = <
TData = Awaited<ReturnType<typeof getRoutePolicyByID>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRoutePolicyByIDPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRoutePolicyByID>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getGetRoutePolicyByIDQueryKey({ id });
const queryFn: QueryFunction<
Awaited<ReturnType<typeof getRoutePolicyByID>>
> = ({ signal }) => getRoutePolicyByID({ id }, signal);
return {
queryKey,
queryFn,
enabled: !!id,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof getRoutePolicyByID>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type GetRoutePolicyByIDQueryResult = NonNullable<
Awaited<ReturnType<typeof getRoutePolicyByID>>
>;
export type GetRoutePolicyByIDQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Get route policy by ID
*/
export function useGetRoutePolicyByID<
TData = Awaited<ReturnType<typeof getRoutePolicyByID>>,
TError = ErrorType<RenderErrorResponseDTO>
>(
{ id }: GetRoutePolicyByIDPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof getRoutePolicyByID>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getGetRoutePolicyByIDQueryOptions({ id }, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary Get route policy by ID
*/
export const invalidateGetRoutePolicyByID = async (
queryClient: QueryClient,
{ id }: GetRoutePolicyByIDPathParameters,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getGetRoutePolicyByIDQueryKey({ id }) },
options,
);
return queryClient;
};
/**
* This endpoint updates a route policy by ID
* @summary Update route policy
*/
export const updateRoutePolicy = (
{ id }: UpdateRoutePolicyPathParameters,
alertmanagertypesPostableRoutePolicyDTO: BodyType<AlertmanagertypesPostableRoutePolicyDTO>,
) => {
return GeneratedAPIInstance<UpdateRoutePolicy200>({
url: `/api/v1/route_policies/${id}`,
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
data: alertmanagertypesPostableRoutePolicyDTO,
});
};
export const getUpdateRoutePolicyMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateRoutePolicy>>,
TError,
{
pathParams: UpdateRoutePolicyPathParameters;
data: BodyType<AlertmanagertypesPostableRoutePolicyDTO>;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof updateRoutePolicy>>,
TError,
{
pathParams: UpdateRoutePolicyPathParameters;
data: BodyType<AlertmanagertypesPostableRoutePolicyDTO>;
},
TContext
> => {
const mutationKey = ['updateRoutePolicy'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof updateRoutePolicy>>,
{
pathParams: UpdateRoutePolicyPathParameters;
data: BodyType<AlertmanagertypesPostableRoutePolicyDTO>;
}
> = (props) => {
const { pathParams, data } = props ?? {};
return updateRoutePolicy(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type UpdateRoutePolicyMutationResult = NonNullable<
Awaited<ReturnType<typeof updateRoutePolicy>>
>;
export type UpdateRoutePolicyMutationBody = BodyType<AlertmanagertypesPostableRoutePolicyDTO>;
export type UpdateRoutePolicyMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Update route policy
*/
export const useUpdateRoutePolicy = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateRoutePolicy>>,
TError,
{
pathParams: UpdateRoutePolicyPathParameters;
data: BodyType<AlertmanagertypesPostableRoutePolicyDTO>;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof updateRoutePolicy>>,
TError,
{
pathParams: UpdateRoutePolicyPathParameters;
data: BodyType<AlertmanagertypesPostableRoutePolicyDTO>;
},
TContext
> => {
const mutationOptions = getUpdateRoutePolicyMutationOptions(options);
return useMutation(mutationOptions);
};

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,5 @@
import notFound404Url from '@/assets/Images/notFound404.png';
function NotFound(): JSX.Element {
return (
<img
@@ -5,7 +7,7 @@ function NotFound(): JSX.Element {
maxHeight: 480,
maxWidth: 480,
}}
src="/Images/notFound404.png"
src={notFound404Url}
alt="not-found"
/>
);

View File

@@ -3,6 +3,8 @@ import get from 'api/browser/localstorage/get';
import { LOCALSTORAGE } from 'constants/localStorage';
import { THEME_MODE } from 'hooks/useDarkMode/constant';
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
import './AppLoading.styles.scss';
function AppLoading(): JSX.Element {
@@ -24,11 +26,7 @@ function AppLoading(): JSX.Element {
<div className="perilin-bg" />
<div className="app-loading-content">
<div className="brand">
<img
src="/Logos/signoz-brand-logo.svg"
alt="SigNoz"
className="brand-logo"
/>
<img src={signozBrandLogoUrl} alt="SigNoz" className="brand-logo" />
<Typography.Title level={2} className="brand-title">
SigNoz

View File

@@ -47,7 +47,7 @@ describe('AppLoading', () => {
// Check for brand logo
const logo = screen.getByAltText(SIGNOZ_TEXT);
expect(logo).toBeInTheDocument();
expect(logo).toHaveAttribute('src', '/Logos/signoz-brand-logo.svg');
expect(logo).toHaveAttribute('src', 'test-file-stub');
// Check for brand title
const title = screen.getByText(SIGNOZ_TEXT);

View File

@@ -2,6 +2,8 @@ import { useCallback } from 'react';
import { Button } from '@signozhq/button';
import { LifeBuoy } from 'lucide-react';
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
import './AuthHeader.styles.scss';
function AuthHeader(): JSX.Element {
@@ -13,7 +15,7 @@ function AuthHeader(): JSX.Element {
<header className="auth-header">
<div className="auth-header-logo">
<img
src="/Logos/signoz-brand-logo.svg"
src={signozBrandLogoUrl}
alt="SigNoz"
className="auth-header-logo-icon"
/>

View File

@@ -34,7 +34,7 @@ const mockChangelog: ChangelogSchema = {
id: 1,
documentId: 'doc1',
ext: '.webp',
url: '/uploads/feature1.webp',
url: 'assets/uploads/feature1.webp',
mime: 'image/webp',
alternativeText: null,
},

View File

@@ -1,6 +1,9 @@
import { useTranslation } from 'react-i18next';
import { Space, Typography } from 'antd';
import broomUrl from '@/assets/Icons/broom.svg';
import infraContainersUrl from '@/assets/Icons/infraContainers.svg';
import WaitlistFragment from '../WaitlistFragment/WaitlistFragment';
import './Containers.styles.scss';
@@ -16,7 +19,7 @@ function Containers(): JSX.Element {
<div className="dev-status-container">
<div className="infra-container-card">
<img
src="/Icons/infraContainers.svg"
src={infraContainersUrl}
alt="infra-container"
width={32}
height={32}
@@ -29,7 +32,7 @@ function Containers(): JSX.Element {
<div className="infra-container-working-msg">
<Space>
<img src="/Icons/broom.svg" alt="broom" width={24} height={24} />
<img src={broomUrl} alt="broom" width={24} height={24} />
<Text className="infra-container-card-text">{t('working_message')}</Text>
</Space>
</div>

View File

@@ -1,6 +1,9 @@
import { useTranslation } from 'react-i18next';
import { Space, Typography } from 'antd';
import broomUrl from '@/assets/Icons/broom.svg';
import infraContainersUrl from '@/assets/Icons/infraContainers.svg';
import WaitlistFragment from '../WaitlistFragment/WaitlistFragment';
import './Processes.styles.scss';
@@ -16,7 +19,7 @@ function Processes(): JSX.Element {
<div className="dev-status-container">
<div className="infra-container-card">
<img
src="/Icons/infraContainers.svg"
src={infraContainersUrl}
alt="infra-container"
width={32}
height={32}
@@ -28,7 +31,7 @@ function Processes(): JSX.Element {
<div className="infra-container-working-msg">
<Space>
<img src="/Icons/broom.svg" alt="broom" width={24} height={24} />
<img src={broomUrl} alt="broom" width={24} height={24} />
<Text className="infra-container-card-text">{t('working_message')}</Text>
</Space>
</div>

View File

@@ -101,7 +101,7 @@ exports[`Not Found page test should render Not Found page without errors 1`] = `
>
<img
alt="not-found"
src="/Images/notFound404.png"
src="test-file-stub"
style="max-height: 480px; max-width: 480px;"
/>
<div

View File

@@ -1,16 +1,14 @@
import { Typography } from 'antd';
import loadingPlaneUrl from '@/assets/Icons/loading-plane.gif';
import './PanelDataLoading.styles.scss';
export function PanelDataLoading(): JSX.Element {
return (
<div className="loading-panel-data">
<div className="loading-panel-data-content">
<img
className="loading-gif"
src="/Icons/loading-plane.gif"
alt="wait-icon"
/>
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
<Typography.Text>Fetching data...</Typography.Text>
</div>

View File

@@ -2,6 +2,8 @@ import { ReactChild } from 'react';
import { useTranslation } from 'react-i18next';
import { Card, Space, Typography } from 'antd';
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
import { Container, LeftContainer, Logo } from './styles';
const { Title } = Typography;
@@ -16,7 +18,7 @@ function WelcomeLeftContainer({
<Container>
<LeftContainer direction="vertical">
<Space align="center">
<Logo src="/Logos/signoz-brand-logo.svg" alt="logo" />
<Logo src={signozBrandLogoUrl} alt="logo" />
<Title style={{ fontSize: '46px', margin: 0 }}>SigNoz</Title>
</Space>
<Typography>{t('monitor_signup')}</Typography>

View File

@@ -7,6 +7,8 @@ import {
} from 'hooks/useAuthZ/types';
import { parsePermission } from 'hooks/useAuthZ/utils';
import noDataUrl from '@/assets/Icons/no-data.svg';
import ErrorBoundaryFallback from '../../pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
import AppLoading from '../AppLoading/AppLoading';
import { GuardAuthZ } from '../GuardAuthZ/GuardAuthZ';
@@ -23,7 +25,7 @@ function OnNoPermissionsFallback(response: {
return (
<div className="guard-authz-error-no-authz">
<div className="guard-authz-error-no-authz-content">
<img src="/Icons/no-data.svg" alt="No permission" />
<img src={noDataUrl} alt="No permission" />
<h3>Uh-oh! You dont have permission to view this page.</h3>
<p>
You need the following permission to view this page:

View File

@@ -24,6 +24,8 @@ import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import EndPointsDropDown from './components/EndPointsDropDown';
import ErrorState from './components/ErrorState';
import { SPAN_ATTRIBUTES } from './constants';
@@ -209,7 +211,7 @@ function TopErrors({
<div className="no-filtered-endpoints-message-container">
<div className="no-filtered-endpoints-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -10,6 +10,8 @@ import {
import { UnfoldVertical } from 'lucide-react';
import { SuccessResponse } from 'types/api';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import ErrorState from './ErrorState';
import '../DomainDetails.styles.scss';
@@ -78,7 +80,7 @@ function DependentServices({
<div className="no-status-code-data-message-container">
<div className="no-status-code-data-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -1,6 +1,8 @@
import { UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import noDataUrl from '@/assets/Icons/no-data.svg';
import EndPointsDropDown from './EndPointsDropDown';
function EndPointDetailsZeroState({
@@ -14,7 +16,7 @@ function EndPointDetailsZeroState({
<div className="end-point-details-zero-state-wrapper">
<div className="end-point-details-zero-state-content">
<img
src="/Icons/no-data.svg"
src={noDataUrl}
alt="no-data"
width={32}
height={32}

View File

@@ -1,13 +1,15 @@
import { Button, Typography } from 'antd';
import { RotateCw } from 'lucide-react';
import awwSnapUrl from '@/assets/Icons/awwSnap.svg';
function ErrorState({ refetch }: { refetch: () => void }): JSX.Element {
return (
<div className="error-state-container">
<div className="error-state-content-wrapper">
<div className="error-state-content">
<div className="icon">
<img src="/Icons/awwSnap.svg" alt="awwSnap" width={32} height={32} />
<img src={awwSnapUrl} alt="awwSnap" width={32} height={32} />
</div>
<div className="error-state-text">
<Typography.Text>Uh-oh :/ We ran into an error.</Typography.Text>

View File

@@ -7,6 +7,8 @@ import {
} from 'container/ApiMonitoring/utils';
import { SuccessResponse } from 'types/api';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import ErrorState from './ErrorState';
function StatusCodeTable({
@@ -52,7 +54,7 @@ function StatusCodeTable({
<div className="no-status-code-data-message-container">
<div className="no-status-code-data-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -23,6 +23,8 @@ import { DataSource } from 'types/common/queryBuilder';
import { GlobalReducer } from 'types/reducer/globalTime';
import DOCLINKS from 'utils/docLinks';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import { ApiMonitoringHardcodedAttributeKeys } from '../../constants';
import { DEFAULT_PARAMS, useApiMonitoringParams } from '../../queryParams';
import { columnsConfig, formatDataForTable } from '../../utils';
@@ -132,7 +134,7 @@ function DomainList(): JSX.Element {
<div className="no-filtered-domains-message-container">
<div className="no-filtered-domains-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -1,5 +1,8 @@
import { useIsDarkMode } from 'hooks/useDarkMode';
import integrationsHeroBgUrl from '@/assets/Images/integrations-hero-bg.png';
import awsDarkUrl from '@/assets/Logos/aws-dark.svg';
import AccountActions from './components/AccountActions';
import './HeroSection.style.scss';
@@ -12,13 +15,13 @@ function HeroSection(): JSX.Element {
style={
isDarkMode
? {
backgroundImage: `url('/Images/integrations-hero-bg.png')`,
backgroundImage: `url('${integrationsHeroBgUrl}')`,
}
: {}
}
>
<div className="hero-section__icon">
<img src="/Logos/aws-dark.svg" alt="aws-logo" />
<img src={awsDarkUrl} alt="aws-logo" />
</div>
<div className="hero-section__details">
<div className="title">Amazon Web Services</div>

View File

@@ -3,6 +3,8 @@ import Lottie from 'react-lottie';
import { Alert } from 'antd';
import integrationsSuccess from 'assets/Lotties/integrations-success.json';
import solidCheckCircleUrl from '@/assets/Icons/solid-check-circle.svg';
import './SuccessView.style.scss';
export function SuccessView(): JSX.Element {
@@ -36,7 +38,7 @@ export function SuccessView(): JSX.Element {
)}
<div className="cloud-account-setup-success-view">
<div className="cloud-account-setup-success-view__icon">
<img src="Icons/solid-check-circle.svg" alt="Success" />
<img src={solidCheckCircleUrl} alt="Success" />
</div>
<div className="cloud-account-setup-success-view__content">
<div className="cloud-account-setup-success-view__title">

View File

@@ -25,6 +25,7 @@ export default function ChartWrapper({
showTooltip = true,
showLegend = true,
canPinTooltip = false,
pinKey,
syncMode,
syncKey,
onDestroy = noop,
@@ -93,6 +94,7 @@ export default function ChartWrapper({
<TooltipPlugin
config={config}
canPinTooltip={canPinTooltip}
pinKey={pinKey}
syncMode={syncMode}
maxWidth={Math.max(
TOOLTIP_MIN_WIDTH,

View File

@@ -14,6 +14,8 @@ interface BaseChartProps {
showLegend?: boolean;
timezone?: Timezone;
canPinTooltip?: boolean;
/** Key that pins the tooltip while hovering. Defaults to DEFAULT_PIN_TOOLTIP_KEY ('l'). */
pinKey?: string;
yAxisUnit?: string;
decimalPrecision?: PrecisionOption;
pinnedTooltipElement?: (clickData: TooltipClickData) => React.ReactNode;

View File

@@ -121,6 +121,7 @@ function BarPanel(props: PanelWrapperProps): JSX.Element {
legendConfig={{
position: widget?.legendPosition ?? LegendPosition.BOTTOM,
}}
canPinTooltip
plotRef={onPlotRef}
onDestroy={onPlotDestroy}
yAxisUnit={widget.yAxisUnit}

View File

@@ -112,6 +112,7 @@ function TimeSeriesPanel(props: PanelWrapperProps): JSX.Element {
legendConfig={{
position: widget?.legendPosition ?? LegendPosition.BOTTOM,
}}
canPinTooltip
yAxisUnit={widget.yAxisUnit}
decimalPrecision={widget.decimalPrecision}
timezone={timezone}

View File

@@ -7,6 +7,8 @@ import { EmptyLogsListConfig } from 'container/LogsExplorerList/utils';
import { Delete } from 'lucide-react';
import { DataSource, PanelTypeKeys } from 'types/common/queryBuilder';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import './EmptyLogsSearch.styles.scss';
interface EmptyLogsSearchProps {
@@ -46,7 +48,7 @@ export default function EmptyLogsSearch({
<div className="empty-logs-search__row">
<div className="empty-logs-search__content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -11,6 +11,8 @@ import history from 'lib/history';
import APIError from 'types/api/error';
import { OrgSessionContext } from 'types/api/v2/sessions/context/get';
import tvUrl from '@/assets/svgs/tv.svg';
import SuccessScreen from './SuccessScreen';
import './ForgotPassword.styles.scss';
@@ -131,7 +133,7 @@ function ForgotPassword({
>
<div className="login-form-header">
<div className="login-form-emoji">
<img src="/svgs/tv.svg" alt="TV" width="32" height="32" />
<img src={tvUrl} alt="TV" width="32" height="32" />
</div>
<h4 className="forgot-password-title">Forgot your password?</h4>
<p className="forgot-password-description">

View File

@@ -1,5 +1,7 @@
import history from 'lib/history';
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
import './FullScreenHeader.styles.scss';
export default function FullScreenHeader({
@@ -13,7 +15,7 @@ export default function FullScreenHeader({
return (
<div className="full-screen-header-container">
<div className="brand-logo" onClick={handleLogoClick}>
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
<img src={signozBrandLogoUrl} alt="SigNoz" />
<div className="brand-logo-name">SigNoz</div>
</div>

View File

@@ -16,6 +16,10 @@ import {
import { ROLES, USER_ROLES } from 'types/roles';
import { ComponentTypes } from 'utils/permission';
import dashboardEmojiUrl from '@/assets/Icons/dashboard_emoji.svg';
import landscapeUrl from '@/assets/Icons/landscape.svg';
import toolsUrl from '@/assets/Icons/tools.svg';
import './DashboardEmptyState.styles.scss';
export default function DashboardEmptyState(): JSX.Element {
@@ -72,7 +76,7 @@ export default function DashboardEmptyState(): JSX.Element {
<div className="dashboard-content">
<section className="heading">
<img
src="/Icons/dashboard_emoji.svg"
src={dashboardEmojiUrl}
alt="header-image"
style={{ height: '32px', width: '32px' }}
/>
@@ -88,7 +92,7 @@ export default function DashboardEmptyState(): JSX.Element {
<div className="actions-configure">
<div className="actions-configure-text">
<img
src="/Icons/tools.svg"
src={toolsUrl}
alt="header-image"
style={{ height: '14px', width: '14px' }}
/>
@@ -125,7 +129,7 @@ export default function DashboardEmptyState(): JSX.Element {
<div className="actions-add-panel">
<div className="actions-panel-text">
<img
src="/Icons/landscape.svg"
src={landscapeUrl}
alt="header-image"
style={{ height: '14px', width: '14px' }}
/>

View File

@@ -14,6 +14,10 @@ import { useAppContext } from 'providers/App/App';
import { GettableAlert } from 'types/api/alerts/get';
import { USER_ROLES } from 'types/roles';
import beaconUrl from '@/assets/Icons/beacon.svg';
import { getItemIcon } from '../constants';
export default function AlertRules({
onUpdateChecklistDoneItem,
loadingUserPreferences,
@@ -65,11 +69,7 @@ export default function AlertRules({
<div className="empty-state-container">
<div className="empty-state-content-container">
<div className="empty-state-content">
<img
src="/Icons/beacon.svg"
alt="empty-alert-icon"
className="empty-state-icon"
/>
<img src={beaconUrl} alt="empty-alert-icon" className="empty-state-icon" />
<div className="empty-title">No Alert rules yet.</div>
@@ -156,11 +156,7 @@ export default function AlertRules({
>
<div className="alert-rule-item-name-container home-data-item-name-container">
<img
src={
Math.random() % 2 === 0
? '/Icons/eight-ball.svg'
: '/Icons/circus-tent.svg'
}
src={getItemIcon(rule.id)}
alt="alert-rules"
className="alert-rules-img"
/>

View File

@@ -11,6 +11,10 @@ import { useAppContext } from 'providers/App/App';
import { Dashboard } from 'types/api/dashboard/getAll';
import { USER_ROLES } from 'types/roles';
import dialsUrl from '@/assets/Icons/dials.svg';
import { getItemIcon } from '../constants';
export default function Dashboards({
onUpdateChecklistDoneItem,
loadingUserPreferences,
@@ -52,11 +56,7 @@ export default function Dashboards({
<div className="empty-state-container">
<div className="empty-state-content-container">
<div className="empty-state-content">
<img
src="/Icons/dials.svg"
alt="empty-alert-icon"
className="empty-state-icon"
/>
<img src={dialsUrl} alt="empty-alert-icon" className="empty-state-icon" />
<div className="empty-title">You dont have any dashboards yet.</div>
@@ -135,11 +135,7 @@ export default function Dashboards({
>
<div className="dashboard-item-name-container home-data-item-name-container">
<img
src={
Math.random() % 2 === 0
? '/Icons/eight-ball.svg'
: '/Icons/circus-tent.svg'
}
src={getItemIcon(dashboard.id)}
alt="alert-rules"
className="alert-rules-img"
/>

View File

@@ -10,6 +10,10 @@ import Card from 'periscope/components/Card/Card';
import { useAppContext } from 'providers/App/App';
import { LicensePlatform } from 'types/api/licensesV3/getActive';
import containerPlusUrl from '@/assets/Icons/container-plus.svg';
import helloWaveUrl from '@/assets/Icons/hello-wave.svg';
import hurrayUrl from '@/assets/Icons/hurray.svg';
import { DOCS_LINKS } from '../constants';
function DataSourceInfo({
@@ -68,7 +72,7 @@ function DataSourceInfo({
<div className="workspace-ready-container">
<div className="workspace-ready-header">
<span className="workspace-ready-title">
<img src="/Icons/hurray.svg" alt="hurray" />
<img src={hurrayUrl} alt="hurray" />
Your workspace is ready
</span>
@@ -77,7 +81,7 @@ function DataSourceInfo({
color="primary"
size="sm"
className="periscope-btn primary"
prefixIcon={<img src="/Icons/container-plus.svg" alt="plus" />}
prefixIcon={<img src={containerPlusUrl} alt="plus" />}
onClick={handleConnect}
role="button"
tabIndex={0}
@@ -135,7 +139,7 @@ function DataSourceInfo({
<div className="hello-wave-container">
<div className="hello-wave-img-container">
<img
src="/Icons/hello-wave.svg"
src={helloWaveUrl}
alt="hello-wave"
className="hello-wave-img"
width={36}

View File

@@ -33,6 +33,15 @@ import { isIngestionActive } from 'utils/app';
import { isModifierKeyPressed } from 'utils/app';
import { popupContainer } from 'utils/selectPopupContainer';
import crackerUrl from '@/assets/Icons/cracker.svg';
import dashboardUrl from '@/assets/Icons/dashboard.svg';
import spinnerHalfBlueUrl from '@/assets/Icons/spinner-half-blue.svg';
import wrenchUrl from '@/assets/Icons/wrench.svg';
import allInOneUrl from '@/assets/Images/allInOne.svg';
import allInOneLightModeUrl from '@/assets/Images/allInOneLightMode.svg';
import dottedDividerUrl from '@/assets/Images/dotted-divider.svg';
import perilianBackgroundUrl from '@/assets/Images/perilianBackground.svg';
import AlertRules from './AlertRules/AlertRules';
import { defaultChecklistItemsState } from './constants';
import Dashboards from './Dashboards/Dashboards';
@@ -313,7 +322,7 @@ export default function Home(): JSX.Element {
className="periscope-btn secondary welcome-checklist-btn"
>
<img
src="/Icons/spinner-half-blue.svg"
src={spinnerHalfBlueUrl}
alt="spinner-half-blue"
width={16}
height={16}
@@ -340,7 +349,7 @@ export default function Home(): JSX.Element {
/>
<div className="divider">
<img src="/Images/dotted-divider.svg" alt="divider" />
<img src={dottedDividerUrl} alt="divider" />
</div>
<div className="active-ingestions-container">
@@ -483,7 +492,7 @@ export default function Home(): JSX.Element {
<div className="section-content">
<div className="section-icon">
<img
src="/Icons/wrench.svg"
src={wrenchUrl}
alt="wrench"
width={16}
height={16}
@@ -558,12 +567,7 @@ export default function Home(): JSX.Element {
<div className="section-container">
<div className="section-content">
<div className="section-icon">
<img
src="/Icons/dashboard.svg"
alt="dashboard"
width={16}
height={16}
/>
<img src={dashboardUrl} alt="dashboard" width={16} height={16} />
</div>
<div className="section-title">
@@ -602,7 +606,7 @@ export default function Home(): JSX.Element {
<div className="section-content">
<div className="section-icon">
<img
src="/Icons/cracker.svg"
src={crackerUrl}
alt="cracker"
width={16}
height={16}
@@ -681,7 +685,7 @@ export default function Home(): JSX.Element {
<div className="checklist-container-right-img">
<div className="checklist-img-bg-container">
<img
src="/Images/perilianBackground.svg"
src={perilianBackgroundUrl}
alt="not-found"
className="checklist-img-bg"
/>
@@ -689,11 +693,7 @@ export default function Home(): JSX.Element {
<div className="checklist-img-container">
<img
src={
isDarkMode
? '/Images/allInOne.svg'
: '/Images/allInOneLightMode.svg'
}
src={isDarkMode ? allInOneUrl : allInOneLightModeUrl}
alt="checklist-img"
className="checklist-img"
/>

View File

@@ -20,6 +20,11 @@ import { ViewProps } from 'types/api/saveViews/types';
import { DataSource } from 'types/common/queryBuilder';
import { USER_ROLES } from 'types/roles';
import floppyDiscUrl from '@/assets/Icons/floppy-disc.svg';
import logsUrl from '@/assets/Icons/logs.svg';
import { getItemIcon } from '../constants';
export default function SavedViews({
onUpdateChecklistDoneItem,
loadingUserPreferences,
@@ -151,7 +156,7 @@ export default function SavedViews({
<div className="empty-state-content-container">
<div className="empty-state-content">
<img
src="/Icons/floppy-disc.svg"
src={floppyDiscUrl}
alt="empty-alert-icon"
className="empty-state-icon"
/>
@@ -224,9 +229,7 @@ export default function SavedViews({
>
<div className="saved-view-item-name-container home-data-item-name-container">
<img
src={
view.id % 2 === 0 ? '/Icons/eight-ball.svg' : '/Icons/circus-tent.svg'
}
src={getItemIcon(String(view.id))}
alt="alert-rules"
className="alert-rules-img"
/>
@@ -345,7 +348,7 @@ export default function SavedViews({
className={selectedEntity === 'logs' ? 'selected tab' : 'tab'}
onClick={(): void => handleTabChange('logs')}
>
<img src="/Icons/logs.svg" alt="logs-icon" className="logs-icon" />
<img src={logsUrl} alt="logs-icon" className="logs-icon" />
Logs
</Button>
<Button

View File

@@ -32,6 +32,8 @@ import { Tags } from 'types/reducer/trace';
import { USER_ROLES } from 'types/roles';
import { isModifierKeyPressed } from 'utils/app';
import triangleRulerUrl from '@/assets/Icons/triangle-ruler.svg';
import { FeatureKeys } from '../../../constants/features';
import { DOCS_LINKS } from '../constants';
import { columns, TIME_PICKER_OPTIONS } from './constants';
@@ -51,7 +53,7 @@ const EmptyState = memo(
<div className="empty-state-content-container">
<div className="empty-state-content">
<img
src="/Icons/triangle-ruler.svg"
src={triangleRulerUrl}
alt="empty-alert-icon"
className="empty-state-icon"
/>

View File

@@ -18,6 +18,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { USER_ROLES } from 'types/roles';
import { isModifierKeyPressed } from 'utils/app';
import triangleRulerUrl from '@/assets/Icons/triangle-ruler.svg';
import { DOCS_LINKS } from '../constants';
import { columns, TIME_PICKER_OPTIONS } from './constants';
@@ -103,7 +105,7 @@ export default function ServiceTraces({
<div className="empty-state-content-container">
<div className="empty-state-content">
<img
src="/Icons/triangle-ruler.svg"
src={triangleRulerUrl}
alt="empty-alert-icon"
className="empty-state-icon"
/>

View File

@@ -1,8 +1,20 @@
import { ORG_PREFERENCES } from 'constants/orgPreferences';
import ROUTES from 'constants/routes';
import circusTentUrl from '@/assets/Icons/circus-tent.svg';
import eightBallUrl from '@/assets/Icons/eight-ball.svg';
import { ChecklistItem } from './HomeChecklist/HomeChecklist';
const ITEM_ICONS = [circusTentUrl, eightBallUrl];
export function getItemIcon(id: string): string {
if (!id) {
return ITEM_ICONS[0];
}
return ITEM_ICONS[id.charCodeAt(id.length - 1) % ITEM_ICONS.length];
}
export const checkListStepToPreferenceKeyMap = {
WILL_DO_LATER: ORG_PREFERENCES.WELCOME_CHECKLIST_DO_LATER,
SEND_LOGS: ORG_PREFERENCES.WELCOME_CHECKLIST_SEND_LOGS_SKIPPED,

View File

@@ -2,6 +2,8 @@ import { useTranslation } from 'react-i18next';
import { Typography } from 'antd';
import { DataSource } from 'types/common/queryBuilder';
import loadingPlaneUrl from '@/assets/Icons/loading-plane.gif';
import './HostMetricsLoading.styles.scss';
export function HostMetricsLoading(): JSX.Element {
@@ -9,11 +11,7 @@ export function HostMetricsLoading(): JSX.Element {
return (
<div className="loading-host-metrics">
<div className="loading-host-metrics-content">
<img
className="loading-gif"
src="/Icons/loading-plane.gif"
alt="wait-icon"
/>
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
<Typography>
{t('pending_data_placeholder', {

View File

@@ -1,5 +1,7 @@
import { Typography } from 'antd';
import eyesEmojiUrl from '@/assets/Images/eyesEmoji.svg';
export default function HostsEmptyOrIncorrectMetrics({
noData,
incorrectData,
@@ -10,7 +12,7 @@ export default function HostsEmptyOrIncorrectMetrics({
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" />
<img className="eyes-emoji" src={eyesEmojiUrl} alt="eyes emoji" />
{noData && (
<div className="no-hosts-message">

View File

@@ -15,6 +15,9 @@ import { InfraMonitoringEvents } from 'constants/events';
import { isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import eyesEmojiUrl from '@/assets/Images/eyesEmoji.svg';
import HostsEmptyOrIncorrectMetrics from './HostsEmptyOrIncorrectMetrics';
import {
EmptyOrLoadingViewProps,
@@ -74,7 +77,7 @@ function EmptyOrLoadingView(
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" />
<img className="eyes-emoji" src={eyesEmojiUrl} alt="eyes emoji" />
<div className="no-hosts-message">
<Typography.Title level={5} className="no-hosts-message-title">
Queried time range is before earliest host metrics
@@ -93,7 +96,7 @@ function EmptyOrLoadingView(
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -26,6 +26,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { buildAbsolutePath, isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import {
@@ -655,7 +657,7 @@ function K8sClustersList({
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -27,6 +27,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { buildAbsolutePath, isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import { FeatureKeys } from '../../../constants/features';
import { useAppContext } from '../../../providers/App/App';
import {
@@ -677,7 +679,7 @@ function K8sDaemonSetsList({
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -29,6 +29,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { buildAbsolutePath, isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -684,7 +686,7 @@ function K8sDeploymentsList({
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -29,6 +29,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { buildAbsolutePath, isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -645,7 +647,7 @@ function K8sJobsList({
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -1,5 +1,7 @@
import { Typography } from 'antd';
import eyesEmojiUrl from '@/assets/Images/eyesEmoji.svg';
export default function HostsEmptyOrIncorrectMetrics({
noData,
incorrectData,
@@ -10,7 +12,7 @@ export default function HostsEmptyOrIncorrectMetrics({
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" />
<img className="eyes-emoji" src={eyesEmojiUrl} alt="eyes emoji" />
{noData && (
<div className="no-hosts-message">

View File

@@ -28,6 +28,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { buildAbsolutePath, isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -679,7 +681,7 @@ function K8sNamespacesList({
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -28,6 +28,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { buildAbsolutePath, isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -657,7 +659,7 @@ function K8sNodesList({
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -31,6 +31,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { buildAbsolutePath, isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -712,7 +714,7 @@ function K8sPodsList({
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -29,6 +29,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { buildAbsolutePath, isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -681,7 +683,7 @@ function K8sStatefulSetsList({
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -29,6 +29,8 @@ import { GlobalReducer } from 'types/reducer/globalTime';
import { buildAbsolutePath, isModifierKeyPressed } from 'utils/app';
import { openInNewTab } from 'utils/navigation';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import {
GetK8sEntityToAggregateAttribute,
INFRA_MONITORING_K8S_PARAMS_KEYS,
@@ -636,7 +638,7 @@ function K8sVolumesList({
<div className="no-filtered-hosts-message-container">
<div className="no-filtered-hosts-message-content">
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -9,6 +9,8 @@ import { useAppContext } from 'providers/App/App';
import { DataSource } from 'types/common/queryBuilder';
import { isModifierKeyPressed } from 'utils/app';
import alertEmojiUrl from '@/assets/Icons/alert_emoji.svg';
import AlertInfoCard from './AlertInfoCard';
import { ALERT_CARDS, ALERT_INFO_LINKS } from './alertLinks';
import InfoLinkText from './InfoLinkText';
@@ -59,7 +61,7 @@ export function AlertsEmptyState(): JSX.Element {
<div className="alert-content">
<section className="heading">
<img
src="/Icons/alert_emoji.svg"
src={alertEmojiUrl}
alt="alert-header"
style={{ height: '32px', width: '32px' }}
/>

View File

@@ -15,6 +15,9 @@ import cx from 'classnames';
import { ConciergeBell, DraftingCompass, Drill, Plus, X } from 'lucide-react';
import { DashboardTemplate } from 'types/api/dashboard/getAll';
import blankDashboardTemplatePreviewUrl from '@/assets/Images/blankDashboardTemplatePreview.svg';
import redisTemplatePreviewUrl from '@/assets/Images/redisTemplatePreview.svg';
import { filterTemplates } from '../utils';
import './DashboardTemplatesModal.styles.scss';
@@ -25,91 +28,91 @@ const templatesList: DashboardTemplate[] = [
icon: <Drill />,
id: 'blank',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'Alert Manager',
icon: <ConciergeBell />,
id: 'alertManager',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'Apache',
icon: <ApacheIcon />,
id: 'apache',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'Docker',
icon: <DockerIcon />,
id: 'docker',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'Elasticsearch',
icon: <ElasticSearchIcon />,
id: 'elasticSearch',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'MongoDB',
icon: <MongoDBIcon />,
id: 'mongoDB',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'Heroku',
icon: <HerokuIcon />,
id: 'heroku',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'Nginx',
icon: <NginxIcon />,
id: 'nginx',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'Kubernetes',
icon: <KubernetesIcon />,
id: 'kubernetes',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'MySQL',
icon: <MySQLIcon />,
id: 'mySQL',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'PostgreSQL',
icon: <PostgreSQLIcon />,
id: 'postgreSQL',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
{
name: 'Redis',
icon: <RedisIcon />,
id: 'redis',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/redisTemplatePreview.svg',
previewImage: redisTemplatePreviewUrl,
},
{
name: 'AWS',
icon: <DraftingCompass size={14} />,
id: 'aws',
description: 'Create a custom dashboard from scratch.',
previewImage: '/Images/blankDashboardTemplatePreview.svg',
previewImage: blankDashboardTemplatePreviewUrl,
},
];

View File

@@ -84,6 +84,10 @@ import {
import APIError from 'types/api/error';
import { isModifierKeyPressed } from 'utils/app';
import awwSnapUrl from '@/assets/Icons/awwSnap.svg';
import dashboardsUrl from '@/assets/Icons/dashboards.svg';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import DashboardTemplatesModal from './DashboardTemplates/DashboardTemplatesModal';
import ImportJSON from './ImportJSON';
import { RequestDashboardBtn } from './RequestDashboardBtn';
@@ -675,11 +679,7 @@ function DashboardsList(): JSX.Element {
</div>
) : dashboardFetchError ? (
<div className="dashboard-error-state">
<img
src="/Icons/awwSnap.svg"
alt="something went wrong"
className="error-img"
/>
<img src={awwSnapUrl} alt="something went wrong" className="error-img" />
<Typography.Text className="error-text">
Something went wrong :/ Please retry or contact support.
@@ -705,11 +705,7 @@ function DashboardsList(): JSX.Element {
</div>
) : dashboards.length === 0 && !searchString ? (
<div className="dashboard-empty-state">
<img
src="/Icons/dashboards.svg"
alt="dashboards"
className="dashboard-img"
/>
<img src={dashboardsUrl} alt="dashboards" className="dashboard-img" />
<section className="text">
<Typography.Text className="no-dashboard">
No dashboards yet.{' '}
@@ -789,7 +785,7 @@ function DashboardsList(): JSX.Element {
{dashboards.length === 0 ? (
<div className="no-search">
<img src="/Icons/emptyState.svg" alt="img" className="img" />
<img src={emptyStateUrl} alt="img" className="img" />
<Typography.Text className="text">
No dashboards found for {searchString}. Create a new dashboard?
</Typography.Text>

View File

@@ -21,6 +21,8 @@ import { useEventSource } from 'providers/EventSource';
import { ILog } from 'types/api/logs/log';
import { DataSource, StringOperators } from 'types/common/queryBuilder';
import loadingPlaneUrl from '@/assets/Icons/loading-plane.gif';
import { LiveLogsListProps } from './types';
import './LiveLogsList.styles.scss';
@@ -137,11 +139,7 @@ function LiveLogsList({
() => (
<div className="live-logs-list-loading">
<div className="loading-live-logs-content">
<img
className="loading-gif"
src="/Icons/loading-plane.gif"
alt="wait-icon"
/>
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
<Typography>Fetching live logs...</Typography>
</div>

View File

@@ -15,6 +15,8 @@ import { ErrorV2 } from 'types/api';
import APIError from 'types/api/error';
import { SessionsContext } from 'types/api/v2/sessions/context/get';
import tvUrl from '@/assets/svgs/tv.svg';
import { FormContainer, Label, ParentContainer } from './styles';
import './Login.styles.scss';
@@ -305,7 +307,7 @@ function Login(): JSX.Element {
<FormContainer form={form} onFinish={onSubmitHandler}>
<div className="login-form-header">
<div className="login-form-emoji">
<img src="/svgs/tv.svg" alt="TV" width="32" height="32" />
<img src={tvUrl} alt="TV" width="32" height="32" />
</div>
<Typography.Title level={4} className="login-form-title">
Sign in to your workspace

View File

@@ -3,6 +3,8 @@ import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import history from 'lib/history';
import { ArrowRight } from 'lucide-react';
import awwSnapUrl from '@/assets/Icons/awwSnap.svg';
import './LogsError.styles.scss';
export default function LogsError(): JSX.Element {
@@ -19,11 +21,7 @@ export default function LogsError(): JSX.Element {
return (
<div className="logs-error-container">
<div className="logs-error-content">
<img
src="/Icons/awwSnap.svg"
alt="error-emoji"
className="error-state-svg"
/>
<img src={awwSnapUrl} alt="error-emoji" className="error-state-svg" />
<Typography.Text>
<span className="aww-snap">Aw snap :/ </span> Something went wrong. Please
try again or contact support.

View File

@@ -4,6 +4,8 @@ import { Color } from '@signozhq/design-tokens';
import { Spin } from 'antd';
import { CircleCheck } from 'lucide-react';
import solidXCircleUrl from '@/assets/Icons/solid-x-circle.svg';
import './QueryStatus.styles.scss';
interface IQueryStatusProps {
@@ -24,7 +26,7 @@ export default function QueryStatus(
if (error) {
return (
<img
src="/Icons/solid-x-circle.svg"
src={solidXCircleUrl}
alt="header"
className="error"
style={{ height: '14px', width: '14px' }}

View File

@@ -2,6 +2,8 @@ import { useTranslation } from 'react-i18next';
import { Typography } from 'antd';
import { DataSource } from 'types/common/queryBuilder';
import loadingPlaneUrl from '@/assets/Icons/loading-plane.gif';
import './LogsLoading.styles.scss';
export function LogsLoading(): JSX.Element {
@@ -9,11 +11,7 @@ export function LogsLoading(): JSX.Element {
return (
<div className="loading-logs">
<div className="loading-logs-content">
<img
className="loading-gif"
src="/Icons/loading-plane.gif"
alt="wait-icon"
/>
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
<Typography>
{t('pending_data_placeholder', { dataSource: DataSource.LOGS })}

View File

@@ -2,6 +2,8 @@ import { useTranslation } from 'react-i18next';
import { Typography } from 'antd';
import { DataSource } from 'types/common/queryBuilder';
import loadingPlaneUrl from '@/assets/Icons/loading-plane.gif';
import './MetricsLoading.styles.scss';
export function MetricsLoading(): JSX.Element {
@@ -9,11 +11,7 @@ export function MetricsLoading(): JSX.Element {
return (
<div className="loading-metrics">
<div className="loading-metrics-content">
<img
className="loading-gif"
src="/Icons/loading-plane.gif"
alt="wait-icon"
/>
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
<Typography>
{t('pending_data_placeholder', { dataSource: DataSource.METRICS })}

View File

@@ -13,6 +13,8 @@ import { Querybuildertypesv5OrderDirectionDTO } from 'api/generated/services/sig
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
import { Info } from 'lucide-react';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import { MetricsListItemRowData, MetricsTableProps } from './types';
import { getMetricsTableColumns } from './utils';
@@ -95,7 +97,7 @@ function MetricsTable({
data-testid="metrics-table-empty-state"
>
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -7,6 +7,8 @@ import { ArrowUpRight } from 'lucide-react';
import { DataSource } from 'types/common/queryBuilder';
import DOCLINKS from 'utils/docLinks';
import eyesEmojiUrl from '@/assets/Images/eyesEmoji.svg';
import './NoLogs.styles.scss';
export default function NoLogs({
@@ -50,7 +52,7 @@ export default function NoLogs({
return (
<div className="no-logs-container">
<div className="no-logs-container-content">
<img className="eyes-emoji" src="/Images/eyesEmoji.svg" alt="eyes emoji" />
<img className="eyes-emoji" src={eyesEmojiUrl} alt="eyes emoji" />
<Typography className="no-logs-text">
No {dataSource} yet.
<span className="sub-text">

View File

@@ -23,6 +23,15 @@ import { PayloadProps as QueryServicePayloadProps } from 'types/api/metrics/getS
import { GlobalReducer } from 'types/reducer/globalTime';
import { Tags } from 'types/reducer/trace';
import elixirPngUrl from '@/assets/Logos/elixir.png';
import goPngUrl from '@/assets/Logos/go.png';
import javaPngUrl from '@/assets/Logos/java.png';
import javascriptPngUrl from '@/assets/Logos/javascript.png';
import pythonPngUrl from '@/assets/Logos/python.png';
import railsPngUrl from '@/assets/Logos/rails.png';
import rustPngUrl from '@/assets/Logos/rust.png';
import swiftPngUrl from '@/assets/Logos/swift.png';
import './ConnectionStatus.styles.scss';
const pollingInterval = 10000;
@@ -139,7 +148,7 @@ export default function ConnectionStatus(): JSX.Element {
<Header
entity="java"
heading="Java OpenTelemetry Instrumentation"
imgURL="/Logos/java.png"
imgURL={javaPngUrl}
docsURL="https://signoz.io/docs/instrumentation/java/"
imgClassName="supported-language-img"
/>
@@ -150,7 +159,7 @@ export default function ConnectionStatus(): JSX.Element {
<Header
entity="python"
heading="Python OpenTelemetry Instrumentation"
imgURL="/Logos/python.png"
imgURL={pythonPngUrl}
docsURL="https://signoz.io/docs/instrumentation/python/"
imgClassName="supported-language-img"
/>
@@ -161,7 +170,7 @@ export default function ConnectionStatus(): JSX.Element {
<Header
entity="javascript"
heading="Javascript OpenTelemetry Instrumentation"
imgURL="/Logos/javascript.png"
imgURL={javascriptPngUrl}
docsURL="https://signoz.io/docs/instrumentation/javascript/"
imgClassName="supported-language-img"
/>
@@ -171,7 +180,7 @@ export default function ConnectionStatus(): JSX.Element {
<Header
entity="go"
heading="Go OpenTelemetry Instrumentation"
imgURL="/Logos/go.png"
imgURL={goPngUrl}
docsURL="https://signoz.io/docs/instrumentation/golang/"
imgClassName="supported-language-img"
/>
@@ -181,7 +190,7 @@ export default function ConnectionStatus(): JSX.Element {
<Header
entity="rails"
heading="Ruby on Rails OpenTelemetry Instrumentation"
imgURL="/Logos/rails.png"
imgURL={railsPngUrl}
docsURL="https://signoz.io/docs/instrumentation/ruby-on-rails/"
imgClassName="supported-language-img"
/>
@@ -191,7 +200,7 @@ export default function ConnectionStatus(): JSX.Element {
<Header
entity="rust"
heading="Rust OpenTelemetry Instrumentation"
imgURL="/Logos/rust.png"
imgURL={rustPngUrl}
docsURL="https://signoz.io/docs/instrumentation/rust/"
imgClassName="supported-language-img"
/>
@@ -201,7 +210,7 @@ export default function ConnectionStatus(): JSX.Element {
<Header
entity="rust"
heading="Elixir OpenTelemetry Instrumentation"
imgURL="/Logos/elixir.png"
imgURL={elixirPngUrl}
docsURL="https://signoz.io/docs/instrumentation/elixir/"
imgClassName="supported-language-img"
/>
@@ -211,7 +220,7 @@ export default function ConnectionStatus(): JSX.Element {
<Header
entity="swift"
heading="Swift OpenTelemetry Instrumentation"
imgURL="/Logos/swift.png"
imgURL={swiftPngUrl}
docsURL="https://signoz.io/docs/instrumentation/swift/"
imgClassName="supported-language-img"
/>

View File

@@ -18,6 +18,13 @@ import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard';
import { DataSource, ReduceOperators } from 'types/common/queryBuilder';
import cmdTerminalSvgUrl from '@/assets/Logos/cmd-terminal.svg';
import dockerSvgUrl from '@/assets/Logos/docker.svg';
import kubernetesSvgUrl from '@/assets/Logos/kubernetes.svg';
import nodeJsSvgUrl from '@/assets/Logos/node-js.svg';
import softwareWindowSvgUrl from '@/assets/Logos/software-window.svg';
import syslogsSvgUrl from '@/assets/Logos/syslogs.svg';
import './LogsConnectionStatus.styles.scss';
const enum ApplicationLogsType {
@@ -165,7 +172,7 @@ export default function LogsConnectionStatus(): JSX.Element {
<Header
entity="kubernetes"
heading="Collecting Kubernetes Pod logs"
imgURL="/Logos/kubernetes.svg"
imgURL={kubernetesSvgUrl}
docsURL="https://signoz.io/docs/userguide/collect_kubernetes_pod_logs/#collect-kubernetes-pod-logs-in-signoz-cloud"
imgClassName="supported-logs-type-img"
/>
@@ -176,7 +183,7 @@ export default function LogsConnectionStatus(): JSX.Element {
<Header
entity="docker"
heading="Collecting Docker container logs"
imgURL="/Logos/docker.svg"
imgURL={dockerSvgUrl}
docsURL="https://signoz.io/docs/userguide/collect_docker_logs/"
imgClassName="supported-logs-type-img"
/>
@@ -187,7 +194,7 @@ export default function LogsConnectionStatus(): JSX.Element {
<Header
entity="syslog"
heading="Collecting Syslogs"
imgURL="/Logos/syslogs.svg"
imgURL={syslogsSvgUrl}
docsURL="https://signoz.io/docs/userguide/collecting_syslogs/"
imgClassName="supported-logs-type-img"
/>
@@ -197,7 +204,7 @@ export default function LogsConnectionStatus(): JSX.Element {
<Header
entity="nodejs"
heading="Collecting NodeJS winston logs"
imgURL="/Logos/node-js.svg"
imgURL={nodeJsSvgUrl}
docsURL="https://signoz.io/docs/userguide/collecting_nodejs_winston_logs/"
imgClassName="supported-logs-type-img"
/>
@@ -212,11 +219,11 @@ export default function LogsConnectionStatus(): JSX.Element {
? 'Collecting Application Logs from Log file'
: 'Collecting Application Logs Using OTEL SDK'
}
imgURL={`/Logos/${
imgURL={
logType === ApplicationLogsType.FROM_LOG_FILE
? 'software-window'
: 'cmd-terminal'
}.svg`}
? softwareWindowSvgUrl
: cmdTerminalSvgUrl
}
docsURL={
logType === ApplicationLogsType.FROM_LOG_FILE
? 'https://signoz.io/docs/userguide/collect_logs_from_file/'

View File

@@ -17,6 +17,8 @@ import { isEmpty, isNull } from 'lodash-es';
import { UserPlus } from 'lucide-react';
import { isModifierKeyPressed } from 'utils/app';
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
import { useOnboardingContext } from '../../context/OnboardingContext';
import {
ModuleProps,
@@ -381,7 +383,7 @@ export default function ModuleStepsContainer({
<div>
<div className="steps-container-header">
<div className="brand-logo" onClick={handleLogoClick}>
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
<img src={signozBrandLogoUrl} alt="SigNoz" />
<div className="brand-logo-name">SigNoz</div>
</div>

View File

@@ -1,5 +1,38 @@
import ROUTES from 'constants/routes';
import azureAksSvgUrl from '@/assets/Logos/azure-aks.svg';
import azureAppServiceSvgUrl from '@/assets/Logos/azure-app-service.svg';
import azureBlobStorageSvgUrl from '@/assets/Logos/azure-blob-storage.svg';
import azureContainerAppsSvgUrl from '@/assets/Logos/azure-container-apps.svg';
import azureFunctionsSvgUrl from '@/assets/Logos/azure-functions.svg';
import azureSqlDatabaseMetricsSvgUrl from '@/assets/Logos/azure-sql-database-metrics.svg';
import azureVmSvgUrl from '@/assets/Logos/azure-vm.svg';
import cloudwatchPngUrl from '@/assets/Logos/cloudwatch.png';
import cmdTerminalSvgUrl from '@/assets/Logos/cmd-terminal.svg';
import dockerSvgUrl from '@/assets/Logos/docker.svg';
import dotnetPngUrl from '@/assets/Logos/dotnet.png';
import ec2SvgUrl from '@/assets/Logos/ec2.svg';
import ecsSvgUrl from '@/assets/Logos/ecs.svg';
import eksSvgUrl from '@/assets/Logos/eks.svg';
import elixirPngUrl from '@/assets/Logos/elixir.png';
import fluentBitPngUrl from '@/assets/Logos/fluent-bit.png';
import fluentdPngUrl from '@/assets/Logos/fluentd.png';
import goPngUrl from '@/assets/Logos/go.png';
import herokuPngUrl from '@/assets/Logos/heroku.png';
import httpPngUrl from '@/assets/Logos/http.png';
import javaPngUrl from '@/assets/Logos/java.png';
import javascriptPngUrl from '@/assets/Logos/javascript.png';
import kubernetesSvgUrl from '@/assets/Logos/kubernetes.svg';
import logstashSvgUrl from '@/assets/Logos/logstash.svg';
import phpPngUrl from '@/assets/Logos/php.png';
import pythonPngUrl from '@/assets/Logos/python.png';
import railsPngUrl from '@/assets/Logos/rails.png';
import rustPngUrl from '@/assets/Logos/rust.png';
import softwareWindowSvgUrl from '@/assets/Logos/software-window.svg';
import swiftPngUrl from '@/assets/Logos/swift.png';
import syslogsSvgUrl from '@/assets/Logos/syslogs.svg';
import vercelPngUrl from '@/assets/Logos/vercel.png';
import { ModuleProps } from '../OnboardingContainer';
import { DataSourceType } from '../Steps/DataSource/DataSource';
@@ -89,230 +122,230 @@ export const frameworksMap = {
export const defaultApplicationDataSource = {
name: 'java',
id: 'java',
imgURL: `Logos/java.png`,
imgURL: javaPngUrl,
};
const supportedLanguages = [
{
name: 'java',
id: 'java',
imgURL: `/Logos/java.png`,
imgURL: javaPngUrl,
},
{
name: 'python',
id: 'python',
imgURL: `/Logos/python.png`,
imgURL: pythonPngUrl,
},
{
name: 'go',
id: 'go',
imgURL: `/Logos/go.png`,
imgURL: goPngUrl,
},
{
name: 'javascript',
id: 'javascript',
imgURL: `/Logos/javascript.png`,
imgURL: javascriptPngUrl,
},
{
name: 'rails',
id: 'rails',
imgURL: `/Logos/rails.png`,
imgURL: railsPngUrl,
},
{
name: '.NET',
id: 'dotnet',
imgURL: `/Logos/dotnet.png`,
imgURL: dotnetPngUrl,
},
{
name: 'rust',
id: 'rust',
imgURL: `/Logos/rust.png`,
imgURL: rustPngUrl,
},
{
name: 'elixir',
id: 'elixir',
imgURL: `/Logos/elixir.png`,
imgURL: elixirPngUrl,
},
{
name: 'swift',
id: 'swift',
imgURL: `/Logos/swift.png`,
imgURL: swiftPngUrl,
},
{
name: 'php',
id: 'php',
imgURL: `/Logos/php.png`,
imgURL: phpPngUrl,
},
];
export const defaultLogsType = {
name: 'Kubernetes Pod Logs',
id: 'kubernetes',
imgURL: `/Logos/kubernetes.svg`,
imgURL: kubernetesSvgUrl,
};
const supportedLogsTypes = [
{
name: 'Kubernetes Pod Logs',
id: 'kubernetes',
imgURL: `/Logos/kubernetes.svg`,
imgURL: kubernetesSvgUrl,
},
{
name: 'Docker Container Logs',
id: 'docker',
imgURL: `/Logos/docker.svg`,
imgURL: dockerSvgUrl,
},
{
name: 'SysLogs',
id: 'syslogs',
imgURL: `/Logos/syslogs.svg`,
imgURL: syslogsSvgUrl,
},
{
name: 'Application Logs',
id: 'application_logs',
imgURL: `/Logos/software-window.svg`,
imgURL: softwareWindowSvgUrl,
},
{
name: 'FluentBit',
id: 'fluentBit',
imgURL: `/Logos/fluent-bit.png`,
imgURL: fluentBitPngUrl,
},
{
name: 'FluentD',
id: 'fluentD',
imgURL: `/Logos/fluentd.png`,
imgURL: fluentdPngUrl,
},
{
name: 'LogStash',
id: 'logStash',
imgURL: `/Logos/logstash.svg`,
imgURL: logstashSvgUrl,
},
{
name: 'Heroku',
id: 'heroku',
imgURL: `/Logos/heroku.png`,
imgURL: herokuPngUrl,
},
{
name: 'Vercel',
id: 'vercel',
imgURL: `/Logos/vercel.png`,
imgURL: vercelPngUrl,
},
{
name: 'HTTP',
id: 'http',
imgURL: `/Logos/http.png`,
imgURL: httpPngUrl,
},
{
name: 'Cloudwatch',
id: 'cloudwatch',
imgURL: `/Logos/cloudwatch.png`,
imgURL: cloudwatchPngUrl,
},
];
export const defaultInfraMetricsType = {
name: 'Kubernetes Infra Metrics',
id: 'kubernetesInfraMetrics',
imgURL: `/Logos/kubernetes.svg`,
imgURL: kubernetesSvgUrl,
};
const supportedInfraMetrics = [
{
name: 'Kubernetes Infra Metrics',
id: 'kubernetesInfraMetrics',
imgURL: `/Logos/kubernetes.svg`,
imgURL: kubernetesSvgUrl,
},
{
name: 'HostMetrics',
id: 'hostMetrics',
imgURL: `/Logos/software-window.svg`,
imgURL: softwareWindowSvgUrl,
},
{
name: 'Other Metrics',
id: 'otherMetrics',
imgURL: `/Logos/cmd-terminal.svg`,
imgURL: cmdTerminalSvgUrl,
},
];
export const defaultAwsServices = {
name: 'EC2 - App/Server Logs',
id: 'awsEc2ApplicationLogs',
imgURL: `/Logos/ec2.svg`,
imgURL: ec2SvgUrl,
};
const supportedAwsServices = [
{
name: 'EC2 - App/Server Logs',
id: 'awsEc2ApplicationLogs',
imgURL: `/Logos/ec2.svg`,
imgURL: ec2SvgUrl,
},
{
name: 'EC2 - Infra Metrics',
id: 'awsEc2InfrastructureMetrics',
imgURL: `/Logos/ec2.svg`,
imgURL: ec2SvgUrl,
},
{
name: 'ECS - EC2',
id: 'awsEcsEc2',
imgURL: `/Logos/ecs.svg`,
imgURL: ecsSvgUrl,
},
{
name: 'ECS - Fargate',
id: 'awsEcsFargate',
imgURL: `/Logos/ecs.svg`,
imgURL: ecsSvgUrl,
},
{
name: 'ECS - External',
id: 'awsEcsExternal',
imgURL: `/Logos/ecs.svg`,
imgURL: ecsSvgUrl,
},
{
name: 'EKS',
id: 'awsEks',
imgURL: `/Logos/eks.svg`,
imgURL: eksSvgUrl,
},
];
export const defaultAzureServices = {
name: 'VM',
id: 'azureVm',
imgURL: `/Logos/azure-vm.svg`,
imgURL: azureVmSvgUrl,
};
const supportedAzureServices = [
{
name: 'VM',
id: 'azureVm',
imgURL: `/Logos/azure-vm.svg`,
imgURL: azureVmSvgUrl,
},
{
name: 'App Service',
id: 'azureAppService',
imgURL: `/Logos/azure-app-service.svg`,
imgURL: azureAppServiceSvgUrl,
},
{
name: 'AKS',
id: 'azureAks',
imgURL: `/Logos/azure-aks.svg`,
imgURL: azureAksSvgUrl,
},
{
name: 'Azure Functions',
id: 'azureFunctions',
imgURL: `/Logos/azure-functions.svg`,
imgURL: azureFunctionsSvgUrl,
},
{
name: 'Azure Container Apps',
id: 'azureContainerApps',
imgURL: `/Logos/azure-container-apps.svg`,
imgURL: azureContainerAppsSvgUrl,
},
{
name: 'SQL Database Metrics',
id: 'azureSQLDatabaseMetrics',
imgURL: `/Logos/azure-sql-database-metrics.svg`,
imgURL: azureSqlDatabaseMetricsSvgUrl,
},
{
name: 'Azure Blob Storage',
id: 'azureBlobStorage',
imgURL: `/Logos/azure-blob-storage.svg`,
imgURL: azureBlobStorageSvgUrl,
},
];

View File

@@ -12,6 +12,8 @@ export function OnboardingFooter(): JSX.Element {
className="footer-content"
rel="noreferrer"
>
{/* hippa.svg does not exist in src/assets — suppressed until asset is added */}
{/* eslint-disable-next-line rulesdir/no-unsupported-asset-pattern */}
<img src="/logos/hippa.svg" alt="HIPPA" className="footer-logo" />
<span className="footer-text">HIPPA</span>
</a>
@@ -22,6 +24,8 @@ export function OnboardingFooter(): JSX.Element {
className="footer-content"
rel="noreferrer"
>
{/* soc2.svg does not exist in src/assets — suppressed until asset is added */}
{/* eslint-disable-next-line rulesdir/no-unsupported-asset-pattern */}
<img src="/logos/soc2.svg" alt="SOC2" className="footer-logo" />
<span className="footer-text">SOC2</span>
</a>

View File

@@ -1,10 +1,12 @@
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
import './OnboardingHeader.styles.scss';
export function OnboardingHeader(): JSX.Element {
return (
<div className="header-container">
<div className="logo-container">
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
<img src={signozBrandLogoUrl} alt="SigNoz" />
<span className="logo-text">SigNoz</span>
</div>
</div>

View File

@@ -1,5 +1,7 @@
import { Typography } from 'antd';
import barberPoolUrl from '@/assets/svgs/barber-pool.svg';
interface OnboardingQuestionHeaderProps {
title: string;
subtitle: string;
@@ -12,7 +14,7 @@ export function OnboardingQuestionHeader({
return (
<div className="onboarding-header-section">
<div className="onboarding-header-icon">
<img src="/svgs/barber-pool.svg" alt="SigNoz" width="32" height="32" />
<img src={barberPoolUrl} alt="SigNoz" width="32" height="32" />
</div>
<Typography.Title level={4} className="onboarding-header-title">
{title}

View File

@@ -1,6 +1,8 @@
import { Tooltip, Typography } from 'antd';
import { AxiosError } from 'axios';
import noDataUrl from '@/assets/Icons/no-data.svg';
import './Error.styles.scss';
interface IErrorProps {
@@ -13,7 +15,7 @@ function Error(props: IErrorProps): JSX.Element {
return (
<div className="error-flamegraph">
<img
src="/Icons/no-data.svg"
src={noDataUrl}
alt="error-flamegraph"
className="error-flamegraph-img"
/>

View File

@@ -18,6 +18,8 @@ import { SuccessResponseV2 } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import { PublicDashboardDataProps } from 'types/api/dashboard/public/get';
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
import Panel from './Panel';
import './PublicDashboardContainer.styles.scss';
@@ -130,11 +132,7 @@ function PublicDashboardContainer({
<div className="public-dashboard-header">
<div className="public-dashboard-header-left">
<div className="brand-logo">
<img
src="/Logos/signoz-brand-logo.svg"
alt="SigNoz"
className="brand-logo-img"
/>
<img src={signozBrandLogoUrl} alt="SigNoz" className="brand-logo-img" />
<Typography className="brand-logo-name">SigNoz</Typography>
</div>

View File

@@ -2,6 +2,9 @@ import { useMemo } from 'react';
import { Button, Table, TableProps, Typography } from 'antd';
import { RotateCw } from 'lucide-react';
import awwSnapUrl from '@/assets/Icons/awwSnap.svg';
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
import RoutingPolicyListItem from './RoutingPolicyListItem';
import { RoutingPolicy, RoutingPolicyListProps } from './types';
@@ -36,10 +39,10 @@ function RoutingPolicyList({
() => (
<div className="no-routing-policies-message-container">
{showError ? (
<img src="/Icons/awwSnap.svg" alt="aww-snap" className="error-state-svg" />
<img src={awwSnapUrl} alt="aww-snap" className="error-state-svg" />
) : (
<img
src="/Icons/emptyState.svg"
src={emptyStateUrl}
alt="thinking-emoji"
className="empty-state-svg"
/>

View File

@@ -66,6 +66,8 @@ import { isModifierKeyPressed } from 'utils/app';
import { showErrorNotification } from 'utils/error';
import { openInNewTab } from 'utils/navigation';
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
import { useCmdK } from '../../providers/cmdKProvider';
import { routeConfig } from './config';
import { getQueryString } from './helper';
@@ -958,7 +960,7 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
onClickHandler(ROUTES.HOME, event);
}}
>
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
<img src={signozBrandLogoUrl} alt="SigNoz" />
</div>
{licenseTag && (

View File

@@ -1,5 +1,7 @@
import { Typography } from 'antd';
import noDataUrl from '@/assets/Icons/no-data.svg';
import './NoData.styles.scss';
interface INoDataProps {
@@ -11,7 +13,7 @@ function NoData(props: INoDataProps): JSX.Element {
return (
<div className="no-data">
<img src="/Icons/no-data.svg" alt="no-data" className="no-data-img" />
<img src={noDataUrl} alt="no-data" className="no-data-img" />
<Typography.Text className="no-data-text">
No {name} found for selected span
</Typography.Text>

View File

@@ -30,6 +30,8 @@ import { TagFilter } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import { v4 as uuid } from 'uuid';
import noDataUrl from '@/assets/Icons/no-data.svg';
import './spanLogs.styles.scss';
interface SpanLogsProps {
@@ -233,7 +235,7 @@ function SpanLogs({
const renderNoLogsFound = (): JSX.Element => (
<div className="span-logs-empty-content">
<section className="description">
<img src="/Icons/no-data.svg" alt="no-data" className="no-data-img" />
<img src={noDataUrl} alt="no-data" className="no-data-img" />
<Typography.Text className="no-data-text-1">
No logs found for selected span.
<span className="no-data-text-2">View logs for the current trace.</span>

View File

@@ -33,6 +33,8 @@ import { useAppContext } from 'providers/App/App';
import { Span } from 'types/api/trace/getTraceV2';
import { toFixed } from 'utils/toFixed';
import funnelAddUrl from '@/assets/Icons/funnel-add.svg';
import Filters from './Filters/Filters';
import './Success.styles.scss';
@@ -189,7 +191,7 @@ function SpanOverview({
icon={
<img
className="add-funnel-button__icon"
src="/Icons/funnel-add.svg"
src={funnelAddUrl}
alt="funnel-icon"
/>
}

View File

@@ -2,6 +2,8 @@ import { useTranslation } from 'react-i18next';
import { Typography } from 'antd';
import { DataSource } from 'types/common/queryBuilder';
import loadingPlaneUrl from '@/assets/Icons/loading-plane.gif';
import './TraceLoading.styles.scss';
export function TracesLoading(): JSX.Element {
@@ -9,11 +11,7 @@ export function TracesLoading(): JSX.Element {
return (
<div className="loading-traces">
<div className="loading-traces-content">
<img
className="loading-gif"
src="/Icons/loading-plane.gif"
alt="wait-icon"
/>
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
<Typography>
{t('pending_data_placeholder', { dataSource: DataSource.TRACES })}

View File

@@ -16,7 +16,6 @@ import {
} from './tooltipController';
import {
DashboardCursorSync,
TooltipClickData,
TooltipControllerContext,
TooltipControllerState,
TooltipLayoutInfo,
@@ -31,6 +30,8 @@ const INTERACTIVE_CONTAINER_CLASSNAME = '.tooltip-plugin-container';
// Delay before hiding an unpinned tooltip when the cursor briefly leaves
// the plot this avoids flicker when moving between nearby points.
const HOVER_DISMISS_DELAY_MS = 100;
// Default key that pins the tooltip while hovering over the chart.
export const DEFAULT_PIN_TOOLTIP_KEY = 'l';
// eslint-disable-next-line sonarjs/cognitive-complexity
export default function TooltipPlugin({
@@ -42,6 +43,7 @@ export default function TooltipPlugin({
syncKey = '_tooltip_sync_global_',
pinnedTooltipElement,
canPinTooltip = false,
pinKey = DEFAULT_PIN_TOOLTIP_KEY,
}: TooltipPluginProps): JSX.Element | null {
const containerRef = useRef<HTMLDivElement>(null);
const rafId = useRef<number | null>(null);
@@ -259,68 +261,73 @@ export default function TooltipPlugin({
}
};
// When pinning is enabled, a click on the plot overlay while
// hovering converts the transient tooltip into a pinned one.
// Uses getPlot(controller) to avoid closing over u (plot), which
// would retain the plot and detached canvases across unmounts.
const handleUPlotOverClick = (event: MouseEvent): void => {
// Pin the tooltip when the user presses the configured pin key while
// hovering over the chart. Uses getFocusedSeriesAtPosition with a
// synthetic event built from the current uPlot cursor position so the
// same series-resolution logic as click-pinning is reused.
const handleKeyDown = (event: KeyboardEvent): void => {
if (event.key.toLowerCase() !== pinKey.toLowerCase()) {
return;
}
const plot = getPlot(controller);
if (
plot &&
event.target === plot.over &&
controller.hoverActive &&
!controller.pinned &&
controller.focusedSeriesIndex != null
!plot ||
!controller.hoverActive ||
controller.pinned ||
controller.focusedSeriesIndex == null
) {
const xValue = plot.posToVal(event.offsetX, 'x');
const yValue = plot.posToVal(event.offsetY, 'y');
const focusedSeries = getFocusedSeriesAtPosition(event, plot);
let clickedDataTimestamp = xValue;
if (focusedSeries) {
const dataIndex = plot.posToIdx(event.offsetX);
const xSeriesData = plot.data[0];
if (
xSeriesData &&
dataIndex >= 0 &&
dataIndex < xSeriesData.length &&
xSeriesData[dataIndex] !== undefined
) {
clickedDataTimestamp = xSeriesData[dataIndex];
}
}
const clickData: TooltipClickData = {
xValue,
yValue,
focusedSeries,
clickedDataTimestamp,
mouseX: event.offsetX,
mouseY: event.offsetY,
absoluteMouseX: event.clientX,
absoluteMouseY: event.clientY,
};
controller.clickData = clickData;
setTimeout(() => {
controller.pinned = true;
scheduleRender(true);
}, 0);
return;
}
const cursorLeft = plot.cursor.left ?? -1;
const cursorTop = plot.cursor.top ?? -1;
if (cursorLeft < 0 || cursorTop < 0) {
return;
}
const plotRect = plot.over.getBoundingClientRect();
const syntheticEvent = ({
clientX: plotRect.left + cursorLeft,
clientY: plotRect.top + cursorTop,
target: plot.over,
offsetX: cursorLeft,
offsetY: cursorTop,
} as unknown) as MouseEvent;
const xValue = plot.posToVal(cursorLeft, 'x');
const yValue = plot.posToVal(cursorTop, 'y');
const focusedSeries = getFocusedSeriesAtPosition(syntheticEvent, plot);
const dataIndex = plot.posToIdx(cursorLeft);
let clickedDataTimestamp = xValue;
const xSeriesData = plot.data[0];
if (
xSeriesData &&
dataIndex >= 0 &&
dataIndex < xSeriesData.length &&
xSeriesData[dataIndex] !== undefined
) {
clickedDataTimestamp = xSeriesData[dataIndex];
}
controller.clickData = {
xValue,
yValue,
focusedSeries,
clickedDataTimestamp,
mouseX: cursorLeft,
mouseY: cursorTop,
absoluteMouseX: syntheticEvent.clientX,
absoluteMouseY: syntheticEvent.clientY,
};
controller.pinned = true;
scheduleRender(true);
};
let overClickHandler: ((event: MouseEvent) => void) | null = null;
// Called once per uPlot instance; used to store the instance
// on the controller and optionally attach the pinning handler.
// Called once per uPlot instance; used to store the instance on the controller.
const handleInit = (u: uPlot): void => {
controller.plot = u;
updateState({ hasPlot: true });
if (canPinTooltip) {
overClickHandler = handleUPlotOverClick;
u.over.addEventListener('click', overClickHandler);
}
};
// If the underlying data changes we drop any pinned tooltip,
@@ -365,6 +372,9 @@ export default function TooltipPlugin({
window.addEventListener('resize', handleWindowResize);
window.addEventListener('scroll', handleScroll, true);
if (canPinTooltip) {
document.addEventListener('keydown', handleKeyDown, true);
}
return (): void => {
layoutRef.current?.observer.disconnect();
@@ -372,6 +382,9 @@ export default function TooltipPlugin({
window.removeEventListener('scroll', handleScroll, true);
document.removeEventListener('mousedown', onOutsideInteraction, true);
document.removeEventListener('keydown', onOutsideInteraction, true);
if (canPinTooltip) {
document.removeEventListener('keydown', handleKeyDown, true);
}
cancelPendingRender();
removeReadyHook();
removeInitHook();
@@ -379,13 +392,6 @@ export default function TooltipPlugin({
removeSetSeriesHook();
removeSetLegendHook();
removeSetCursorHook();
if (overClickHandler) {
const plot = getPlot(controller);
if (plot) {
plot.over.removeEventListener('click', overClickHandler);
}
overClickHandler = null;
}
clearPlotReferences();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -423,8 +429,12 @@ export default function TooltipPlugin({
}, [isHovering, hasPlot]);
const tooltipBody = useMemo(() => {
if (isPinned && pinnedTooltipElement != null && viewState.clickData != null) {
return pinnedTooltipElement(viewState.clickData);
if (isPinned) {
if (pinnedTooltipElement != null && viewState.clickData != null) {
return pinnedTooltipElement(viewState.clickData);
}
// No custom pinned element — keep showing the last hover contents.
return contents ?? null;
}
if (isHovering) {

View File

@@ -102,6 +102,12 @@ export function updateHoverState(
controller: TooltipControllerState,
syncTooltipWithDashboard: boolean,
): void {
// When pinned, keep hoverActive stable so the tooltip stays visible
// until explicitly dismissed — the cursor lock fires asynchronously
// and setSeries/setLegend can otherwise race and clear hoverActive.
if (controller.pinned) {
return;
}
// When the cursor is driven by dashboardlevel sync, we only show
// the tooltip if the plot is in viewport and at least one series
// is active. Otherwise we fall back to local interaction logic.

View File

@@ -37,6 +37,8 @@ export interface TooltipLayoutInfo {
export interface TooltipPluginProps {
config: UPlotConfigBuilder;
canPinTooltip?: boolean;
/** Key that pins the tooltip while hovering. Defaults to DEFAULT_PIN_TOOLTIP_KEY ('l'). */
pinKey?: string;
syncMode?: DashboardCursorSync;
syncKey?: string;
render: (args: TooltipRenderArgs) => ReactNode;

View File

@@ -6,7 +6,9 @@ import type uPlot from 'uplot';
import { TooltipRenderArgs } from '../../components/types';
import { UPlotConfigBuilder } from '../../config/UPlotConfigBuilder';
import TooltipPlugin from '../TooltipPlugin/TooltipPlugin';
import TooltipPlugin, {
DEFAULT_PIN_TOOLTIP_KEY,
} from '../TooltipPlugin/TooltipPlugin';
import { DashboardCursorSync } from '../TooltipPlugin/types';
// Avoid depending on the full uPlot + onClickPlugin behaviour in these tests.
@@ -60,7 +62,7 @@ function getHandler(config: ConfigMock, hookName: string): HookHandler {
function createFakePlot(): {
over: HTMLDivElement;
setCursor: jest.Mock<void, [uPlot.Cursor]>;
cursor: { event: Record<string, unknown> };
cursor: { event: Record<string, unknown>; left: number; top: number };
posToVal: jest.Mock<number, [value: number]>;
posToIdx: jest.Mock<number, []>;
data: [number[], number[]];
@@ -71,7 +73,9 @@ function createFakePlot(): {
return {
over,
setCursor: jest.fn(),
cursor: { event: {} },
// left / top are set to valid values so keyboard-pin tests do not
// hit the "cursor off-screen" guard inside handleKeyDown.
cursor: { event: {}, left: 50, top: 50 },
// In real uPlot these map overlay coordinates to data-space values.
posToVal: jest.fn((value: number) => value),
posToIdx: jest.fn(() => 0),
@@ -245,18 +249,21 @@ describe('TooltipPlugin', () => {
// ---- Pin behaviour ----------------------------------------------------------
describe('pin behaviour', () => {
it('pins the tooltip when canPinTooltip is true and overlay is clicked', () => {
it('pins the tooltip when canPinTooltip is true and the pinKey is pressed while hovering', () => {
const config = createConfigMock();
const fakePlot = renderAndActivateHover(config, undefined, {
canPinTooltip: true,
});
renderAndActivateHover(config, undefined, { canPinTooltip: true });
const container = screen.getByTestId('tooltip-plugin-container');
expect(container.classList.contains('pinned')).toBe(false);
act(() => {
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: DEFAULT_PIN_TOOLTIP_KEY,
bubbles: true,
}),
);
});
return waitFor(() => {
@@ -272,7 +279,7 @@ describe('TooltipPlugin', () => {
React.createElement('div', null, 'pinned-tooltip'),
);
const fakePlot = renderAndActivateHover(
renderAndActivateHover(
config,
() => React.createElement('div', null, 'hover-tooltip'),
{
@@ -284,7 +291,12 @@ describe('TooltipPlugin', () => {
expect(screen.getByText('hover-tooltip')).toBeInTheDocument();
act(() => {
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: DEFAULT_PIN_TOOLTIP_KEY,
bubbles: true,
}),
);
});
await waitFor(() => {
@@ -318,9 +330,14 @@ describe('TooltipPlugin', () => {
getHandler(config, 'setSeries')(fakePlot, 1, { focus: true });
});
// Pin the tooltip.
// Pin the tooltip via the keyboard shortcut.
act(() => {
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: DEFAULT_PIN_TOOLTIP_KEY,
bubbles: true,
}),
);
});
// Wait until the tooltip is actually pinned (pointer events enabled)
@@ -369,9 +386,14 @@ describe('TooltipPlugin', () => {
jest.runAllTimers();
});
// Pin.
// Pin via keyboard.
act(() => {
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: DEFAULT_PIN_TOOLTIP_KEY,
bubbles: true,
}),
);
jest.runAllTimers();
});
@@ -417,8 +439,14 @@ describe('TooltipPlugin', () => {
jest.runAllTimers();
});
// Pin via keyboard.
act(() => {
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: DEFAULT_PIN_TOOLTIP_KEY,
bubbles: true,
}),
);
jest.runAllTimers();
});
@@ -467,8 +495,14 @@ describe('TooltipPlugin', () => {
jest.runAllTimers();
});
// Pin via keyboard.
act(() => {
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: DEFAULT_PIN_TOOLTIP_KEY,
bubbles: true,
}),
);
jest.runAllTimers();
});
@@ -498,6 +532,170 @@ describe('TooltipPlugin', () => {
});
});
// ---- Keyboard pin edge cases ------------------------------------------------
describe('keyboard pin edge cases', () => {
it('does not pin when cursor coordinates are negative (cursor off-screen)', () => {
const config = createConfigMock();
render(
React.createElement(TooltipPlugin, {
config,
render: () => React.createElement('div', null, 'tooltip-body'),
syncMode: DashboardCursorSync.None,
canPinTooltip: true,
}),
);
// Negative cursor coords — handleKeyDown bails out before pinning.
const fakePlot = {
...createFakePlot(),
cursor: { event: {}, left: -1, top: -1 },
};
act(() => {
getHandler(config, 'init')(fakePlot);
getHandler(config, 'setSeries')(fakePlot, 1, { focus: true });
});
act(() => {
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: DEFAULT_PIN_TOOLTIP_KEY,
bubbles: true,
}),
);
});
const container = screen.getByTestId('tooltip-plugin-container');
expect(container.classList.contains('pinned')).toBe(false);
});
it('does not pin when hover is not active', () => {
const config = createConfigMock();
render(
React.createElement(TooltipPlugin, {
config,
render: () => React.createElement('div', null, 'tooltip-body'),
syncMode: DashboardCursorSync.None,
canPinTooltip: true,
}),
);
const fakePlot = createFakePlot();
act(() => {
// Initialise the plot but do NOT call setSeries hoverActive stays false.
getHandler(config, 'init')(fakePlot);
});
act(() => {
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: DEFAULT_PIN_TOOLTIP_KEY,
bubbles: true,
}),
);
});
// The container exists once the plot is initialised, but it should
// be hidden and not pinned since hover was never activated.
const container = screen.getByTestId('tooltip-plugin-container');
expect(container.classList.contains('pinned')).toBe(false);
expect(container.classList.contains('visible')).toBe(false);
});
it('ignores other keys and only pins on the configured pinKey', async () => {
const config = createConfigMock();
renderAndActivateHover(config, undefined, {
canPinTooltip: true,
pinKey: 'p',
});
// Default key 'l' should NOT pin when pinKey is 'p'.
act(() => {
document.body.dispatchEvent(
new KeyboardEvent('keydown', {
key: DEFAULT_PIN_TOOLTIP_KEY,
bubbles: true,
}),
);
});
await waitFor(() => {
expect(
screen
.getByTestId('tooltip-plugin-container')
.classList.contains('pinned'),
).toBe(false);
});
// Custom pin key 'p' SHOULD pin.
act(() => {
document.body.dispatchEvent(
new KeyboardEvent('keydown', { key: 'p', bubbles: true }),
);
});
await waitFor(() => {
expect(
screen
.getByTestId('tooltip-plugin-container')
.classList.contains('pinned'),
).toBe(true);
});
});
it('does not register a keydown listener when canPinTooltip is false', () => {
const config = createConfigMock();
const addSpy = jest.spyOn(document, 'addEventListener');
render(
React.createElement(TooltipPlugin, {
config,
render: () => null,
syncMode: DashboardCursorSync.None,
canPinTooltip: false,
}),
);
const keydownCalls = addSpy.mock.calls.filter(
([type]) => type === 'keydown',
);
expect(keydownCalls).toHaveLength(0);
});
it('removes the keydown pin listener on unmount', () => {
const config = createConfigMock();
const addSpy = jest.spyOn(document, 'addEventListener');
const removeSpy = jest.spyOn(document, 'removeEventListener');
const { unmount } = render(
React.createElement(TooltipPlugin, {
config,
render: () => null,
syncMode: DashboardCursorSync.None,
canPinTooltip: true,
}),
);
const pinListenerCall = addSpy.mock.calls.find(
([type]) => type === 'keydown',
);
expect(pinListenerCall).toBeDefined();
if (!pinListenerCall) {
return;
}
const [, pinListener, pinOptions] = pinListenerCall;
unmount();
expect(removeSpy).toHaveBeenCalledWith('keydown', pinListener, pinOptions);
});
});
// ---- Cursor sync ------------------------------------------------------------
describe('cursor sync', () => {

View File

@@ -7,6 +7,10 @@ import { LifeBuoy, List } from 'lucide-react';
import { handleContactSupport } from 'pages/Integrations/utils';
import { isModifierKeyPressed } from 'utils/app';
import broomUrl from '@/assets/Icons/broom.svg';
import constructionUrl from '@/assets/Icons/construction.svg';
import noDataUrl from '@/assets/Icons/no-data.svg';
import './AlertNotFound.styles.scss';
interface AlertNotFoundProps {
@@ -28,7 +32,7 @@ function AlertNotFound({ isTestAlert }: AlertNotFoundProps): JSX.Element {
return (
<div className="alert-not-found">
<section className="description">
<img src="/Icons/no-data.svg" alt="no-data" className="not-found-img" />
<img src={noDataUrl} alt="no-data" className="not-found-img" />
<Typography.Text className="not-found-text">
Uh-oh! We couldn&apos;t find the given alert rule.
</Typography.Text>
@@ -42,17 +46,13 @@ function AlertNotFound({ isTestAlert }: AlertNotFoundProps): JSX.Element {
{!isTestAlert && (
<>
<div className="reason">
<img
src="/Icons/construction.svg"
alt="no-data"
className="construction-img"
/>
<img src={constructionUrl} alt="no-data" className="construction-img" />
<Typography.Text className="text">
The alert rule link is incorrect, please verify it once.
</Typography.Text>
</div>
<div className="reason">
<img src="/Icons/broom.svg" alt="no-data" className="broom-img" />
<img src={broomUrl} alt="no-data" className="broom-img" />
<Typography.Text className="text">
The alert rule you&apos;re trying to check has been deleted.
</Typography.Text>
@@ -61,7 +61,7 @@ function AlertNotFound({ isTestAlert }: AlertNotFoundProps): JSX.Element {
)}
{isTestAlert && (
<div className="reason">
<img src="/Icons/broom.svg" alt="no-data" className="broom-img" />
<img src={broomUrl} alt="no-data" className="broom-img" />
<Typography.Text className="text">
You clicked on the Alert notification link received when testing a new
Alert rule. Once the alert rule is saved, future notifications will link

View File

@@ -5,6 +5,8 @@ import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import { Home, LifeBuoy } from 'lucide-react';
import { handleContactSupport } from 'pages/Integrations/utils';
import cloudUrl from '@/assets/Images/cloud.svg';
import './ErrorBoundaryFallback.styles.scss';
function ErrorBoundaryFallback(): JSX.Element {
@@ -23,7 +25,7 @@ function ErrorBoundaryFallback(): JSX.Element {
<div className="error-boundary-fallback-container">
<div className="error-boundary-fallback-content">
<div className="error-icon">
<img src="/Images/cloud.svg" alt="error-cloud-icon" />
<img src={cloudUrl} alt="error-cloud-icon" />
</div>
<div className="title">Something went wrong :/</div>

View File

@@ -10,6 +10,16 @@ import { Typography } from 'antd';
import Slack from 'container/SideNav/Slack';
import store from 'store';
import elixirPngUrl from '@/assets/Logos/elixir.png';
import goPngUrl from '@/assets/Logos/go.png';
import javaPngUrl from '@/assets/Logos/java.png';
import javascriptPngUrl from '@/assets/Logos/javascript.png';
import msNetFrameworkPngUrl from '@/assets/Logos/ms-net-framework.png';
import phpPngUrl from '@/assets/Logos/php.png';
import pythonPngUrl from '@/assets/Logos/python.png';
import railsPngUrl from '@/assets/Logos/rails.png';
import rustPngUrl from '@/assets/Logos/rust.png';
import { TGetStartedContentSection } from './types';
export const GetStartedContent = (): TGetStartedContentSection[] => {
@@ -23,14 +33,14 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
{
title: 'Instrument your Java Application',
icon: (
<img src={`/Logos/java.png?currentVersion=${currentVersion}`} alt="" />
<img src={`${javaPngUrl}?currentVersion=${currentVersion}`} alt="" />
),
url: 'https://signoz.io/docs/instrumentation/java/',
},
{
title: 'Instrument your Python Application',
icon: (
<img src={`/Logos/python.png?currentVersion=${currentVersion}`} alt="" />
<img src={`${pythonPngUrl}?currentVersion=${currentVersion}`} alt="" />
),
url: 'https://signoz.io/docs/instrumentation/python/',
},
@@ -38,7 +48,7 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
title: 'Instrument your JS Application',
icon: (
<img
src={`/Logos/javascript.png?currentVersion=${currentVersion}`}
src={`${javascriptPngUrl}?currentVersion=${currentVersion}`}
alt=""
/>
),
@@ -46,16 +56,14 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
},
{
title: 'Instrument your Go Application',
icon: (
<img src={`/Logos/go.png?currentVersion=${currentVersion}`} alt="" />
),
icon: <img src={`${goPngUrl}?currentVersion=${currentVersion}`} alt="" />,
url: 'https://signoz.io/docs/instrumentation/golang/',
},
{
title: 'Instrument your .NET Application',
icon: (
<img
src={`/Logos/ms-net-framework.png?currentVersion=${currentVersion}`}
src={`${msNetFrameworkPngUrl}?currentVersion=${currentVersion}`}
alt=""
/>
),
@@ -63,29 +71,27 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
},
{
title: 'Instrument your PHP Application',
icon: (
<img src={`/Logos/php.png?currentVersion=${currentVersion}`} alt="" />
),
icon: <img src={`${phpPngUrl}?currentVersion=${currentVersion}`} alt="" />,
url: 'https://signoz.io/docs/instrumentation/php/',
},
{
title: 'Instrument your Rails Application',
icon: (
<img src={`/Logos/rails.png?currentVersion=${currentVersion}`} alt="" />
<img src={`${railsPngUrl}?currentVersion=${currentVersion}`} alt="" />
),
url: 'https://signoz.io/docs/instrumentation/ruby-on-rails/',
},
{
title: 'Instrument your Rust Application',
icon: (
<img src={`/Logos/rust.png?currentVersion=${currentVersion}`} alt="" />
<img src={`${rustPngUrl}?currentVersion=${currentVersion}`} alt="" />
),
url: 'https://signoz.io/docs/instrumentation/rust/',
},
{
title: 'Instrument your Elixir Application',
icon: (
<img src={`/Logos/elixir.png?currentVersion=${currentVersion}`} alt="" />
<img src={`${elixirPngUrl}?currentVersion=${currentVersion}`} alt="" />
),
url: 'https://signoz.io/docs/instrumentation/elixir/',
},

View File

@@ -6,6 +6,8 @@ import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import { defaultTo } from 'lodash-es';
import { ArrowLeft, MoveUpRight, RotateCw } from 'lucide-react';
import awwSnapUrl from '@/assets/Icons/awwSnap.svg';
import { handleContactSupport } from '../utils';
import IntegrationDetailContent from './IntegrationDetailContent';
import IntegrationDetailHeader from './IntegrationDetailHeader';
@@ -84,11 +86,7 @@ function IntegrationDetailPage(props: IntegrationDetailPageProps): JSX.Element {
) : isError ? (
<div className="error-container">
<div className="error-content">
<img
src="/Icons/awwSnap.svg"
alt="error-emoji"
className="error-state-svg"
/>
<img src={awwSnapUrl} alt="error-emoji" className="error-state-svg" />
<Typography.Text>
Something went wrong :/ Please retry or contact support.
</Typography.Text>

View File

@@ -6,6 +6,9 @@ import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import { MoveUpRight, RotateCw } from 'lucide-react';
import { IntegrationsProps } from 'types/api/integrations/types';
import awwSnapUrl from '@/assets/Icons/awwSnap.svg';
import awsDarkUrl from '@/assets/Logos/aws-dark.svg';
import { handleContactSupport, INTEGRATION_TYPES } from './utils';
import './Integrations.styles.scss';
@@ -19,7 +22,7 @@ export const AWS_INTEGRATION = {
email: 'integrations@signoz.io',
homepage: 'https://signoz.io',
},
icon: `Logos/aws-dark.svg`,
icon: awsDarkUrl,
is_installed: false,
is_new: true,
};
@@ -60,11 +63,7 @@ function IntegrationsList(props: IntegrationsListProps): JSX.Element {
{!loading && isError && (
<div className="error-container">
<div className="error-content">
<img
src="/Icons/awwSnap.svg"
alt="error-emoji"
className="error-state-svg"
/>
<img src={awwSnapUrl} alt="error-emoji" className="error-state-svg" />
<Typography.Text>
Something went wrong :/ Please retry or contact support.
</Typography.Text>

View File

@@ -3,6 +3,8 @@ import { Typography } from 'antd';
import { useGetPublicDashboardData } from 'hooks/dashboard/useGetPublicDashboardData';
import { FrownIcon } from 'lucide-react';
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
import PublicDashboardContainer from '../../container/PublicDashboardContainer';
import './PublicDashboard.styles.scss';
@@ -38,11 +40,7 @@ function PublicDashboardPage(): JSX.Element {
<div className="public-dashboard-error-content-header">
<div className="brand">
<img
src="/Logos/signoz-brand-logo.svg"
alt="SigNoz"
className="brand-logo"
/>
<img src={signozBrandLogoUrl} alt="SigNoz" className="brand-logo" />
<Typography.Title level={2} className="brand-title">
SigNoz

View File

@@ -13,6 +13,8 @@ import { useNotifications } from 'hooks/useNotifications';
import { ArrowRight, CircleAlert } from 'lucide-react';
import APIError from 'types/api/error';
import tvUrl from '@/assets/svgs/tv.svg';
import { FormContainer, Label } from './styles';
import './SignUp.styles.scss';
@@ -130,7 +132,7 @@ function SignUp(): JSX.Element {
<div className="signup-card">
<div className="signup-form-header">
<div className="signup-header-icon">
<img src="/svgs/tv.svg" alt="TV" width="32" height="32" />
<img src={tvUrl} alt="TV" width="32" height="32" />
</div>
<Typography.Title level={4} className="signup-header-title">
Create your account

View File

@@ -3,6 +3,10 @@ import { useGetTenantLicense } from 'hooks/useGetTenantLicense';
import { LifeBuoy, RefreshCw } from 'lucide-react';
import { handleContactSupport } from 'pages/Integrations/utils';
import broomUrl from '@/assets/Icons/broom.svg';
import constructionUrl from '@/assets/Icons/construction.svg';
import noDataUrl from '@/assets/Icons/no-data.svg';
import './NoData.styles.scss';
function NoData(): JSX.Element {
@@ -11,7 +15,7 @@ function NoData(): JSX.Element {
return (
<div className="not-found-trace">
<section className="description">
<img src="/Icons/no-data.svg" alt="no-data" className="not-found-img" />
<img src={noDataUrl} alt="no-data" className="not-found-img" />
<Typography.Text className="not-found-text-1">
Uh-oh! We cannot show the selected trace.
<span className="not-found-text-2">
@@ -21,18 +25,14 @@ function NoData(): JSX.Element {
</section>
<section className="reasons">
<div className="reason-1">
<img
src="/Icons/construction.svg"
alt="no-data"
className="construction-img"
/>
<img src={constructionUrl} alt="no-data" className="construction-img" />
<Typography.Text className="text">
The trace data has not been rendered on your SigNoz server yet. You can
wait for a bit and refresh this page if this is the case.
</Typography.Text>
</div>
<div className="reason-2">
<img src="/Icons/broom.svg" alt="no-data" className="broom-img" />
<img src={broomUrl} alt="no-data" className="broom-img" />
<Typography.Text className="text">
The trace has been deleted as the data has crossed its retention period.
</Typography.Text>

View File

@@ -1,5 +1,7 @@
import LearnMore from 'components/LearnMore/LearnMore';
import emptyFunnelIconUrl from '@/assets/Icons/empty-funnel-icon.svg';
import './EmptyFunnelResults.styles.scss';
function EmptyFunnelResults({
@@ -13,7 +15,7 @@ function EmptyFunnelResults({
<div className="funnel-results funnel-results--empty">
<div className="empty-funnel-results">
<div className="empty-funnel-results__icon">
<img src="/Icons/empty-funnel-icon.svg" alt="Empty funnel results" />
<img src={emptyFunnelIconUrl} alt="Empty funnel results" />
</div>
<div className="empty-funnel-results__title">{title}</div>
<div className="empty-funnel-results__description">{description}</div>

Some files were not shown because too many files have changed in this diff Show More