mirror of
https://github.com/SigNoz/signoz.git
synced 2026-04-16 17:00:28 +01:00
Compare commits
2 Commits
chore/tool
...
chore/tool
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e1916daa6 | ||
|
|
7eb8806c0f |
@@ -1,7 +1,5 @@
|
||||
node_modules
|
||||
build
|
||||
eslint-rules/
|
||||
stylelint-rules/
|
||||
*.typegen.ts
|
||||
i18-generate-hash.js
|
||||
src/parser/TraceOperatorParser/**
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
// 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
|
||||
*/
|
||||
@@ -39,7 +32,6 @@ module.exports = {
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: [
|
||||
'rulesdir', // Local custom rules
|
||||
'react', // React-specific rules
|
||||
'@typescript-eslint', // TypeScript linting
|
||||
'simple-import-sort', // Auto-sort imports
|
||||
@@ -64,9 +56,6 @@ 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
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
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,
|
||||
},
|
||||
};
|
||||
@@ -1,390 +0,0 @@
|
||||
'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 },
|
||||
});
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
@@ -1,121 +0,0 @@
|
||||
'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,
|
||||
};
|
||||
@@ -10,10 +10,9 @@
|
||||
"preview": "vite preview",
|
||||
"prettify": "prettier --write .",
|
||||
"fmt": "prettier --check .",
|
||||
"lint": "eslint ./src && stylelint \"src/**/*.scss\"",
|
||||
"lint": "eslint ./src",
|
||||
"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",
|
||||
@@ -230,7 +229,6 @@
|
||||
"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",
|
||||
@@ -246,7 +244,6 @@
|
||||
"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",
|
||||
@@ -254,8 +251,6 @@
|
||||
"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",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import notFound404Url from '@/assets/Images/notFound404.png';
|
||||
|
||||
function NotFound(): JSX.Element {
|
||||
return (
|
||||
<img
|
||||
@@ -7,7 +5,7 @@ function NotFound(): JSX.Element {
|
||||
maxHeight: 480,
|
||||
maxWidth: 480,
|
||||
}}
|
||||
src={notFound404Url}
|
||||
src="/Images/notFound404.png"
|
||||
alt="not-found"
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -3,8 +3,6 @@ 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 {
|
||||
@@ -26,7 +24,11 @@ function AppLoading(): JSX.Element {
|
||||
<div className="perilin-bg" />
|
||||
<div className="app-loading-content">
|
||||
<div className="brand">
|
||||
<img src={signozBrandLogoUrl} alt="SigNoz" className="brand-logo" />
|
||||
<img
|
||||
src="/Logos/signoz-brand-logo.svg"
|
||||
alt="SigNoz"
|
||||
className="brand-logo"
|
||||
/>
|
||||
|
||||
<Typography.Title level={2} className="brand-title">
|
||||
SigNoz
|
||||
|
||||
@@ -47,7 +47,7 @@ describe('AppLoading', () => {
|
||||
// Check for brand logo
|
||||
const logo = screen.getByAltText(SIGNOZ_TEXT);
|
||||
expect(logo).toBeInTheDocument();
|
||||
expect(logo).toHaveAttribute('src', 'test-file-stub');
|
||||
expect(logo).toHaveAttribute('src', '/Logos/signoz-brand-logo.svg');
|
||||
|
||||
// Check for brand title
|
||||
const title = screen.getByText(SIGNOZ_TEXT);
|
||||
|
||||
@@ -2,8 +2,6 @@ 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 {
|
||||
@@ -15,7 +13,7 @@ function AuthHeader(): JSX.Element {
|
||||
<header className="auth-header">
|
||||
<div className="auth-header-logo">
|
||||
<img
|
||||
src={signozBrandLogoUrl}
|
||||
src="/Logos/signoz-brand-logo.svg"
|
||||
alt="SigNoz"
|
||||
className="auth-header-logo-icon"
|
||||
/>
|
||||
|
||||
@@ -34,7 +34,7 @@ const mockChangelog: ChangelogSchema = {
|
||||
id: 1,
|
||||
documentId: 'doc1',
|
||||
ext: '.webp',
|
||||
url: 'assets/uploads/feature1.webp',
|
||||
url: '/uploads/feature1.webp',
|
||||
mime: 'image/webp',
|
||||
alternativeText: null,
|
||||
},
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
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';
|
||||
@@ -19,7 +16,7 @@ function Containers(): JSX.Element {
|
||||
<div className="dev-status-container">
|
||||
<div className="infra-container-card">
|
||||
<img
|
||||
src={infraContainersUrl}
|
||||
src="/Icons/infraContainers.svg"
|
||||
alt="infra-container"
|
||||
width={32}
|
||||
height={32}
|
||||
@@ -32,7 +29,7 @@ function Containers(): JSX.Element {
|
||||
|
||||
<div className="infra-container-working-msg">
|
||||
<Space>
|
||||
<img src={broomUrl} alt="broom" width={24} height={24} />
|
||||
<img src="/Icons/broom.svg" alt="broom" width={24} height={24} />
|
||||
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
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';
|
||||
@@ -19,7 +16,7 @@ function Processes(): JSX.Element {
|
||||
<div className="dev-status-container">
|
||||
<div className="infra-container-card">
|
||||
<img
|
||||
src={infraContainersUrl}
|
||||
src="/Icons/infraContainers.svg"
|
||||
alt="infra-container"
|
||||
width={32}
|
||||
height={32}
|
||||
@@ -31,7 +28,7 @@ function Processes(): JSX.Element {
|
||||
|
||||
<div className="infra-container-working-msg">
|
||||
<Space>
|
||||
<img src={broomUrl} alt="broom" width={24} height={24} />
|
||||
<img src="/Icons/broom.svg" alt="broom" width={24} height={24} />
|
||||
<Text className="infra-container-card-text">{t('working_message')}</Text>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
@@ -101,7 +101,7 @@ exports[`Not Found page test should render Not Found page without errors 1`] = `
|
||||
>
|
||||
<img
|
||||
alt="not-found"
|
||||
src="test-file-stub"
|
||||
src="/Images/notFound404.png"
|
||||
style="max-height: 480px; max-width: 480px;"
|
||||
/>
|
||||
<div
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
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={loadingPlaneUrl} alt="wait-icon" />
|
||||
<img
|
||||
className="loading-gif"
|
||||
src="/Icons/loading-plane.gif"
|
||||
alt="wait-icon"
|
||||
/>
|
||||
|
||||
<Typography.Text>Fetching data...</Typography.Text>
|
||||
</div>
|
||||
|
||||
@@ -2,8 +2,6 @@ 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;
|
||||
@@ -18,7 +16,7 @@ function WelcomeLeftContainer({
|
||||
<Container>
|
||||
<LeftContainer direction="vertical">
|
||||
<Space align="center">
|
||||
<Logo src={signozBrandLogoUrl} alt="logo" />
|
||||
<Logo src="/Logos/signoz-brand-logo.svg" alt="logo" />
|
||||
<Title style={{ fontSize: '46px', margin: 0 }}>SigNoz</Title>
|
||||
</Space>
|
||||
<Typography>{t('monitor_signup')}</Typography>
|
||||
|
||||
@@ -7,8 +7,6 @@ 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';
|
||||
@@ -25,7 +23,7 @@ function OnNoPermissionsFallback(response: {
|
||||
return (
|
||||
<div className="guard-authz-error-no-authz">
|
||||
<div className="guard-authz-error-no-authz-content">
|
||||
<img src={noDataUrl} alt="No permission" />
|
||||
<img src="/Icons/no-data.svg" alt="No permission" />
|
||||
<h3>Uh-oh! You don’t have permission to view this page.</h3>
|
||||
<p>
|
||||
You need the following permission to view this page:
|
||||
|
||||
@@ -24,8 +24,6 @@ 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';
|
||||
@@ -211,7 +209,7 @@ function TopErrors({
|
||||
<div className="no-filtered-endpoints-message-container">
|
||||
<div className="no-filtered-endpoints-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -10,8 +10,6 @@ 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';
|
||||
@@ -80,7 +78,7 @@ function DependentServices({
|
||||
<div className="no-status-code-data-message-container">
|
||||
<div className="no-status-code-data-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
|
||||
import noDataUrl from '@/assets/Icons/no-data.svg';
|
||||
|
||||
import EndPointsDropDown from './EndPointsDropDown';
|
||||
|
||||
function EndPointDetailsZeroState({
|
||||
@@ -16,7 +14,7 @@ function EndPointDetailsZeroState({
|
||||
<div className="end-point-details-zero-state-wrapper">
|
||||
<div className="end-point-details-zero-state-content">
|
||||
<img
|
||||
src={noDataUrl}
|
||||
src="/Icons/no-data.svg"
|
||||
alt="no-data"
|
||||
width={32}
|
||||
height={32}
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
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={awwSnapUrl} alt="awwSnap" width={32} height={32} />
|
||||
<img src="/Icons/awwSnap.svg" alt="awwSnap" width={32} height={32} />
|
||||
</div>
|
||||
<div className="error-state-text">
|
||||
<Typography.Text>Uh-oh :/ We ran into an error.</Typography.Text>
|
||||
|
||||
@@ -7,8 +7,6 @@ import {
|
||||
} from 'container/ApiMonitoring/utils';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
|
||||
import emptyStateUrl from '@/assets/Icons/emptyState.svg';
|
||||
|
||||
import ErrorState from './ErrorState';
|
||||
|
||||
function StatusCodeTable({
|
||||
@@ -54,7 +52,7 @@ function StatusCodeTable({
|
||||
<div className="no-status-code-data-message-container">
|
||||
<div className="no-status-code-data-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -23,8 +23,6 @@ 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';
|
||||
@@ -134,7 +132,7 @@ function DomainList(): JSX.Element {
|
||||
<div className="no-filtered-domains-message-container">
|
||||
<div className="no-filtered-domains-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
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';
|
||||
@@ -15,13 +12,13 @@ function HeroSection(): JSX.Element {
|
||||
style={
|
||||
isDarkMode
|
||||
? {
|
||||
backgroundImage: `url('${integrationsHeroBgUrl}')`,
|
||||
backgroundImage: `url('/Images/integrations-hero-bg.png')`,
|
||||
}
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<div className="hero-section__icon">
|
||||
<img src={awsDarkUrl} alt="aws-logo" />
|
||||
<img src="/Logos/aws-dark.svg" alt="aws-logo" />
|
||||
</div>
|
||||
<div className="hero-section__details">
|
||||
<div className="title">Amazon Web Services</div>
|
||||
|
||||
@@ -3,8 +3,6 @@ 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 {
|
||||
@@ -38,7 +36,7 @@ export function SuccessView(): JSX.Element {
|
||||
)}
|
||||
<div className="cloud-account-setup-success-view">
|
||||
<div className="cloud-account-setup-success-view__icon">
|
||||
<img src={solidCheckCircleUrl} alt="Success" />
|
||||
<img src="Icons/solid-check-circle.svg" alt="Success" />
|
||||
</div>
|
||||
<div className="cloud-account-setup-success-view__content">
|
||||
<div className="cloud-account-setup-success-view__title">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useCallback, useMemo, useRef } from 'react';
|
||||
import ChartLayout from 'container/DashboardContainer/visualization/layout/ChartLayout/ChartLayout';
|
||||
import Legend from 'lib/uPlotV2/components/Legend/Legend';
|
||||
import {
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import UPlotChart from 'lib/uPlotV2/components/UPlotChart/UPlotChart';
|
||||
import { PlotContextProvider } from 'lib/uPlotV2/context/PlotContext';
|
||||
import TooltipPlugin from 'lib/uPlotV2/plugins/TooltipPlugin/TooltipPlugin';
|
||||
import { CursorSyncPanelMetadata } from 'lib/uPlotV2/plugins/TooltipPlugin/types';
|
||||
import noop from 'lodash-es/noop';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
@@ -25,9 +26,9 @@ export default function ChartWrapper({
|
||||
showTooltip = true,
|
||||
showLegend = true,
|
||||
canPinTooltip = false,
|
||||
pinKey,
|
||||
syncMode,
|
||||
syncKey,
|
||||
yAxisUnit,
|
||||
onDestroy = noop,
|
||||
children,
|
||||
layoutChildren,
|
||||
@@ -35,6 +36,9 @@ export default function ChartWrapper({
|
||||
pinnedTooltipElement,
|
||||
'data-testid': testId,
|
||||
}: ChartProps): JSX.Element {
|
||||
const panelMetadata = useMemo<CursorSyncPanelMetadata>(() => ({ yAxisUnit }), [
|
||||
yAxisUnit,
|
||||
]);
|
||||
const plotInstanceRef = useRef<uPlot | null>(null);
|
||||
|
||||
const legendComponent = useCallback(
|
||||
@@ -94,13 +98,13 @@ export default function ChartWrapper({
|
||||
<TooltipPlugin
|
||||
config={config}
|
||||
canPinTooltip={canPinTooltip}
|
||||
pinKey={pinKey}
|
||||
syncMode={syncMode}
|
||||
maxWidth={Math.max(
|
||||
TOOLTIP_MIN_WIDTH,
|
||||
averageLegendWidth + TOOLTIP_WIDTH_PADDING,
|
||||
)}
|
||||
syncKey={syncKey}
|
||||
panelMetadata={panelMetadata}
|
||||
render={renderTooltipCallback}
|
||||
pinnedTooltipElement={pinnedTooltipElement}
|
||||
/>
|
||||
|
||||
@@ -14,8 +14,6 @@ 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;
|
||||
|
||||
@@ -121,7 +121,6 @@ function BarPanel(props: PanelWrapperProps): JSX.Element {
|
||||
legendConfig={{
|
||||
position: widget?.legendPosition ?? LegendPosition.BOTTOM,
|
||||
}}
|
||||
canPinTooltip
|
||||
plotRef={onPlotRef}
|
||||
onDestroy={onPlotDestroy}
|
||||
yAxisUnit={widget.yAxisUnit}
|
||||
|
||||
@@ -112,7 +112,6 @@ function TimeSeriesPanel(props: PanelWrapperProps): JSX.Element {
|
||||
legendConfig={{
|
||||
position: widget?.legendPosition ?? LegendPosition.BOTTOM,
|
||||
}}
|
||||
canPinTooltip
|
||||
yAxisUnit={widget.yAxisUnit}
|
||||
decimalPrecision={widget.decimalPrecision}
|
||||
timezone={timezone}
|
||||
|
||||
@@ -7,8 +7,6 @@ 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 {
|
||||
@@ -48,7 +46,7 @@ export default function EmptyLogsSearch({
|
||||
<div className="empty-logs-search__row">
|
||||
<div className="empty-logs-search__content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -11,8 +11,6 @@ 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';
|
||||
@@ -133,7 +131,7 @@ function ForgotPassword({
|
||||
>
|
||||
<div className="login-form-header">
|
||||
<div className="login-form-emoji">
|
||||
<img src={tvUrl} alt="TV" width="32" height="32" />
|
||||
<img src="/svgs/tv.svg" alt="TV" width="32" height="32" />
|
||||
</div>
|
||||
<h4 className="forgot-password-title">Forgot your password?</h4>
|
||||
<p className="forgot-password-description">
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import history from 'lib/history';
|
||||
|
||||
import signozBrandLogoUrl from '@/assets/Logos/signoz-brand-logo.svg';
|
||||
|
||||
import './FullScreenHeader.styles.scss';
|
||||
|
||||
export default function FullScreenHeader({
|
||||
@@ -15,7 +13,7 @@ export default function FullScreenHeader({
|
||||
return (
|
||||
<div className="full-screen-header-container">
|
||||
<div className="brand-logo" onClick={handleLogoClick}>
|
||||
<img src={signozBrandLogoUrl} alt="SigNoz" />
|
||||
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
|
||||
|
||||
<div className="brand-logo-name">SigNoz</div>
|
||||
</div>
|
||||
|
||||
@@ -16,10 +16,6 @@ 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 {
|
||||
@@ -76,7 +72,7 @@ export default function DashboardEmptyState(): JSX.Element {
|
||||
<div className="dashboard-content">
|
||||
<section className="heading">
|
||||
<img
|
||||
src={dashboardEmojiUrl}
|
||||
src="/Icons/dashboard_emoji.svg"
|
||||
alt="header-image"
|
||||
style={{ height: '32px', width: '32px' }}
|
||||
/>
|
||||
@@ -92,7 +88,7 @@ export default function DashboardEmptyState(): JSX.Element {
|
||||
<div className="actions-configure">
|
||||
<div className="actions-configure-text">
|
||||
<img
|
||||
src={toolsUrl}
|
||||
src="/Icons/tools.svg"
|
||||
alt="header-image"
|
||||
style={{ height: '14px', width: '14px' }}
|
||||
/>
|
||||
@@ -129,7 +125,7 @@ export default function DashboardEmptyState(): JSX.Element {
|
||||
<div className="actions-add-panel">
|
||||
<div className="actions-panel-text">
|
||||
<img
|
||||
src={landscapeUrl}
|
||||
src="/Icons/landscape.svg"
|
||||
alt="header-image"
|
||||
style={{ height: '14px', width: '14px' }}
|
||||
/>
|
||||
|
||||
@@ -14,10 +14,6 @@ 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,
|
||||
@@ -69,7 +65,11 @@ export default function AlertRules({
|
||||
<div className="empty-state-container">
|
||||
<div className="empty-state-content-container">
|
||||
<div className="empty-state-content">
|
||||
<img src={beaconUrl} alt="empty-alert-icon" className="empty-state-icon" />
|
||||
<img
|
||||
src="/Icons/beacon.svg"
|
||||
alt="empty-alert-icon"
|
||||
className="empty-state-icon"
|
||||
/>
|
||||
|
||||
<div className="empty-title">No Alert rules yet.</div>
|
||||
|
||||
@@ -156,7 +156,11 @@ export default function AlertRules({
|
||||
>
|
||||
<div className="alert-rule-item-name-container home-data-item-name-container">
|
||||
<img
|
||||
src={getItemIcon(rule.id)}
|
||||
src={
|
||||
Math.random() % 2 === 0
|
||||
? '/Icons/eight-ball.svg'
|
||||
: '/Icons/circus-tent.svg'
|
||||
}
|
||||
alt="alert-rules"
|
||||
className="alert-rules-img"
|
||||
/>
|
||||
|
||||
@@ -11,10 +11,6 @@ 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,
|
||||
@@ -56,7 +52,11 @@ export default function Dashboards({
|
||||
<div className="empty-state-container">
|
||||
<div className="empty-state-content-container">
|
||||
<div className="empty-state-content">
|
||||
<img src={dialsUrl} alt="empty-alert-icon" className="empty-state-icon" />
|
||||
<img
|
||||
src="/Icons/dials.svg"
|
||||
alt="empty-alert-icon"
|
||||
className="empty-state-icon"
|
||||
/>
|
||||
|
||||
<div className="empty-title">You don’t have any dashboards yet.</div>
|
||||
|
||||
@@ -135,7 +135,11 @@ export default function Dashboards({
|
||||
>
|
||||
<div className="dashboard-item-name-container home-data-item-name-container">
|
||||
<img
|
||||
src={getItemIcon(dashboard.id)}
|
||||
src={
|
||||
Math.random() % 2 === 0
|
||||
? '/Icons/eight-ball.svg'
|
||||
: '/Icons/circus-tent.svg'
|
||||
}
|
||||
alt="alert-rules"
|
||||
className="alert-rules-img"
|
||||
/>
|
||||
|
||||
@@ -10,10 +10,6 @@ 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({
|
||||
@@ -72,7 +68,7 @@ function DataSourceInfo({
|
||||
<div className="workspace-ready-container">
|
||||
<div className="workspace-ready-header">
|
||||
<span className="workspace-ready-title">
|
||||
<img src={hurrayUrl} alt="hurray" />
|
||||
<img src="/Icons/hurray.svg" alt="hurray" />
|
||||
Your workspace is ready
|
||||
</span>
|
||||
|
||||
@@ -81,7 +77,7 @@ function DataSourceInfo({
|
||||
color="primary"
|
||||
size="sm"
|
||||
className="periscope-btn primary"
|
||||
prefixIcon={<img src={containerPlusUrl} alt="plus" />}
|
||||
prefixIcon={<img src="/Icons/container-plus.svg" alt="plus" />}
|
||||
onClick={handleConnect}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
@@ -139,7 +135,7 @@ function DataSourceInfo({
|
||||
<div className="hello-wave-container">
|
||||
<div className="hello-wave-img-container">
|
||||
<img
|
||||
src={helloWaveUrl}
|
||||
src="/Icons/hello-wave.svg"
|
||||
alt="hello-wave"
|
||||
className="hello-wave-img"
|
||||
width={36}
|
||||
|
||||
@@ -33,15 +33,6 @@ 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';
|
||||
@@ -322,7 +313,7 @@ export default function Home(): JSX.Element {
|
||||
className="periscope-btn secondary welcome-checklist-btn"
|
||||
>
|
||||
<img
|
||||
src={spinnerHalfBlueUrl}
|
||||
src="/Icons/spinner-half-blue.svg"
|
||||
alt="spinner-half-blue"
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -349,7 +340,7 @@ export default function Home(): JSX.Element {
|
||||
/>
|
||||
|
||||
<div className="divider">
|
||||
<img src={dottedDividerUrl} alt="divider" />
|
||||
<img src="/Images/dotted-divider.svg" alt="divider" />
|
||||
</div>
|
||||
|
||||
<div className="active-ingestions-container">
|
||||
@@ -492,7 +483,7 @@ export default function Home(): JSX.Element {
|
||||
<div className="section-content">
|
||||
<div className="section-icon">
|
||||
<img
|
||||
src={wrenchUrl}
|
||||
src="/Icons/wrench.svg"
|
||||
alt="wrench"
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -567,7 +558,12 @@ export default function Home(): JSX.Element {
|
||||
<div className="section-container">
|
||||
<div className="section-content">
|
||||
<div className="section-icon">
|
||||
<img src={dashboardUrl} alt="dashboard" width={16} height={16} />
|
||||
<img
|
||||
src="/Icons/dashboard.svg"
|
||||
alt="dashboard"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="section-title">
|
||||
@@ -606,7 +602,7 @@ export default function Home(): JSX.Element {
|
||||
<div className="section-content">
|
||||
<div className="section-icon">
|
||||
<img
|
||||
src={crackerUrl}
|
||||
src="/Icons/cracker.svg"
|
||||
alt="cracker"
|
||||
width={16}
|
||||
height={16}
|
||||
@@ -685,7 +681,7 @@ export default function Home(): JSX.Element {
|
||||
<div className="checklist-container-right-img">
|
||||
<div className="checklist-img-bg-container">
|
||||
<img
|
||||
src={perilianBackgroundUrl}
|
||||
src="/Images/perilianBackground.svg"
|
||||
alt="not-found"
|
||||
className="checklist-img-bg"
|
||||
/>
|
||||
@@ -693,7 +689,11 @@ export default function Home(): JSX.Element {
|
||||
|
||||
<div className="checklist-img-container">
|
||||
<img
|
||||
src={isDarkMode ? allInOneUrl : allInOneLightModeUrl}
|
||||
src={
|
||||
isDarkMode
|
||||
? '/Images/allInOne.svg'
|
||||
: '/Images/allInOneLightMode.svg'
|
||||
}
|
||||
alt="checklist-img"
|
||||
className="checklist-img"
|
||||
/>
|
||||
|
||||
@@ -20,11 +20,6 @@ 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,
|
||||
@@ -156,7 +151,7 @@ export default function SavedViews({
|
||||
<div className="empty-state-content-container">
|
||||
<div className="empty-state-content">
|
||||
<img
|
||||
src={floppyDiscUrl}
|
||||
src="/Icons/floppy-disc.svg"
|
||||
alt="empty-alert-icon"
|
||||
className="empty-state-icon"
|
||||
/>
|
||||
@@ -229,7 +224,9 @@ export default function SavedViews({
|
||||
>
|
||||
<div className="saved-view-item-name-container home-data-item-name-container">
|
||||
<img
|
||||
src={getItemIcon(String(view.id))}
|
||||
src={
|
||||
view.id % 2 === 0 ? '/Icons/eight-ball.svg' : '/Icons/circus-tent.svg'
|
||||
}
|
||||
alt="alert-rules"
|
||||
className="alert-rules-img"
|
||||
/>
|
||||
@@ -348,7 +345,7 @@ export default function SavedViews({
|
||||
className={selectedEntity === 'logs' ? 'selected tab' : 'tab'}
|
||||
onClick={(): void => handleTabChange('logs')}
|
||||
>
|
||||
<img src={logsUrl} alt="logs-icon" className="logs-icon" />
|
||||
<img src="/Icons/logs.svg" alt="logs-icon" className="logs-icon" />
|
||||
Logs
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -32,8 +32,6 @@ 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';
|
||||
@@ -53,7 +51,7 @@ const EmptyState = memo(
|
||||
<div className="empty-state-content-container">
|
||||
<div className="empty-state-content">
|
||||
<img
|
||||
src={triangleRulerUrl}
|
||||
src="/Icons/triangle-ruler.svg"
|
||||
alt="empty-alert-icon"
|
||||
className="empty-state-icon"
|
||||
/>
|
||||
|
||||
@@ -18,8 +18,6 @@ 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';
|
||||
|
||||
@@ -105,7 +103,7 @@ export default function ServiceTraces({
|
||||
<div className="empty-state-content-container">
|
||||
<div className="empty-state-content">
|
||||
<img
|
||||
src={triangleRulerUrl}
|
||||
src="/Icons/triangle-ruler.svg"
|
||||
alt="empty-alert-icon"
|
||||
className="empty-state-icon"
|
||||
/>
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
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,
|
||||
|
||||
@@ -2,8 +2,6 @@ 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 {
|
||||
@@ -11,7 +9,11 @@ export function HostMetricsLoading(): JSX.Element {
|
||||
return (
|
||||
<div className="loading-host-metrics">
|
||||
<div className="loading-host-metrics-content">
|
||||
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
|
||||
<img
|
||||
className="loading-gif"
|
||||
src="/Icons/loading-plane.gif"
|
||||
alt="wait-icon"
|
||||
/>
|
||||
|
||||
<Typography>
|
||||
{t('pending_data_placeholder', {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Typography } from 'antd';
|
||||
|
||||
import eyesEmojiUrl from '@/assets/Images/eyesEmoji.svg';
|
||||
|
||||
export default function HostsEmptyOrIncorrectMetrics({
|
||||
noData,
|
||||
incorrectData,
|
||||
@@ -12,7 +10,7 @@ export default function HostsEmptyOrIncorrectMetrics({
|
||||
return (
|
||||
<div className="hosts-empty-state-container">
|
||||
<div className="hosts-empty-state-container-content">
|
||||
<img className="eyes-emoji" src={eyesEmojiUrl} alt="eyes emoji" />
|
||||
<img className="eyes-emoji" src="/Images/eyesEmoji.svg" alt="eyes emoji" />
|
||||
|
||||
{noData && (
|
||||
<div className="no-hosts-message">
|
||||
|
||||
@@ -15,9 +15,6 @@ 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,
|
||||
@@ -77,7 +74,7 @@ function EmptyOrLoadingView(
|
||||
return (
|
||||
<div className="hosts-empty-state-container">
|
||||
<div className="hosts-empty-state-container-content">
|
||||
<img className="eyes-emoji" src={eyesEmojiUrl} alt="eyes emoji" />
|
||||
<img className="eyes-emoji" src="/Images/eyesEmoji.svg" 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
|
||||
@@ -96,7 +93,7 @@ function EmptyOrLoadingView(
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -26,8 +26,6 @@ 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 {
|
||||
@@ -657,7 +655,7 @@ function K8sClustersList({
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -27,8 +27,6 @@ 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 {
|
||||
@@ -679,7 +677,7 @@ function K8sDaemonSetsList({
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -29,8 +29,6 @@ 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,
|
||||
@@ -686,7 +684,7 @@ function K8sDeploymentsList({
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -29,8 +29,6 @@ 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,
|
||||
@@ -647,7 +645,7 @@ function K8sJobsList({
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Typography } from 'antd';
|
||||
|
||||
import eyesEmojiUrl from '@/assets/Images/eyesEmoji.svg';
|
||||
|
||||
export default function HostsEmptyOrIncorrectMetrics({
|
||||
noData,
|
||||
incorrectData,
|
||||
@@ -12,7 +10,7 @@ export default function HostsEmptyOrIncorrectMetrics({
|
||||
return (
|
||||
<div className="hosts-empty-state-container">
|
||||
<div className="hosts-empty-state-container-content">
|
||||
<img className="eyes-emoji" src={eyesEmojiUrl} alt="eyes emoji" />
|
||||
<img className="eyes-emoji" src="/Images/eyesEmoji.svg" alt="eyes emoji" />
|
||||
|
||||
{noData && (
|
||||
<div className="no-hosts-message">
|
||||
|
||||
@@ -28,8 +28,6 @@ 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 +679,7 @@ function K8sNamespacesList({
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -28,8 +28,6 @@ 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,
|
||||
@@ -659,7 +657,7 @@ function K8sNodesList({
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -31,8 +31,6 @@ 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,
|
||||
@@ -714,7 +712,7 @@ function K8sPodsList({
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -29,8 +29,6 @@ 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,
|
||||
@@ -683,7 +681,7 @@ function K8sStatefulSetsList({
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -29,8 +29,6 @@ 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,
|
||||
@@ -638,7 +636,7 @@ function K8sVolumesList({
|
||||
<div className="no-filtered-hosts-message-container">
|
||||
<div className="no-filtered-hosts-message-content">
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -9,8 +9,6 @@ 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';
|
||||
@@ -61,7 +59,7 @@ export function AlertsEmptyState(): JSX.Element {
|
||||
<div className="alert-content">
|
||||
<section className="heading">
|
||||
<img
|
||||
src={alertEmojiUrl}
|
||||
src="/Icons/alert_emoji.svg"
|
||||
alt="alert-header"
|
||||
style={{ height: '32px', width: '32px' }}
|
||||
/>
|
||||
|
||||
@@ -15,9 +15,6 @@ 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';
|
||||
@@ -28,91 +25,91 @@ const templatesList: DashboardTemplate[] = [
|
||||
icon: <Drill />,
|
||||
id: 'blank',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Alert Manager',
|
||||
icon: <ConciergeBell />,
|
||||
id: 'alertManager',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Apache',
|
||||
icon: <ApacheIcon />,
|
||||
id: 'apache',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Docker',
|
||||
icon: <DockerIcon />,
|
||||
id: 'docker',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Elasticsearch',
|
||||
icon: <ElasticSearchIcon />,
|
||||
id: 'elasticSearch',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'MongoDB',
|
||||
icon: <MongoDBIcon />,
|
||||
id: 'mongoDB',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Heroku',
|
||||
icon: <HerokuIcon />,
|
||||
id: 'heroku',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Nginx',
|
||||
icon: <NginxIcon />,
|
||||
id: 'nginx',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Kubernetes',
|
||||
icon: <KubernetesIcon />,
|
||||
id: 'kubernetes',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'MySQL',
|
||||
icon: <MySQLIcon />,
|
||||
id: 'mySQL',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'PostgreSQL',
|
||||
icon: <PostgreSQLIcon />,
|
||||
id: 'postgreSQL',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'Redis',
|
||||
icon: <RedisIcon />,
|
||||
id: 'redis',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: redisTemplatePreviewUrl,
|
||||
previewImage: '/Images/redisTemplatePreview.svg',
|
||||
},
|
||||
{
|
||||
name: 'AWS',
|
||||
icon: <DraftingCompass size={14} />,
|
||||
id: 'aws',
|
||||
description: 'Create a custom dashboard from scratch.',
|
||||
previewImage: blankDashboardTemplatePreviewUrl,
|
||||
previewImage: '/Images/blankDashboardTemplatePreview.svg',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -84,10 +84,6 @@ 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';
|
||||
@@ -679,7 +675,11 @@ function DashboardsList(): JSX.Element {
|
||||
</div>
|
||||
) : dashboardFetchError ? (
|
||||
<div className="dashboard-error-state">
|
||||
<img src={awwSnapUrl} alt="something went wrong" className="error-img" />
|
||||
<img
|
||||
src="/Icons/awwSnap.svg"
|
||||
alt="something went wrong"
|
||||
className="error-img"
|
||||
/>
|
||||
|
||||
<Typography.Text className="error-text">
|
||||
Something went wrong :/ Please retry or contact support.
|
||||
@@ -705,7 +705,11 @@ function DashboardsList(): JSX.Element {
|
||||
</div>
|
||||
) : dashboards.length === 0 && !searchString ? (
|
||||
<div className="dashboard-empty-state">
|
||||
<img src={dashboardsUrl} alt="dashboards" className="dashboard-img" />
|
||||
<img
|
||||
src="/Icons/dashboards.svg"
|
||||
alt="dashboards"
|
||||
className="dashboard-img"
|
||||
/>
|
||||
<section className="text">
|
||||
<Typography.Text className="no-dashboard">
|
||||
No dashboards yet.{' '}
|
||||
@@ -785,7 +789,7 @@ function DashboardsList(): JSX.Element {
|
||||
|
||||
{dashboards.length === 0 ? (
|
||||
<div className="no-search">
|
||||
<img src={emptyStateUrl} alt="img" className="img" />
|
||||
<img src="/Icons/emptyState.svg" alt="img" className="img" />
|
||||
<Typography.Text className="text">
|
||||
No dashboards found for {searchString}. Create a new dashboard?
|
||||
</Typography.Text>
|
||||
|
||||
@@ -21,8 +21,6 @@ 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';
|
||||
@@ -139,7 +137,11 @@ function LiveLogsList({
|
||||
() => (
|
||||
<div className="live-logs-list-loading">
|
||||
<div className="loading-live-logs-content">
|
||||
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
|
||||
<img
|
||||
className="loading-gif"
|
||||
src="/Icons/loading-plane.gif"
|
||||
alt="wait-icon"
|
||||
/>
|
||||
|
||||
<Typography>Fetching live logs...</Typography>
|
||||
</div>
|
||||
|
||||
@@ -15,8 +15,6 @@ 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';
|
||||
@@ -307,7 +305,7 @@ function Login(): JSX.Element {
|
||||
<FormContainer form={form} onFinish={onSubmitHandler}>
|
||||
<div className="login-form-header">
|
||||
<div className="login-form-emoji">
|
||||
<img src={tvUrl} alt="TV" width="32" height="32" />
|
||||
<img src="/svgs/tv.svg" alt="TV" width="32" height="32" />
|
||||
</div>
|
||||
<Typography.Title level={4} className="login-form-title">
|
||||
Sign in to your workspace
|
||||
|
||||
@@ -3,8 +3,6 @@ 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 {
|
||||
@@ -21,7 +19,11 @@ export default function LogsError(): JSX.Element {
|
||||
return (
|
||||
<div className="logs-error-container">
|
||||
<div className="logs-error-content">
|
||||
<img src={awwSnapUrl} alt="error-emoji" className="error-state-svg" />
|
||||
<img
|
||||
src="/Icons/awwSnap.svg"
|
||||
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.
|
||||
|
||||
@@ -4,8 +4,6 @@ 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 {
|
||||
@@ -26,7 +24,7 @@ export default function QueryStatus(
|
||||
if (error) {
|
||||
return (
|
||||
<img
|
||||
src={solidXCircleUrl}
|
||||
src="/Icons/solid-x-circle.svg"
|
||||
alt="header"
|
||||
className="error"
|
||||
style={{ height: '14px', width: '14px' }}
|
||||
|
||||
@@ -2,8 +2,6 @@ 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 {
|
||||
@@ -11,7 +9,11 @@ export function LogsLoading(): JSX.Element {
|
||||
return (
|
||||
<div className="loading-logs">
|
||||
<div className="loading-logs-content">
|
||||
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
|
||||
<img
|
||||
className="loading-gif"
|
||||
src="/Icons/loading-plane.gif"
|
||||
alt="wait-icon"
|
||||
/>
|
||||
|
||||
<Typography>
|
||||
{t('pending_data_placeholder', { dataSource: DataSource.LOGS })}
|
||||
|
||||
@@ -2,8 +2,6 @@ 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 {
|
||||
@@ -11,7 +9,11 @@ export function MetricsLoading(): JSX.Element {
|
||||
return (
|
||||
<div className="loading-metrics">
|
||||
<div className="loading-metrics-content">
|
||||
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
|
||||
<img
|
||||
className="loading-gif"
|
||||
src="/Icons/loading-plane.gif"
|
||||
alt="wait-icon"
|
||||
/>
|
||||
|
||||
<Typography>
|
||||
{t('pending_data_placeholder', { dataSource: DataSource.METRICS })}
|
||||
|
||||
@@ -13,8 +13,6 @@ 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';
|
||||
|
||||
@@ -97,7 +95,7 @@ function MetricsTable({
|
||||
data-testid="metrics-table-empty-state"
|
||||
>
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -7,8 +7,6 @@ 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({
|
||||
@@ -52,7 +50,7 @@ export default function NoLogs({
|
||||
return (
|
||||
<div className="no-logs-container">
|
||||
<div className="no-logs-container-content">
|
||||
<img className="eyes-emoji" src={eyesEmojiUrl} alt="eyes emoji" />
|
||||
<img className="eyes-emoji" src="/Images/eyesEmoji.svg" alt="eyes emoji" />
|
||||
<Typography className="no-logs-text">
|
||||
No {dataSource} yet.
|
||||
<span className="sub-text">
|
||||
|
||||
@@ -23,15 +23,6 @@ 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;
|
||||
@@ -148,7 +139,7 @@ export default function ConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="java"
|
||||
heading="Java OpenTelemetry Instrumentation"
|
||||
imgURL={javaPngUrl}
|
||||
imgURL="/Logos/java.png"
|
||||
docsURL="https://signoz.io/docs/instrumentation/java/"
|
||||
imgClassName="supported-language-img"
|
||||
/>
|
||||
@@ -159,7 +150,7 @@ export default function ConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="python"
|
||||
heading="Python OpenTelemetry Instrumentation"
|
||||
imgURL={pythonPngUrl}
|
||||
imgURL="/Logos/python.png"
|
||||
docsURL="https://signoz.io/docs/instrumentation/python/"
|
||||
imgClassName="supported-language-img"
|
||||
/>
|
||||
@@ -170,7 +161,7 @@ export default function ConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="javascript"
|
||||
heading="Javascript OpenTelemetry Instrumentation"
|
||||
imgURL={javascriptPngUrl}
|
||||
imgURL="/Logos/javascript.png"
|
||||
docsURL="https://signoz.io/docs/instrumentation/javascript/"
|
||||
imgClassName="supported-language-img"
|
||||
/>
|
||||
@@ -180,7 +171,7 @@ export default function ConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="go"
|
||||
heading="Go OpenTelemetry Instrumentation"
|
||||
imgURL={goPngUrl}
|
||||
imgURL="/Logos/go.png"
|
||||
docsURL="https://signoz.io/docs/instrumentation/golang/"
|
||||
imgClassName="supported-language-img"
|
||||
/>
|
||||
@@ -190,7 +181,7 @@ export default function ConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="rails"
|
||||
heading="Ruby on Rails OpenTelemetry Instrumentation"
|
||||
imgURL={railsPngUrl}
|
||||
imgURL="/Logos/rails.png"
|
||||
docsURL="https://signoz.io/docs/instrumentation/ruby-on-rails/"
|
||||
imgClassName="supported-language-img"
|
||||
/>
|
||||
@@ -200,7 +191,7 @@ export default function ConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="rust"
|
||||
heading="Rust OpenTelemetry Instrumentation"
|
||||
imgURL={rustPngUrl}
|
||||
imgURL="/Logos/rust.png"
|
||||
docsURL="https://signoz.io/docs/instrumentation/rust/"
|
||||
imgClassName="supported-language-img"
|
||||
/>
|
||||
@@ -210,7 +201,7 @@ export default function ConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="rust"
|
||||
heading="Elixir OpenTelemetry Instrumentation"
|
||||
imgURL={elixirPngUrl}
|
||||
imgURL="/Logos/elixir.png"
|
||||
docsURL="https://signoz.io/docs/instrumentation/elixir/"
|
||||
imgClassName="supported-language-img"
|
||||
/>
|
||||
@@ -220,7 +211,7 @@ export default function ConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="swift"
|
||||
heading="Swift OpenTelemetry Instrumentation"
|
||||
imgURL={swiftPngUrl}
|
||||
imgURL="/Logos/swift.png"
|
||||
docsURL="https://signoz.io/docs/instrumentation/swift/"
|
||||
imgClassName="supported-language-img"
|
||||
/>
|
||||
|
||||
@@ -18,13 +18,6 @@ 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 {
|
||||
@@ -172,7 +165,7 @@ export default function LogsConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="kubernetes"
|
||||
heading="Collecting Kubernetes Pod logs"
|
||||
imgURL={kubernetesSvgUrl}
|
||||
imgURL="/Logos/kubernetes.svg"
|
||||
docsURL="https://signoz.io/docs/userguide/collect_kubernetes_pod_logs/#collect-kubernetes-pod-logs-in-signoz-cloud"
|
||||
imgClassName="supported-logs-type-img"
|
||||
/>
|
||||
@@ -183,7 +176,7 @@ export default function LogsConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="docker"
|
||||
heading="Collecting Docker container logs"
|
||||
imgURL={dockerSvgUrl}
|
||||
imgURL="/Logos/docker.svg"
|
||||
docsURL="https://signoz.io/docs/userguide/collect_docker_logs/"
|
||||
imgClassName="supported-logs-type-img"
|
||||
/>
|
||||
@@ -194,7 +187,7 @@ export default function LogsConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="syslog"
|
||||
heading="Collecting Syslogs"
|
||||
imgURL={syslogsSvgUrl}
|
||||
imgURL="/Logos/syslogs.svg"
|
||||
docsURL="https://signoz.io/docs/userguide/collecting_syslogs/"
|
||||
imgClassName="supported-logs-type-img"
|
||||
/>
|
||||
@@ -204,7 +197,7 @@ export default function LogsConnectionStatus(): JSX.Element {
|
||||
<Header
|
||||
entity="nodejs"
|
||||
heading="Collecting NodeJS winston logs"
|
||||
imgURL={nodeJsSvgUrl}
|
||||
imgURL="/Logos/node-js.svg"
|
||||
docsURL="https://signoz.io/docs/userguide/collecting_nodejs_winston_logs/"
|
||||
imgClassName="supported-logs-type-img"
|
||||
/>
|
||||
@@ -219,11 +212,11 @@ export default function LogsConnectionStatus(): JSX.Element {
|
||||
? 'Collecting Application Logs from Log file'
|
||||
: 'Collecting Application Logs Using OTEL SDK'
|
||||
}
|
||||
imgURL={
|
||||
imgURL={`/Logos/${
|
||||
logType === ApplicationLogsType.FROM_LOG_FILE
|
||||
? softwareWindowSvgUrl
|
||||
: cmdTerminalSvgUrl
|
||||
}
|
||||
? 'software-window'
|
||||
: 'cmd-terminal'
|
||||
}.svg`}
|
||||
docsURL={
|
||||
logType === ApplicationLogsType.FROM_LOG_FILE
|
||||
? 'https://signoz.io/docs/userguide/collect_logs_from_file/'
|
||||
|
||||
@@ -17,8 +17,6 @@ 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,
|
||||
@@ -383,7 +381,7 @@ export default function ModuleStepsContainer({
|
||||
<div>
|
||||
<div className="steps-container-header">
|
||||
<div className="brand-logo" onClick={handleLogoClick}>
|
||||
<img src={signozBrandLogoUrl} alt="SigNoz" />
|
||||
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
|
||||
|
||||
<div className="brand-logo-name">SigNoz</div>
|
||||
</div>
|
||||
|
||||
@@ -1,38 +1,5 @@
|
||||
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';
|
||||
|
||||
@@ -122,230 +89,230 @@ export const frameworksMap = {
|
||||
export const defaultApplicationDataSource = {
|
||||
name: 'java',
|
||||
id: 'java',
|
||||
imgURL: javaPngUrl,
|
||||
imgURL: `Logos/java.png`,
|
||||
};
|
||||
|
||||
const supportedLanguages = [
|
||||
{
|
||||
name: 'java',
|
||||
id: 'java',
|
||||
imgURL: javaPngUrl,
|
||||
imgURL: `/Logos/java.png`,
|
||||
},
|
||||
{
|
||||
name: 'python',
|
||||
id: 'python',
|
||||
imgURL: pythonPngUrl,
|
||||
imgURL: `/Logos/python.png`,
|
||||
},
|
||||
{
|
||||
name: 'go',
|
||||
id: 'go',
|
||||
imgURL: goPngUrl,
|
||||
imgURL: `/Logos/go.png`,
|
||||
},
|
||||
{
|
||||
name: 'javascript',
|
||||
id: 'javascript',
|
||||
imgURL: javascriptPngUrl,
|
||||
imgURL: `/Logos/javascript.png`,
|
||||
},
|
||||
{
|
||||
name: 'rails',
|
||||
id: 'rails',
|
||||
imgURL: railsPngUrl,
|
||||
imgURL: `/Logos/rails.png`,
|
||||
},
|
||||
{
|
||||
name: '.NET',
|
||||
id: 'dotnet',
|
||||
imgURL: dotnetPngUrl,
|
||||
imgURL: `/Logos/dotnet.png`,
|
||||
},
|
||||
{
|
||||
name: 'rust',
|
||||
id: 'rust',
|
||||
imgURL: rustPngUrl,
|
||||
imgURL: `/Logos/rust.png`,
|
||||
},
|
||||
{
|
||||
name: 'elixir',
|
||||
id: 'elixir',
|
||||
imgURL: elixirPngUrl,
|
||||
imgURL: `/Logos/elixir.png`,
|
||||
},
|
||||
{
|
||||
name: 'swift',
|
||||
id: 'swift',
|
||||
imgURL: swiftPngUrl,
|
||||
imgURL: `/Logos/swift.png`,
|
||||
},
|
||||
{
|
||||
name: 'php',
|
||||
id: 'php',
|
||||
imgURL: phpPngUrl,
|
||||
imgURL: `/Logos/php.png`,
|
||||
},
|
||||
];
|
||||
|
||||
export const defaultLogsType = {
|
||||
name: 'Kubernetes Pod Logs',
|
||||
id: 'kubernetes',
|
||||
imgURL: kubernetesSvgUrl,
|
||||
imgURL: `/Logos/kubernetes.svg`,
|
||||
};
|
||||
|
||||
const supportedLogsTypes = [
|
||||
{
|
||||
name: 'Kubernetes Pod Logs',
|
||||
id: 'kubernetes',
|
||||
imgURL: kubernetesSvgUrl,
|
||||
imgURL: `/Logos/kubernetes.svg`,
|
||||
},
|
||||
{
|
||||
name: 'Docker Container Logs',
|
||||
id: 'docker',
|
||||
imgURL: dockerSvgUrl,
|
||||
imgURL: `/Logos/docker.svg`,
|
||||
},
|
||||
{
|
||||
name: 'SysLogs',
|
||||
id: 'syslogs',
|
||||
imgURL: syslogsSvgUrl,
|
||||
imgURL: `/Logos/syslogs.svg`,
|
||||
},
|
||||
{
|
||||
name: 'Application Logs',
|
||||
id: 'application_logs',
|
||||
imgURL: softwareWindowSvgUrl,
|
||||
imgURL: `/Logos/software-window.svg`,
|
||||
},
|
||||
{
|
||||
name: 'FluentBit',
|
||||
id: 'fluentBit',
|
||||
imgURL: fluentBitPngUrl,
|
||||
imgURL: `/Logos/fluent-bit.png`,
|
||||
},
|
||||
{
|
||||
name: 'FluentD',
|
||||
id: 'fluentD',
|
||||
imgURL: fluentdPngUrl,
|
||||
imgURL: `/Logos/fluentd.png`,
|
||||
},
|
||||
{
|
||||
name: 'LogStash',
|
||||
id: 'logStash',
|
||||
imgURL: logstashSvgUrl,
|
||||
imgURL: `/Logos/logstash.svg`,
|
||||
},
|
||||
{
|
||||
name: 'Heroku',
|
||||
id: 'heroku',
|
||||
imgURL: herokuPngUrl,
|
||||
imgURL: `/Logos/heroku.png`,
|
||||
},
|
||||
{
|
||||
name: 'Vercel',
|
||||
id: 'vercel',
|
||||
imgURL: vercelPngUrl,
|
||||
imgURL: `/Logos/vercel.png`,
|
||||
},
|
||||
{
|
||||
name: 'HTTP',
|
||||
id: 'http',
|
||||
imgURL: httpPngUrl,
|
||||
imgURL: `/Logos/http.png`,
|
||||
},
|
||||
{
|
||||
name: 'Cloudwatch',
|
||||
id: 'cloudwatch',
|
||||
imgURL: cloudwatchPngUrl,
|
||||
imgURL: `/Logos/cloudwatch.png`,
|
||||
},
|
||||
];
|
||||
|
||||
export const defaultInfraMetricsType = {
|
||||
name: 'Kubernetes Infra Metrics',
|
||||
id: 'kubernetesInfraMetrics',
|
||||
imgURL: kubernetesSvgUrl,
|
||||
imgURL: `/Logos/kubernetes.svg`,
|
||||
};
|
||||
|
||||
const supportedInfraMetrics = [
|
||||
{
|
||||
name: 'Kubernetes Infra Metrics',
|
||||
id: 'kubernetesInfraMetrics',
|
||||
imgURL: kubernetesSvgUrl,
|
||||
imgURL: `/Logos/kubernetes.svg`,
|
||||
},
|
||||
{
|
||||
name: 'HostMetrics',
|
||||
id: 'hostMetrics',
|
||||
imgURL: softwareWindowSvgUrl,
|
||||
imgURL: `/Logos/software-window.svg`,
|
||||
},
|
||||
{
|
||||
name: 'Other Metrics',
|
||||
id: 'otherMetrics',
|
||||
imgURL: cmdTerminalSvgUrl,
|
||||
imgURL: `/Logos/cmd-terminal.svg`,
|
||||
},
|
||||
];
|
||||
|
||||
export const defaultAwsServices = {
|
||||
name: 'EC2 - App/Server Logs',
|
||||
id: 'awsEc2ApplicationLogs',
|
||||
imgURL: ec2SvgUrl,
|
||||
imgURL: `/Logos/ec2.svg`,
|
||||
};
|
||||
|
||||
const supportedAwsServices = [
|
||||
{
|
||||
name: 'EC2 - App/Server Logs',
|
||||
id: 'awsEc2ApplicationLogs',
|
||||
imgURL: ec2SvgUrl,
|
||||
imgURL: `/Logos/ec2.svg`,
|
||||
},
|
||||
{
|
||||
name: 'EC2 - Infra Metrics',
|
||||
id: 'awsEc2InfrastructureMetrics',
|
||||
imgURL: ec2SvgUrl,
|
||||
imgURL: `/Logos/ec2.svg`,
|
||||
},
|
||||
{
|
||||
name: 'ECS - EC2',
|
||||
id: 'awsEcsEc2',
|
||||
imgURL: ecsSvgUrl,
|
||||
imgURL: `/Logos/ecs.svg`,
|
||||
},
|
||||
{
|
||||
name: 'ECS - Fargate',
|
||||
id: 'awsEcsFargate',
|
||||
imgURL: ecsSvgUrl,
|
||||
imgURL: `/Logos/ecs.svg`,
|
||||
},
|
||||
{
|
||||
name: 'ECS - External',
|
||||
id: 'awsEcsExternal',
|
||||
imgURL: ecsSvgUrl,
|
||||
imgURL: `/Logos/ecs.svg`,
|
||||
},
|
||||
{
|
||||
name: 'EKS',
|
||||
id: 'awsEks',
|
||||
imgURL: eksSvgUrl,
|
||||
imgURL: `/Logos/eks.svg`,
|
||||
},
|
||||
];
|
||||
|
||||
export const defaultAzureServices = {
|
||||
name: 'VM',
|
||||
id: 'azureVm',
|
||||
imgURL: azureVmSvgUrl,
|
||||
imgURL: `/Logos/azure-vm.svg`,
|
||||
};
|
||||
|
||||
const supportedAzureServices = [
|
||||
{
|
||||
name: 'VM',
|
||||
id: 'azureVm',
|
||||
imgURL: azureVmSvgUrl,
|
||||
imgURL: `/Logos/azure-vm.svg`,
|
||||
},
|
||||
{
|
||||
name: 'App Service',
|
||||
id: 'azureAppService',
|
||||
imgURL: azureAppServiceSvgUrl,
|
||||
imgURL: `/Logos/azure-app-service.svg`,
|
||||
},
|
||||
{
|
||||
name: 'AKS',
|
||||
id: 'azureAks',
|
||||
imgURL: azureAksSvgUrl,
|
||||
imgURL: `/Logos/azure-aks.svg`,
|
||||
},
|
||||
{
|
||||
name: 'Azure Functions',
|
||||
id: 'azureFunctions',
|
||||
imgURL: azureFunctionsSvgUrl,
|
||||
imgURL: `/Logos/azure-functions.svg`,
|
||||
},
|
||||
{
|
||||
name: 'Azure Container Apps',
|
||||
id: 'azureContainerApps',
|
||||
imgURL: azureContainerAppsSvgUrl,
|
||||
imgURL: `/Logos/azure-container-apps.svg`,
|
||||
},
|
||||
{
|
||||
name: 'SQL Database Metrics',
|
||||
id: 'azureSQLDatabaseMetrics',
|
||||
imgURL: azureSqlDatabaseMetricsSvgUrl,
|
||||
imgURL: `/Logos/azure-sql-database-metrics.svg`,
|
||||
},
|
||||
{
|
||||
name: 'Azure Blob Storage',
|
||||
id: 'azureBlobStorage',
|
||||
imgURL: azureBlobStorageSvgUrl,
|
||||
imgURL: `/Logos/azure-blob-storage.svg`,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@ 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>
|
||||
@@ -24,8 +22,6 @@ 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>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
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={signozBrandLogoUrl} alt="SigNoz" />
|
||||
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
|
||||
<span className="logo-text">SigNoz</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Typography } from 'antd';
|
||||
|
||||
import barberPoolUrl from '@/assets/svgs/barber-pool.svg';
|
||||
|
||||
interface OnboardingQuestionHeaderProps {
|
||||
title: string;
|
||||
subtitle: string;
|
||||
@@ -14,7 +12,7 @@ export function OnboardingQuestionHeader({
|
||||
return (
|
||||
<div className="onboarding-header-section">
|
||||
<div className="onboarding-header-icon">
|
||||
<img src={barberPoolUrl} alt="SigNoz" width="32" height="32" />
|
||||
<img src="/svgs/barber-pool.svg" alt="SigNoz" width="32" height="32" />
|
||||
</div>
|
||||
<Typography.Title level={4} className="onboarding-header-title">
|
||||
{title}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { Tooltip, Typography } from 'antd';
|
||||
import { AxiosError } from 'axios';
|
||||
|
||||
import noDataUrl from '@/assets/Icons/no-data.svg';
|
||||
|
||||
import './Error.styles.scss';
|
||||
|
||||
interface IErrorProps {
|
||||
@@ -15,7 +13,7 @@ function Error(props: IErrorProps): JSX.Element {
|
||||
return (
|
||||
<div className="error-flamegraph">
|
||||
<img
|
||||
src={noDataUrl}
|
||||
src="/Icons/no-data.svg"
|
||||
alt="error-flamegraph"
|
||||
className="error-flamegraph-img"
|
||||
/>
|
||||
|
||||
@@ -18,8 +18,6 @@ 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';
|
||||
@@ -132,7 +130,11 @@ function PublicDashboardContainer({
|
||||
<div className="public-dashboard-header">
|
||||
<div className="public-dashboard-header-left">
|
||||
<div className="brand-logo">
|
||||
<img src={signozBrandLogoUrl} alt="SigNoz" className="brand-logo-img" />
|
||||
<img
|
||||
src="/Logos/signoz-brand-logo.svg"
|
||||
alt="SigNoz"
|
||||
className="brand-logo-img"
|
||||
/>
|
||||
|
||||
<Typography className="brand-logo-name">SigNoz</Typography>
|
||||
</div>
|
||||
|
||||
@@ -2,9 +2,6 @@ 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';
|
||||
|
||||
@@ -39,10 +36,10 @@ function RoutingPolicyList({
|
||||
() => (
|
||||
<div className="no-routing-policies-message-container">
|
||||
{showError ? (
|
||||
<img src={awwSnapUrl} alt="aww-snap" className="error-state-svg" />
|
||||
<img src="/Icons/awwSnap.svg" alt="aww-snap" className="error-state-svg" />
|
||||
) : (
|
||||
<img
|
||||
src={emptyStateUrl}
|
||||
src="/Icons/emptyState.svg"
|
||||
alt="thinking-emoji"
|
||||
className="empty-state-svg"
|
||||
/>
|
||||
|
||||
@@ -66,8 +66,6 @@ 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';
|
||||
@@ -960,7 +958,7 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
|
||||
onClickHandler(ROUTES.HOME, event);
|
||||
}}
|
||||
>
|
||||
<img src={signozBrandLogoUrl} alt="SigNoz" />
|
||||
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
|
||||
</div>
|
||||
|
||||
{licenseTag && (
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Typography } from 'antd';
|
||||
|
||||
import noDataUrl from '@/assets/Icons/no-data.svg';
|
||||
|
||||
import './NoData.styles.scss';
|
||||
|
||||
interface INoDataProps {
|
||||
@@ -13,7 +11,7 @@ function NoData(props: INoDataProps): JSX.Element {
|
||||
|
||||
return (
|
||||
<div className="no-data">
|
||||
<img src={noDataUrl} alt="no-data" className="no-data-img" />
|
||||
<img src="/Icons/no-data.svg" alt="no-data" className="no-data-img" />
|
||||
<Typography.Text className="no-data-text">
|
||||
No {name} found for selected span
|
||||
</Typography.Text>
|
||||
|
||||
@@ -30,8 +30,6 @@ 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 {
|
||||
@@ -235,7 +233,7 @@ function SpanLogs({
|
||||
const renderNoLogsFound = (): JSX.Element => (
|
||||
<div className="span-logs-empty-content">
|
||||
<section className="description">
|
||||
<img src={noDataUrl} alt="no-data" className="no-data-img" />
|
||||
<img src="/Icons/no-data.svg" 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>
|
||||
|
||||
@@ -33,8 +33,6 @@ 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';
|
||||
@@ -191,7 +189,7 @@ function SpanOverview({
|
||||
icon={
|
||||
<img
|
||||
className="add-funnel-button__icon"
|
||||
src={funnelAddUrl}
|
||||
src="/Icons/funnel-add.svg"
|
||||
alt="funnel-icon"
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ 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 {
|
||||
@@ -11,7 +9,11 @@ export function TracesLoading(): JSX.Element {
|
||||
return (
|
||||
<div className="loading-traces">
|
||||
<div className="loading-traces-content">
|
||||
<img className="loading-gif" src={loadingPlaneUrl} alt="wait-icon" />
|
||||
<img
|
||||
className="loading-gif"
|
||||
src="/Icons/loading-plane.gif"
|
||||
alt="wait-icon"
|
||||
/>
|
||||
|
||||
<Typography>
|
||||
{t('pending_data_placeholder', { dataSource: DataSource.TRACES })}
|
||||
|
||||
@@ -4,6 +4,7 @@ import cx from 'classnames';
|
||||
import { getFocusedSeriesAtPosition } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import { syncCursorRegistry } from './syncCursorRegistry';
|
||||
import {
|
||||
createInitialControllerState,
|
||||
createSetCursorHandler,
|
||||
@@ -16,6 +17,7 @@ import {
|
||||
} from './tooltipController';
|
||||
import {
|
||||
DashboardCursorSync,
|
||||
TooltipClickData,
|
||||
TooltipControllerContext,
|
||||
TooltipControllerState,
|
||||
TooltipLayoutInfo,
|
||||
@@ -30,8 +32,6 @@ 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({
|
||||
@@ -41,9 +41,9 @@ export default function TooltipPlugin({
|
||||
maxHeight = 600,
|
||||
syncMode = DashboardCursorSync.None,
|
||||
syncKey = '_tooltip_sync_global_',
|
||||
panelMetadata,
|
||||
pinnedTooltipElement,
|
||||
canPinTooltip = false,
|
||||
pinKey = DEFAULT_PIN_TOOLTIP_KEY,
|
||||
}: TooltipPluginProps): JSX.Element | null {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const rafId = useRef<number | null>(null);
|
||||
@@ -102,7 +102,29 @@ export default function TooltipPlugin({
|
||||
// crosshair / tooltip can follow the dashboard-wide cursor.
|
||||
if (syncMode !== DashboardCursorSync.None && config.scales[0]?.props.time) {
|
||||
config.setCursor({
|
||||
sync: { key: syncKey, scales: ['x', null] },
|
||||
sync: { key: syncKey, scales: ['x', 'y'] },
|
||||
});
|
||||
|
||||
// Show the horizontal crosshair only when the receiving panel shares
|
||||
// the same y-axis unit as the source panel. When this panel is the
|
||||
// source (cursor.event != null) the line is always shown and this
|
||||
// panel's metadata is written to the registry so receivers can read it.
|
||||
config.addHook('setCursor', (u: uPlot): void => {
|
||||
const yCursorEl = u.root.querySelector<HTMLElement>('.u-cursor-y');
|
||||
if (!yCursorEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (u.cursor.event != null) {
|
||||
// This panel is the source — publish metadata and always show line.
|
||||
syncCursorRegistry.setMetadata(syncKey, panelMetadata);
|
||||
yCursorEl.style.display = '';
|
||||
} else {
|
||||
// This panel is receiving sync — show only if units match.
|
||||
const sourceMeta = syncCursorRegistry.getMetadata(syncKey);
|
||||
yCursorEl.style.display =
|
||||
sourceMeta?.yAxisUnit === panelMetadata?.yAxisUnit ? '' : 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -261,73 +283,68 @@ export default function TooltipPlugin({
|
||||
}
|
||||
};
|
||||
|
||||
// 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;
|
||||
}
|
||||
// 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 => {
|
||||
const plot = getPlot(controller);
|
||||
if (
|
||||
!plot ||
|
||||
!controller.hoverActive ||
|
||||
controller.pinned ||
|
||||
controller.focusedSeriesIndex == null
|
||||
plot &&
|
||||
event.target === plot.over &&
|
||||
controller.hoverActive &&
|
||||
!controller.pinned &&
|
||||
controller.focusedSeriesIndex != null
|
||||
) {
|
||||
return;
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
// Called once per uPlot instance; used to store the instance on the controller.
|
||||
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.
|
||||
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,
|
||||
@@ -372,9 +389,6 @@ 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();
|
||||
@@ -382,9 +396,6 @@ 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();
|
||||
@@ -392,6 +403,13 @@ 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
|
||||
@@ -429,12 +447,8 @@ export default function TooltipPlugin({
|
||||
}, [isHovering, hasPlot]);
|
||||
|
||||
const tooltipBody = useMemo(() => {
|
||||
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 (isPinned && pinnedTooltipElement != null && viewState.clickData != null) {
|
||||
return pinnedTooltipElement(viewState.clickData);
|
||||
}
|
||||
|
||||
if (isHovering) {
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import type { CursorSyncPanelMetadata } from './types';
|
||||
|
||||
/**
|
||||
* Module-level registry that tracks the metadata of the panel currently
|
||||
* acting as the cursor source (the one being hovered) per sync group.
|
||||
*
|
||||
* uPlot fires the source panel's setCursor hook before broadcasting to
|
||||
* receivers, so the registry is always populated before receivers read it.
|
||||
*
|
||||
* Receivers use this to make decisions such as:
|
||||
* - Whether to show the horizontal crosshair line (matching yAxisUnit)
|
||||
* - Future: what to render inside the tooltip (matching groupBy, etc.)
|
||||
*/
|
||||
const metadataBySyncKey = new Map<
|
||||
string,
|
||||
CursorSyncPanelMetadata | undefined
|
||||
>();
|
||||
|
||||
export const syncCursorRegistry = {
|
||||
setMetadata(
|
||||
syncKey: string,
|
||||
metadata: CursorSyncPanelMetadata | undefined,
|
||||
): void {
|
||||
metadataBySyncKey.set(syncKey, metadata);
|
||||
},
|
||||
|
||||
getMetadata(syncKey: string): CursorSyncPanelMetadata | undefined {
|
||||
return metadataBySyncKey.get(syncKey);
|
||||
},
|
||||
};
|
||||
@@ -102,12 +102,6 @@ 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 dashboard‑level 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.
|
||||
|
||||
@@ -4,11 +4,29 @@ import type {
|
||||
ReactNode,
|
||||
RefObject,
|
||||
} from 'react';
|
||||
import type { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import type uPlot from 'uplot';
|
||||
|
||||
import type { TooltipRenderArgs } from '../../components/types';
|
||||
import type { UPlotConfigBuilder } from '../../config/UPlotConfigBuilder';
|
||||
|
||||
/**
|
||||
* Per-panel metadata shared with the cursor sync registry so that
|
||||
* panels receiving a synced cursor can make informed decisions about
|
||||
* rendering (e.g. whether to show the horizontal crosshair, what to
|
||||
* show in the tooltip for the source panel's context).
|
||||
*
|
||||
* Add new fields here as sync-aware features need them.
|
||||
*/
|
||||
export interface CursorSyncPanelMetadata {
|
||||
/** Y-axis unit of this panel (e.g. 'ms', 'req/s'). Used to decide
|
||||
* whether the horizontal crosshair should be shown on receiving panels. */
|
||||
yAxisUnit?: string;
|
||||
/** Label dimensions this panel groups by. Will be used to enrich
|
||||
* tooltip content on receiving panels to show correlated series. */
|
||||
groupBy?: BaseAutocompleteData[];
|
||||
}
|
||||
|
||||
export const TOOLTIP_OFFSET = 10;
|
||||
|
||||
export enum DashboardCursorSync {
|
||||
@@ -37,10 +55,11 @@ 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;
|
||||
/** Metadata about this panel shared with the sync registry so that
|
||||
* receiving panels can make context-aware rendering decisions. */
|
||||
panelMetadata?: CursorSyncPanelMetadata;
|
||||
render: (args: TooltipRenderArgs) => ReactNode;
|
||||
pinnedTooltipElement?: (clickData: TooltipClickData) => ReactNode;
|
||||
maxWidth?: number;
|
||||
|
||||
@@ -6,9 +6,7 @@ import type uPlot from 'uplot';
|
||||
|
||||
import { TooltipRenderArgs } from '../../components/types';
|
||||
import { UPlotConfigBuilder } from '../../config/UPlotConfigBuilder';
|
||||
import TooltipPlugin, {
|
||||
DEFAULT_PIN_TOOLTIP_KEY,
|
||||
} from '../TooltipPlugin/TooltipPlugin';
|
||||
import TooltipPlugin from '../TooltipPlugin/TooltipPlugin';
|
||||
import { DashboardCursorSync } from '../TooltipPlugin/types';
|
||||
|
||||
// Avoid depending on the full uPlot + onClickPlugin behaviour in these tests.
|
||||
@@ -62,7 +60,7 @@ function getHandler(config: ConfigMock, hookName: string): HookHandler {
|
||||
function createFakePlot(): {
|
||||
over: HTMLDivElement;
|
||||
setCursor: jest.Mock<void, [uPlot.Cursor]>;
|
||||
cursor: { event: Record<string, unknown>; left: number; top: number };
|
||||
cursor: { event: Record<string, unknown> };
|
||||
posToVal: jest.Mock<number, [value: number]>;
|
||||
posToIdx: jest.Mock<number, []>;
|
||||
data: [number[], number[]];
|
||||
@@ -73,9 +71,7 @@ function createFakePlot(): {
|
||||
return {
|
||||
over,
|
||||
setCursor: jest.fn(),
|
||||
// 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 },
|
||||
cursor: { event: {} },
|
||||
// In real uPlot these map overlay coordinates to data-space values.
|
||||
posToVal: jest.fn((value: number) => value),
|
||||
posToIdx: jest.fn(() => 0),
|
||||
@@ -249,21 +245,18 @@ describe('TooltipPlugin', () => {
|
||||
// ---- Pin behaviour ----------------------------------------------------------
|
||||
|
||||
describe('pin behaviour', () => {
|
||||
it('pins the tooltip when canPinTooltip is true and the pinKey is pressed while hovering', () => {
|
||||
it('pins the tooltip when canPinTooltip is true and overlay is clicked', () => {
|
||||
const config = createConfigMock();
|
||||
|
||||
renderAndActivateHover(config, undefined, { canPinTooltip: true });
|
||||
const fakePlot = renderAndActivateHover(config, undefined, {
|
||||
canPinTooltip: true,
|
||||
});
|
||||
|
||||
const container = screen.getByTestId('tooltip-plugin-container');
|
||||
expect(container.classList.contains('pinned')).toBe(false);
|
||||
|
||||
act(() => {
|
||||
document.body.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: DEFAULT_PIN_TOOLTIP_KEY,
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
});
|
||||
|
||||
return waitFor(() => {
|
||||
@@ -279,7 +272,7 @@ describe('TooltipPlugin', () => {
|
||||
React.createElement('div', null, 'pinned-tooltip'),
|
||||
);
|
||||
|
||||
renderAndActivateHover(
|
||||
const fakePlot = renderAndActivateHover(
|
||||
config,
|
||||
() => React.createElement('div', null, 'hover-tooltip'),
|
||||
{
|
||||
@@ -291,12 +284,7 @@ describe('TooltipPlugin', () => {
|
||||
expect(screen.getByText('hover-tooltip')).toBeInTheDocument();
|
||||
|
||||
act(() => {
|
||||
document.body.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: DEFAULT_PIN_TOOLTIP_KEY,
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -330,14 +318,9 @@ describe('TooltipPlugin', () => {
|
||||
getHandler(config, 'setSeries')(fakePlot, 1, { focus: true });
|
||||
});
|
||||
|
||||
// Pin the tooltip via the keyboard shortcut.
|
||||
// Pin the tooltip.
|
||||
act(() => {
|
||||
document.body.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: DEFAULT_PIN_TOOLTIP_KEY,
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
});
|
||||
|
||||
// Wait until the tooltip is actually pinned (pointer events enabled)
|
||||
@@ -386,14 +369,9 @@ describe('TooltipPlugin', () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
// Pin via keyboard.
|
||||
// Pin.
|
||||
act(() => {
|
||||
document.body.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: DEFAULT_PIN_TOOLTIP_KEY,
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
@@ -439,14 +417,8 @@ describe('TooltipPlugin', () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
// Pin via keyboard.
|
||||
act(() => {
|
||||
document.body.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: DEFAULT_PIN_TOOLTIP_KEY,
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
@@ -495,14 +467,8 @@ describe('TooltipPlugin', () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
// Pin via keyboard.
|
||||
act(() => {
|
||||
document.body.dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: DEFAULT_PIN_TOOLTIP_KEY,
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
fakePlot.over.dispatchEvent(new MouseEvent('click', { bubbles: true }));
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
@@ -532,170 +498,6 @@ 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', () => {
|
||||
@@ -714,7 +516,7 @@ describe('TooltipPlugin', () => {
|
||||
);
|
||||
|
||||
expect(setCursorSpy).toHaveBeenCalledWith({
|
||||
sync: { key: 'dashboard-sync', scales: ['x', null] },
|
||||
sync: { key: 'dashboard-sync', scales: ['x', 'y'] },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@ 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 {
|
||||
@@ -32,7 +28,7 @@ function AlertNotFound({ isTestAlert }: AlertNotFoundProps): JSX.Element {
|
||||
return (
|
||||
<div className="alert-not-found">
|
||||
<section className="description">
|
||||
<img src={noDataUrl} alt="no-data" className="not-found-img" />
|
||||
<img src="/Icons/no-data.svg" alt="no-data" className="not-found-img" />
|
||||
<Typography.Text className="not-found-text">
|
||||
Uh-oh! We couldn't find the given alert rule.
|
||||
</Typography.Text>
|
||||
@@ -46,13 +42,17 @@ function AlertNotFound({ isTestAlert }: AlertNotFoundProps): JSX.Element {
|
||||
{!isTestAlert && (
|
||||
<>
|
||||
<div className="reason">
|
||||
<img src={constructionUrl} alt="no-data" className="construction-img" />
|
||||
<img
|
||||
src="/Icons/construction.svg"
|
||||
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={broomUrl} alt="no-data" className="broom-img" />
|
||||
<img src="/Icons/broom.svg" alt="no-data" className="broom-img" />
|
||||
<Typography.Text className="text">
|
||||
The alert rule you'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={broomUrl} alt="no-data" className="broom-img" />
|
||||
<img src="/Icons/broom.svg" 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
|
||||
|
||||
@@ -5,8 +5,6 @@ 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 {
|
||||
@@ -25,7 +23,7 @@ function ErrorBoundaryFallback(): JSX.Element {
|
||||
<div className="error-boundary-fallback-container">
|
||||
<div className="error-boundary-fallback-content">
|
||||
<div className="error-icon">
|
||||
<img src={cloudUrl} alt="error-cloud-icon" />
|
||||
<img src="/Images/cloud.svg" alt="error-cloud-icon" />
|
||||
</div>
|
||||
<div className="title">Something went wrong :/</div>
|
||||
|
||||
|
||||
@@ -10,16 +10,6 @@ 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[] => {
|
||||
@@ -33,14 +23,14 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
{
|
||||
title: 'Instrument your Java Application',
|
||||
icon: (
|
||||
<img src={`${javaPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
<img src={`/Logos/java.png?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/java/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Python Application',
|
||||
icon: (
|
||||
<img src={`${pythonPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
<img src={`/Logos/python.png?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/python/',
|
||||
},
|
||||
@@ -48,7 +38,7 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
title: 'Instrument your JS Application',
|
||||
icon: (
|
||||
<img
|
||||
src={`${javascriptPngUrl}?currentVersion=${currentVersion}`}
|
||||
src={`/Logos/javascript.png?currentVersion=${currentVersion}`}
|
||||
alt=""
|
||||
/>
|
||||
),
|
||||
@@ -56,14 +46,16 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Go Application',
|
||||
icon: <img src={`${goPngUrl}?currentVersion=${currentVersion}`} alt="" />,
|
||||
icon: (
|
||||
<img src={`/Logos/go.png?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/golang/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your .NET Application',
|
||||
icon: (
|
||||
<img
|
||||
src={`${msNetFrameworkPngUrl}?currentVersion=${currentVersion}`}
|
||||
src={`/Logos/ms-net-framework.png?currentVersion=${currentVersion}`}
|
||||
alt=""
|
||||
/>
|
||||
),
|
||||
@@ -71,27 +63,29 @@ export const GetStartedContent = (): TGetStartedContentSection[] => {
|
||||
},
|
||||
{
|
||||
title: 'Instrument your PHP Application',
|
||||
icon: <img src={`${phpPngUrl}?currentVersion=${currentVersion}`} alt="" />,
|
||||
icon: (
|
||||
<img src={`/Logos/php.png?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/php/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Rails Application',
|
||||
icon: (
|
||||
<img src={`${railsPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
<img src={`/Logos/rails.png?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/ruby-on-rails/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Rust Application',
|
||||
icon: (
|
||||
<img src={`${rustPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
<img src={`/Logos/rust.png?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/rust/',
|
||||
},
|
||||
{
|
||||
title: 'Instrument your Elixir Application',
|
||||
icon: (
|
||||
<img src={`${elixirPngUrl}?currentVersion=${currentVersion}`} alt="" />
|
||||
<img src={`/Logos/elixir.png?currentVersion=${currentVersion}`} alt="" />
|
||||
),
|
||||
url: 'https://signoz.io/docs/instrumentation/elixir/',
|
||||
},
|
||||
|
||||
@@ -6,8 +6,6 @@ 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';
|
||||
@@ -86,7 +84,11 @@ function IntegrationDetailPage(props: IntegrationDetailPageProps): JSX.Element {
|
||||
) : isError ? (
|
||||
<div className="error-container">
|
||||
<div className="error-content">
|
||||
<img src={awwSnapUrl} alt="error-emoji" className="error-state-svg" />
|
||||
<img
|
||||
src="/Icons/awwSnap.svg"
|
||||
alt="error-emoji"
|
||||
className="error-state-svg"
|
||||
/>
|
||||
<Typography.Text>
|
||||
Something went wrong :/ Please retry or contact support.
|
||||
</Typography.Text>
|
||||
|
||||
@@ -6,9 +6,6 @@ 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';
|
||||
@@ -22,7 +19,7 @@ export const AWS_INTEGRATION = {
|
||||
email: 'integrations@signoz.io',
|
||||
homepage: 'https://signoz.io',
|
||||
},
|
||||
icon: awsDarkUrl,
|
||||
icon: `Logos/aws-dark.svg`,
|
||||
is_installed: false,
|
||||
is_new: true,
|
||||
};
|
||||
@@ -63,7 +60,11 @@ function IntegrationsList(props: IntegrationsListProps): JSX.Element {
|
||||
{!loading && isError && (
|
||||
<div className="error-container">
|
||||
<div className="error-content">
|
||||
<img src={awwSnapUrl} alt="error-emoji" className="error-state-svg" />
|
||||
<img
|
||||
src="/Icons/awwSnap.svg"
|
||||
alt="error-emoji"
|
||||
className="error-state-svg"
|
||||
/>
|
||||
<Typography.Text>
|
||||
Something went wrong :/ Please retry or contact support.
|
||||
</Typography.Text>
|
||||
|
||||
@@ -3,8 +3,6 @@ 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';
|
||||
@@ -40,7 +38,11 @@ function PublicDashboardPage(): JSX.Element {
|
||||
|
||||
<div className="public-dashboard-error-content-header">
|
||||
<div className="brand">
|
||||
<img src={signozBrandLogoUrl} alt="SigNoz" className="brand-logo" />
|
||||
<img
|
||||
src="/Logos/signoz-brand-logo.svg"
|
||||
alt="SigNoz"
|
||||
className="brand-logo"
|
||||
/>
|
||||
|
||||
<Typography.Title level={2} className="brand-title">
|
||||
SigNoz
|
||||
|
||||
@@ -13,8 +13,6 @@ 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';
|
||||
@@ -132,7 +130,7 @@ function SignUp(): JSX.Element {
|
||||
<div className="signup-card">
|
||||
<div className="signup-form-header">
|
||||
<div className="signup-header-icon">
|
||||
<img src={tvUrl} alt="TV" width="32" height="32" />
|
||||
<img src="/svgs/tv.svg" alt="TV" width="32" height="32" />
|
||||
</div>
|
||||
<Typography.Title level={4} className="signup-header-title">
|
||||
Create your account
|
||||
|
||||
@@ -3,10 +3,6 @@ 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 {
|
||||
@@ -15,7 +11,7 @@ function NoData(): JSX.Element {
|
||||
return (
|
||||
<div className="not-found-trace">
|
||||
<section className="description">
|
||||
<img src={noDataUrl} alt="no-data" className="not-found-img" />
|
||||
<img src="/Icons/no-data.svg" 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">
|
||||
@@ -25,14 +21,18 @@ function NoData(): JSX.Element {
|
||||
</section>
|
||||
<section className="reasons">
|
||||
<div className="reason-1">
|
||||
<img src={constructionUrl} alt="no-data" className="construction-img" />
|
||||
<img
|
||||
src="/Icons/construction.svg"
|
||||
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={broomUrl} alt="no-data" className="broom-img" />
|
||||
<img src="/Icons/broom.svg" alt="no-data" className="broom-img" />
|
||||
<Typography.Text className="text">
|
||||
The trace has been deleted as the data has crossed it’s retention period.
|
||||
</Typography.Text>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import LearnMore from 'components/LearnMore/LearnMore';
|
||||
|
||||
import emptyFunnelIconUrl from '@/assets/Icons/empty-funnel-icon.svg';
|
||||
|
||||
import './EmptyFunnelResults.styles.scss';
|
||||
|
||||
function EmptyFunnelResults({
|
||||
@@ -15,7 +13,7 @@ function EmptyFunnelResults({
|
||||
<div className="funnel-results funnel-results--empty">
|
||||
<div className="empty-funnel-results">
|
||||
<div className="empty-funnel-results__icon">
|
||||
<img src={emptyFunnelIconUrl} alt="Empty funnel results" />
|
||||
<img src="/Icons/empty-funnel-icon.svg" alt="Empty funnel results" />
|
||||
</div>
|
||||
<div className="empty-funnel-results__title">{title}</div>
|
||||
<div className="empty-funnel-results__description">{description}</div>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { Empty, Table, TableColumnProps as ColumnProps, Tooltip } from 'antd';
|
||||
|
||||
import solidInfoCircleUrl from '@/assets/Icons/solid-info-circle.svg';
|
||||
|
||||
import './FunnelTable.styles.scss';
|
||||
|
||||
interface FunnelTableProps {
|
||||
@@ -25,7 +23,7 @@ function FunnelTable({
|
||||
<div className="funnel-table__title">{title}</div>
|
||||
<div className="funnel-table__actions">
|
||||
<Tooltip title={tooltip ?? null}>
|
||||
<img src={solidInfoCircleUrl} alt="info" />
|
||||
<img src="/Icons/solid-info-circle.svg" alt="info" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,8 +3,6 @@ import LearnMore from 'components/LearnMore/LearnMore';
|
||||
import { Plus } from 'lucide-react';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
|
||||
import alertEmojiUrl from '@/assets/Icons/alert_emoji.svg';
|
||||
|
||||
import './FunnelsEmptyState.styles.scss';
|
||||
|
||||
interface FunnelsEmptyStateProps {
|
||||
@@ -21,7 +19,7 @@ function FunnelsEmptyState({
|
||||
<div className="funnels-empty__content">
|
||||
<section className="funnels-empty__header">
|
||||
<img
|
||||
src={alertEmojiUrl}
|
||||
src="/Icons/alert_emoji.svg"
|
||||
alt="funnels-empty-icon"
|
||||
className="funnels-empty__icon"
|
||||
/>
|
||||
|
||||
@@ -5,8 +5,6 @@ import history from 'lib/history';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { LicensePlatform, LicenseState } from 'types/api/licensesV3/getActive';
|
||||
|
||||
import featureGraphicCorrelationUrl from '@/assets/Images/feature-graphic-correlation.svg';
|
||||
|
||||
import './WorkspaceAccessRestricted.styles.scss';
|
||||
|
||||
function WorkspaceAccessRestricted(): JSX.Element {
|
||||
@@ -115,7 +113,10 @@ function WorkspaceAccessRestricted(): JSX.Element {
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="workspace-access-restricted__creative">
|
||||
<img src={featureGraphicCorrelationUrl} alt="correlation-graphic" />
|
||||
<img
|
||||
src="/Images/feature-graphic-correlation.svg"
|
||||
alt="correlation-graphic"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -23,8 +23,6 @@ import APIError from 'types/api/error';
|
||||
import { LicensePlatform, LicenseState } from 'types/api/licensesV3/getActive';
|
||||
import { getFormattedDateWithMinutes } from 'utils/timeUtils';
|
||||
|
||||
import featureGraphicCorrelationUrl from '@/assets/Images/feature-graphic-correlation.svg';
|
||||
|
||||
import './WorkspaceSuspended.styles.scss';
|
||||
|
||||
function WorkspaceSuspended(): JSX.Element {
|
||||
@@ -167,7 +165,10 @@ function WorkspaceSuspended(): JSX.Element {
|
||||
</Row>
|
||||
)}
|
||||
<div className="workspace-suspended__creative">
|
||||
<img src={featureGraphicCorrelationUrl} alt="correlation-graphic" />
|
||||
<img
|
||||
src="/Images/feature-graphic-correlation.svg"
|
||||
alt="correlation-graphic"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/**
|
||||
* Stylelint rule: local/no-unsupported-asset-url
|
||||
*
|
||||
* Disallows asset URLs in CSS `url()` declarations that are not base-path-safe.
|
||||
* When SigNoz is served from a sub-path (e.g. /app/), absolute and public-dir
|
||||
* relative paths break because they bypass Vite's module pipeline and don't get
|
||||
* the runtime base-path prefix.
|
||||
*
|
||||
* Flagged patterns:
|
||||
* - Absolute paths: url('/icons/logo.svg')
|
||||
* - Public-relative paths: url('../public/icons/logo.svg')
|
||||
* - Public-dir segments: url('Icons/logo.svg') (resolves from public/)
|
||||
*
|
||||
* Required pattern (base-path-safe):
|
||||
* - src/assets ES import: import logo from '../../assets/icons/logo.svg'
|
||||
* then reference via the imported variable in JS/TS.
|
||||
* - Relative from src/assets in SCSS: url('../../assets/icons/logo.svg')
|
||||
*
|
||||
* See: https://vitejs.dev/guide/assets (Static Asset Handling)
|
||||
*/
|
||||
import stylelint from 'stylelint';
|
||||
import { createRequire } from 'module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const {
|
||||
containsAssetExtension,
|
||||
isAbsolutePath,
|
||||
isPublicRelative,
|
||||
isRelativePublicDir,
|
||||
} = require('../eslint-rules/shared/asset-patterns');
|
||||
|
||||
const ruleName = 'local/no-unsupported-asset-url';
|
||||
|
||||
/**
|
||||
* Extracts all url() inner path strings from a CSS declaration value.
|
||||
* Handles single-quoted, double-quoted, and unquoted url() forms.
|
||||
* e.g. "url('/a.svg') url('/b.png') repeat" → ['/a.svg', '/b.png']
|
||||
*/
|
||||
function extractUrlPaths(value) {
|
||||
if (typeof value !== 'string') return [];
|
||||
const paths = [];
|
||||
const urlPattern = /url\(\s*['"]?([^'")\s]+)['"]?\s*\)/g;
|
||||
let match = urlPattern.exec(value);
|
||||
while (match !== null) {
|
||||
paths.push(match[1]);
|
||||
match = urlPattern.exec(value);
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
|
||||
const meta = {};
|
||||
|
||||
const messages = stylelint.utils.ruleMessages(ruleName, {
|
||||
absolutePath: (urlPath) =>
|
||||
`Absolute asset path "${urlPath}" in url() is not base-path-safe. ` +
|
||||
`Use a relative path from src/assets/ instead: url('../../assets/...')`,
|
||||
publicPath: () =>
|
||||
`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 a relative path from src/assets/ instead: url('../../assets/...')`,
|
||||
relativePath: (urlPath) =>
|
||||
`Relative public-dir path "${urlPath}" in url() is not base-path-safe. ` +
|
||||
`Use a relative path from src/assets/ instead: url('../../assets/...')`,
|
||||
});
|
||||
|
||||
/**
|
||||
* Rule implementation. Walks every CSS declaration in the file and checks
|
||||
* each url() path against three forbidden patterns (absolute, public-relative,
|
||||
* public-dir segment). Reports a violation with a fix hint for each match.
|
||||
*
|
||||
* `primaryOption` is the value from the stylelint config — the rule is a no-op
|
||||
* when falsy (i.e. `"local/no-unsupported-asset-url": [false]`).
|
||||
*
|
||||
* @type {import('stylelint').Rule}
|
||||
*/
|
||||
const rule = (primaryOption) => {
|
||||
return (root, result) => {
|
||||
if (!primaryOption) return;
|
||||
|
||||
root.walkDecls((decl) => {
|
||||
const urlPaths = extractUrlPaths(decl.value);
|
||||
|
||||
for (const urlPath of urlPaths) {
|
||||
// Pattern #1: absolute path with asset extension
|
||||
if (isAbsolutePath(urlPath) && containsAssetExtension(urlPath)) {
|
||||
stylelint.utils.report({
|
||||
message: messages.absolutePath(urlPath),
|
||||
node: decl,
|
||||
result,
|
||||
ruleName,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pattern #2: relative path into public/ with asset extension
|
||||
if (isPublicRelative(urlPath) && containsAssetExtension(urlPath)) {
|
||||
stylelint.utils.report({
|
||||
message: messages.publicPath(),
|
||||
node: decl,
|
||||
result,
|
||||
ruleName,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pattern #3: relative public-dir segment, e.g. url('Icons/foo.svg')
|
||||
if (isRelativePublicDir(urlPath) && containsAssetExtension(urlPath)) {
|
||||
stylelint.utils.report({
|
||||
message: messages.relativePath(urlPath),
|
||||
node: decl,
|
||||
result,
|
||||
ruleName,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
rule.ruleName = ruleName;
|
||||
rule.messages = messages;
|
||||
rule.meta = meta;
|
||||
|
||||
export { ruleName, rule, meta };
|
||||
export default { ruleName, rule, meta };
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user