Compare commits
13 Commits
chore/auto
...
chore/dril
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0115a3d51a | ||
|
|
a4266fa703 | ||
|
|
44add7b7cd | ||
|
|
feea9e9b36 | ||
|
|
e94767bda8 | ||
|
|
bb10f51cc5 | ||
|
|
cd16081a1e | ||
|
|
c23a53c8d2 | ||
|
|
dbfe47e757 | ||
|
|
e29b032e70 | ||
|
|
a92871d704 | ||
|
|
b55ae83993 | ||
|
|
7e7d7ab570 |
3
.vscode/settings.json
vendored
@@ -1,6 +1,8 @@
|
||||
{
|
||||
"oxc.typeAware": true,
|
||||
"oxc.tsConfigPath": "./frontend/tsconfig.json",
|
||||
"oxc.configPath": "./frontend/.oxlintrc.json",
|
||||
"oxc.fmt.configPath": "./frontend/.oxfmtrc.json",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "oxc.oxc-vscode",
|
||||
"editor.codeActionsOnSave": {
|
||||
@@ -19,4 +21,3 @@
|
||||
"python-envs.defaultEnvManager": "ms-python.python:system",
|
||||
"python-envs.pythonProjects": []
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ global:
|
||||
external_url: <unset>
|
||||
# the url where the SigNoz backend receives telemetry data (traces, metrics, logs) from instrumented applications.
|
||||
ingestion_url: <unset>
|
||||
# the url of the SigNoz MCP server. when unset, the MCP settings page is hidden in the frontend.
|
||||
# mcp_url: <unset>
|
||||
|
||||
##################### Version #####################
|
||||
version:
|
||||
|
||||
@@ -2369,6 +2369,13 @@ components:
|
||||
$ref: '#/components/schemas/GlobaltypesIdentNConfig'
|
||||
ingestion_url:
|
||||
type: string
|
||||
mcp_url:
|
||||
nullable: true
|
||||
type: string
|
||||
required:
|
||||
- external_url
|
||||
- ingestion_url
|
||||
- mcp_url
|
||||
type: object
|
||||
GlobaltypesIdentNConfig:
|
||||
properties:
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"react",
|
||||
"react-perf",
|
||||
"typescript",
|
||||
"unicorn",
|
||||
"jsx-a11y",
|
||||
"import",
|
||||
"jest",
|
||||
@@ -206,6 +205,8 @@
|
||||
"@typescript-eslint/explicit-function-return-type": "error",
|
||||
// Requires explicit return types on functions
|
||||
"@typescript-eslint/no-var-requires": "error",
|
||||
"@typescript-eslint/no-useless-default-assignment": "off", // provide unsafe fixes in our codebase due to bad typing
|
||||
"@typescript-eslint/no-duplicate-type-constituents": "off", // provide fixes that breaks some assumptions, eg: type A = L, B = L, C = A | B (removes B)
|
||||
// Disallows require() in TypeScript (use import instead)
|
||||
// Disabled - using TypeScript instead
|
||||
"react/jsx-props-no-spreading": "off",
|
||||
@@ -312,14 +313,6 @@
|
||||
"name": "react-redux",
|
||||
"message": "[State mgmt] react-redux is deprecated. Migrate to Zustand, nuqs, or react-query."
|
||||
},
|
||||
{
|
||||
"name": "xstate",
|
||||
"message": "[State mgmt] xstate is deprecated. Migrate to Zustand or react-query."
|
||||
},
|
||||
{
|
||||
"name": "@xstate/react",
|
||||
"message": "[State mgmt] @xstate/react is deprecated. Migrate to Zustand or react-query."
|
||||
},
|
||||
{
|
||||
"name": "react",
|
||||
"importNames": [
|
||||
@@ -338,82 +331,6 @@
|
||||
"react/no-array-index-key": "warn",
|
||||
// TODO: Changed to warn during oxlint migration, should be changed to error,
|
||||
|
||||
"unicorn/error-message": "warn",
|
||||
"unicorn/escape-case": "warn",
|
||||
"unicorn/new-for-builtins": "warn",
|
||||
"unicorn/no-abusive-eslint-disable": "warn",
|
||||
"unicorn/no-console-spaces": "warn",
|
||||
"unicorn/no-instanceof-array": "warn",
|
||||
"unicorn/no-invalid-remove-event-listener": "warn",
|
||||
"unicorn/no-new-array": "warn",
|
||||
"unicorn/no-new-buffer": "warn",
|
||||
"unicorn/no-thenable": "warn",
|
||||
"unicorn/no-unreadable-array-destructuring": "warn",
|
||||
"unicorn/no-useless-fallback-in-spread": "warn",
|
||||
"unicorn/no-useless-length-check": "warn",
|
||||
"unicorn/no-useless-promise-resolve-reject": "warn",
|
||||
"unicorn/no-useless-spread": "warn",
|
||||
"unicorn/no-zero-fractions": "warn",
|
||||
"unicorn/number-literal-case": "warn",
|
||||
"unicorn/prefer-array-find": "warn",
|
||||
"unicorn/prefer-array-flat": "warn",
|
||||
"unicorn/prefer-array-flat-map": "warn",
|
||||
"unicorn/prefer-array-index-of": "warn",
|
||||
"unicorn/prefer-array-some": "warn",
|
||||
"unicorn/prefer-at": "warn",
|
||||
"unicorn/prefer-code-point": "warn",
|
||||
"unicorn/prefer-date-now": "warn",
|
||||
"unicorn/prefer-default-parameters": "warn",
|
||||
"unicorn/prefer-includes": "warn",
|
||||
"unicorn/prefer-modern-math-apis": "warn",
|
||||
"unicorn/prefer-native-coercion-functions": "warn",
|
||||
"unicorn/prefer-node-protocol": "off",
|
||||
"unicorn/prefer-number-properties": "warn",
|
||||
"unicorn/prefer-optional-catch-binding": "warn",
|
||||
"unicorn/prefer-regexp-test": "warn",
|
||||
"unicorn/prefer-set-has": "warn",
|
||||
"unicorn/prefer-string-replace-all": "warn",
|
||||
"unicorn/prefer-string-slice": "warn",
|
||||
"unicorn/prefer-string-starts-ends-with": "warn",
|
||||
"unicorn/prefer-string-trim-start-end": "warn",
|
||||
"unicorn/prefer-type-error": "warn",
|
||||
"unicorn/require-array-join-separator": "warn",
|
||||
"unicorn/require-number-to-fixed-digits-argument": "warn",
|
||||
"unicorn/throw-new-error": "warn",
|
||||
"unicorn/consistent-function-scoping": "warn",
|
||||
"unicorn/explicit-length-check": "warn",
|
||||
"unicorn/filename-case": [
|
||||
"warn",
|
||||
{
|
||||
"case": "kebabCase"
|
||||
}
|
||||
],
|
||||
"unicorn/no-array-for-each": "warn",
|
||||
"unicorn/no-lonely-if": "warn",
|
||||
"unicorn/no-negated-condition": "warn",
|
||||
"unicorn/no-null": "warn",
|
||||
"unicorn/no-object-as-default-parameter": "warn",
|
||||
"unicorn/no-static-only-class": "warn",
|
||||
"unicorn/no-this-assignment": "warn",
|
||||
"unicorn/no-unreadable-iife": "warn",
|
||||
"unicorn/no-useless-switch-case": "warn",
|
||||
"unicorn/no-useless-undefined": "warn",
|
||||
"unicorn/prefer-add-event-listener": "warn",
|
||||
"unicorn/prefer-dom-node-append": "warn",
|
||||
"unicorn/prefer-dom-node-dataset": "warn",
|
||||
"unicorn/prefer-dom-node-remove": "warn",
|
||||
"unicorn/prefer-dom-node-text-content": "warn",
|
||||
"unicorn/prefer-keyboard-event-key": "warn",
|
||||
"unicorn/prefer-math-trunc": "warn",
|
||||
"unicorn/prefer-modern-dom-apis": "warn",
|
||||
"unicorn/prefer-negative-index": "warn",
|
||||
"unicorn/prefer-prototype-methods": "warn",
|
||||
"unicorn/prefer-query-selector": "warn",
|
||||
"unicorn/prefer-reflect-apply": "warn",
|
||||
"unicorn/prefer-set-size": "warn",
|
||||
"unicorn/prefer-spread": "warn",
|
||||
"unicorn/prefer-ternary": "warn",
|
||||
"unicorn/require-post-message-target-origin": "warn",
|
||||
"oxc/bad-array-method-on-arguments": "error",
|
||||
"oxc/bad-bitwise-operator": "error",
|
||||
"oxc/bad-comparison-sequence": "error",
|
||||
@@ -538,8 +455,8 @@
|
||||
"sonarjs/no-nested-template-literals": "error",
|
||||
// Avoids nested template literals
|
||||
"sonarjs/no-redundant-boolean": "warn", // TODO: Change to error after migration
|
||||
// Removes redundant boolean literals
|
||||
"sonarjs/no-redundant-jump": "error",
|
||||
// Removes redundant boolean literals - turned off because it clashes with unicorn rules
|
||||
"sonarjs/no-redundant-jump": "off",
|
||||
// Removes unnecessary returns/continues
|
||||
"sonarjs/no-same-line-conditional": "error",
|
||||
// Prevents same-line conditionals
|
||||
@@ -614,8 +531,9 @@
|
||||
"signoz/no-navigator-clipboard": "off",
|
||||
// Tests can use navigator.clipboard directly,
|
||||
"signoz/no-raw-absolute-path":"off",
|
||||
"no-restricted-globals": "off"
|
||||
"no-restricted-globals": "off",
|
||||
// Tests need raw localStorage/sessionStorage to seed DOM state for isolation
|
||||
"signoz/no-zustand-getstate-in-hooks": "off"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -72,10 +72,12 @@
|
||||
// to that prefix by the React app (see utils/storage.ts getScopedKey).
|
||||
// Read the <base> tag — already populated by the Go template — to derive
|
||||
// the same prefix here, before any JS module has loaded.
|
||||
var basePath = (document.querySelector('base') || {}).getAttribute('href') || '/';
|
||||
var basePath =
|
||||
(document.querySelector('base') || {}).getAttribute('href') || '/';
|
||||
var prefix = basePath === '/' ? '' : basePath;
|
||||
var theme = localStorage.getItem(prefix + 'THEME');
|
||||
var autoSwitch = localStorage.getItem(prefix + 'THEME_AUTO_SWITCH') === 'true';
|
||||
var autoSwitch =
|
||||
localStorage.getItem(prefix + 'THEME_AUTO_SWITCH') === 'true';
|
||||
if (autoSwitch) {
|
||||
theme = window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
|
||||
@@ -42,13 +42,13 @@ window.getComputedStyle = function (
|
||||
} catch {
|
||||
// Return a minimal CSSStyleDeclaration so callers (testing-library, Radix UI)
|
||||
// see the element as visible and without animations.
|
||||
return ({
|
||||
return {
|
||||
display: '',
|
||||
visibility: '',
|
||||
opacity: '1',
|
||||
animationName: 'none',
|
||||
getPropertyValue: () => '',
|
||||
} as unknown) as CSSStyleDeclaration;
|
||||
} as unknown as CSSStyleDeclaration;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"prettify": "oxfmt",
|
||||
"fmt": "echo 'Disabled due to migration' || oxfmt --check",
|
||||
"fmt": "oxfmt --check",
|
||||
"lint": "oxlint ./src && stylelint \"src/**/*.scss\"",
|
||||
"lint:js": "oxlint ./src",
|
||||
"lint:generated": "oxlint ./src/api/generated --fix",
|
||||
@@ -63,7 +63,6 @@
|
||||
"@visx/shape": "3.5.0",
|
||||
"@visx/tooltip": "3.3.0",
|
||||
"@vitejs/plugin-react": "5.1.4",
|
||||
"@xstate/react": "^3.0.0",
|
||||
"ansi-to-html": "0.7.2",
|
||||
"antd": "5.11.0",
|
||||
"antd-table-saveas-excel": "2.2.1",
|
||||
@@ -146,7 +145,6 @@
|
||||
"vite": "npm:rolldown-vite@7.3.1",
|
||||
"vite-plugin-html": "3.2.2",
|
||||
"web-vitals": "^0.2.4",
|
||||
"xstate": "^4.31.0",
|
||||
"zod": "4.3.6",
|
||||
"zustand": "5.0.11"
|
||||
},
|
||||
@@ -214,9 +212,9 @@
|
||||
"msw": "1.3.2",
|
||||
"npm-run-all": "latest",
|
||||
"orval": "7.18.0",
|
||||
"oxfmt": "0.41.0",
|
||||
"oxlint": "1.59.0",
|
||||
"oxlint-tsgolint": "0.20.0",
|
||||
"oxfmt": "0.46.0",
|
||||
"oxlint": "1.61.0",
|
||||
"oxlint-tsgolint": "0.21.1",
|
||||
"portfinder-sync": "^0.0.2",
|
||||
"postcss": "8.5.6",
|
||||
"postcss-scss": "4.0.9",
|
||||
@@ -240,8 +238,8 @@
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.(js|jsx|ts|tsx)": [
|
||||
"echo 'Disabled due to migration' || oxfmt --check",
|
||||
"oxlint --fix",
|
||||
"oxfmt --check",
|
||||
"oxlint --quiet",
|
||||
"sh scripts/typecheck-staged.sh"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -56,7 +56,6 @@ function isExternalUrl(node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// window.open(withBasePath(x)) and window.open(getAbsoluteUrl(x)) are already safe.
|
||||
function isSafeHelperCall(node) {
|
||||
return (
|
||||
@@ -97,18 +96,27 @@ export default {
|
||||
callee.object.type !== 'Identifier' ||
|
||||
callee.object.name !== 'window' ||
|
||||
callee.property.name !== 'open'
|
||||
)
|
||||
{return;}
|
||||
if (args.length === 0) {return;}
|
||||
if (isExternalUrl(args[0])) {return;}
|
||||
if (isSafeHelperCall(args[0])) {return;}
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (args.length === 0) {
|
||||
return;
|
||||
}
|
||||
if (isExternalUrl(args[0])) {
|
||||
return;
|
||||
}
|
||||
if (isSafeHelperCall(args[0])) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.report({ node, messageId: 'windowOpen' });
|
||||
},
|
||||
|
||||
// window.location.origin + path
|
||||
BinaryExpression(node) {
|
||||
if (node.operator !== '+') {return;}
|
||||
if (node.operator !== '+') {
|
||||
return;
|
||||
}
|
||||
if (isOriginAccess(node.left) || isOriginAccess(node.right)) {
|
||||
context.report({ node, messageId: 'originConcat' });
|
||||
}
|
||||
@@ -124,26 +132,40 @@ export default {
|
||||
// window.location.origin used directly (not in concatenation)
|
||||
// Catches: frontendBaseUrl: window.location.origin
|
||||
MemberExpression(node) {
|
||||
if (!isOriginAccess(node)) {return;}
|
||||
if (!isOriginAccess(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parent = node.parent;
|
||||
// Skip if parent is BinaryExpression with + (handled by BinaryExpression visitor)
|
||||
if (parent.type === 'BinaryExpression' && parent.operator === '+') {return;}
|
||||
if (parent.type === 'BinaryExpression' && parent.operator === '+') {
|
||||
return;
|
||||
}
|
||||
// Skip if inside TemplateLiteral (handled by TemplateLiteral visitor)
|
||||
if (parent.type === 'TemplateLiteral') {return;}
|
||||
if (parent.type === 'TemplateLiteral') {
|
||||
return;
|
||||
}
|
||||
|
||||
context.report({ node, messageId: 'originDirect' });
|
||||
},
|
||||
|
||||
// window.location.href = path
|
||||
AssignmentExpression(node) {
|
||||
if (node.operator !== '=') {return;}
|
||||
if (!isHrefAccess(node.left)) {return;}
|
||||
if (node.operator !== '=') {
|
||||
return;
|
||||
}
|
||||
if (!isHrefAccess(node.left)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow external URLs
|
||||
if (isExternalUrl(node.right)) {return;}
|
||||
if (isExternalUrl(node.right)) {
|
||||
return;
|
||||
}
|
||||
// Allow safe helper calls
|
||||
if (isSafeHelperCall(node.right)) {return;}
|
||||
if (isSafeHelperCall(node.right)) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.report({ node, messageId: 'hrefAssign' });
|
||||
},
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
"roles": "Roles",
|
||||
"role_details": "Role Details",
|
||||
"members": "Members",
|
||||
"service_accounts": "Service Accounts"
|
||||
"service_accounts": "Service Accounts",
|
||||
"mcp_server": "MCP Server"
|
||||
}
|
||||
|
||||
@@ -53,5 +53,6 @@
|
||||
"METER": "SigNoz | Meter",
|
||||
"ROLES_SETTINGS": "SigNoz | Roles",
|
||||
"MEMBERS_SETTINGS": "SigNoz | Members",
|
||||
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts"
|
||||
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts",
|
||||
"MCP_SERVER": "SigNoz | MCP Server"
|
||||
}
|
||||
|
||||
@@ -16,5 +16,6 @@
|
||||
"roles": "Roles",
|
||||
"role_details": "Role Details",
|
||||
"members": "Members",
|
||||
"service_accounts": "Service Accounts"
|
||||
"service_accounts": "Service Accounts",
|
||||
"mcp_server": "MCP Server"
|
||||
}
|
||||
|
||||
@@ -76,5 +76,6 @@
|
||||
"METER": "SigNoz | Meter",
|
||||
"ROLES_SETTINGS": "SigNoz | Roles",
|
||||
"MEMBERS_SETTINGS": "SigNoz | Members",
|
||||
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts"
|
||||
"SERVICE_ACCOUNTS_SETTINGS": "SigNoz | Service Accounts",
|
||||
"MCP_SERVER": "SigNoz | MCP Server"
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
if (org && org.length > 0 && org[0].id !== undefined) {
|
||||
return org[0];
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
}, [org]);
|
||||
|
||||
const { data: usersData, isFetching: isFetchingUsers } = useListUsers({
|
||||
@@ -192,7 +192,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
if (isPrivate) {
|
||||
if (isLoggedInState) {
|
||||
const route = routePermission[key];
|
||||
if (route && route.some((e) => e === user.role) === undefined) {
|
||||
if (route && route.find((e) => e === user.role) === undefined) {
|
||||
return <Redirect to={ROUTES.UN_AUTHORIZED} />;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -320,12 +320,12 @@ function App(): JSX.Element {
|
||||
}),
|
||||
],
|
||||
// Performance Monitoring
|
||||
tracesSampleRate: 1, // Capture 100% of the transactions
|
||||
tracesSampleRate: 1.0, // Capture 100% of the transactions
|
||||
// Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled
|
||||
tracePropagationTargets: [],
|
||||
// Session Replay
|
||||
replaysSessionSampleRate: 0.1, // This sets the sample rate at 10%. You may want to change it to 100% while in development and then sample at a lower rate in production.
|
||||
replaysOnErrorSampleRate: 1, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
|
||||
replaysOnErrorSampleRate: 1.0, // If you're not already sampling the entire session, change the sample rate to 100% when sampling sessions where errors occur.
|
||||
beforeSend(event) {
|
||||
const sessionReplayUrl = posthog.get_session_replay_url?.({
|
||||
withTimestamp: true,
|
||||
|
||||
@@ -24,7 +24,7 @@ export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
|
||||
const { errors, error } = data;
|
||||
|
||||
const errorMessage =
|
||||
Array.isArray(errors) && errors.length > 0 ? errors[0].msg : error;
|
||||
Array.isArray(errors) && errors.length >= 1 ? errors[0].msg : error;
|
||||
|
||||
return {
|
||||
statusCode,
|
||||
|
||||
@@ -21,12 +21,12 @@ const dashboardVariablesQuery = async (
|
||||
});
|
||||
|
||||
const timeVariables: Record<string, number> = {
|
||||
start_timestamp_ms: Number.parseInt(start, 10) * 1e3,
|
||||
end_timestamp_ms: Number.parseInt(end, 10) * 1e3,
|
||||
start_timestamp_nano: Number.parseInt(start, 10) * 1e9,
|
||||
end_timestamp_nano: Number.parseInt(end, 10) * 1e9,
|
||||
start_timestamp: Number.parseInt(start, 10),
|
||||
end_timestamp: Number.parseInt(end, 10),
|
||||
start_timestamp_ms: parseInt(start, 10) * 1e3,
|
||||
end_timestamp_ms: parseInt(end, 10) * 1e3,
|
||||
start_timestamp_nano: parseInt(start, 10) * 1e9,
|
||||
end_timestamp_nano: parseInt(end, 10) * 1e9,
|
||||
start_timestamp: parseInt(start, 10),
|
||||
end_timestamp: parseInt(end, 10),
|
||||
};
|
||||
|
||||
const payload = { ...props };
|
||||
|
||||
@@ -104,7 +104,7 @@ describe('getFieldKeys API', () => {
|
||||
const result = await getFieldKeys('traces');
|
||||
|
||||
// Verify the returned structure matches SuccessResponseV2 format
|
||||
expect(result).toStrictEqual({
|
||||
expect(result).toEqual({
|
||||
httpStatusCode: 200,
|
||||
data: mockSuccessResponse.data.data,
|
||||
});
|
||||
|
||||
@@ -199,7 +199,7 @@ describe('getFieldValues API', () => {
|
||||
const result = await getFieldValues('traces', 'service.name');
|
||||
|
||||
// Verify the returned structure matches SuccessResponseV2 format
|
||||
expect(result).toStrictEqual({
|
||||
expect(result).toEqual({
|
||||
httpStatusCode: 200,
|
||||
data: expect.objectContaining({
|
||||
values: expect.any(Object),
|
||||
|
||||
@@ -3125,12 +3125,17 @@ export interface GlobaltypesConfigDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
external_url?: string;
|
||||
external_url: string;
|
||||
identN?: GlobaltypesIdentNConfigDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
ingestion_url?: string;
|
||||
ingestion_url: string;
|
||||
/**
|
||||
* @type string
|
||||
* @nullable true
|
||||
*/
|
||||
mcp_url: string | null;
|
||||
}
|
||||
|
||||
export interface GlobaltypesIdentNConfigDTO {
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import axios from 'api';
|
||||
import { ErrorResponseHandlerV2 } from 'api/ErrorResponseHandlerV2';
|
||||
import { AxiosError } from 'axios';
|
||||
import { ErrorV2Resp, SuccessResponseV2 } from 'types/api';
|
||||
import {
|
||||
GlobalConfigData,
|
||||
GlobalConfigDataProps,
|
||||
} from 'types/api/globalConfig/types';
|
||||
|
||||
const getGlobalConfig = async (): Promise<
|
||||
SuccessResponseV2<GlobalConfigData>
|
||||
> => {
|
||||
try {
|
||||
const response = await axios.get<GlobalConfigDataProps>(`/global/config`);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
data: response.data.data,
|
||||
};
|
||||
} catch (error) {
|
||||
ErrorResponseHandlerV2(error as AxiosError<ErrorV2Resp>);
|
||||
}
|
||||
};
|
||||
|
||||
export default getGlobalConfig;
|
||||
@@ -32,7 +32,7 @@ export const interceptorsResponse = (
|
||||
): Promise<AxiosResponse<any>> => {
|
||||
if ((value.config as any)?.metadata) {
|
||||
const duration =
|
||||
Date.now() - (value.config as any).metadata.startTime;
|
||||
new Date().getTime() - (value.config as any).metadata.startTime;
|
||||
|
||||
if (duration > RESPONSE_TIMEOUT_THRESHOLD && value.config.url !== '/event') {
|
||||
eventEmitter.emit(Events.SLOW_API_WARNING, true, {
|
||||
@@ -55,7 +55,7 @@ export const interceptorsRequestResponse = (
|
||||
): InternalAxiosRequestConfig => {
|
||||
// Attach metadata safely (not sent with the request)
|
||||
Object.defineProperty(value, 'metadata', {
|
||||
value: { startTime: Date.now() },
|
||||
value: { startTime: new Date().getTime() },
|
||||
enumerable: false, // Prevents it from being included in the request
|
||||
});
|
||||
|
||||
@@ -143,7 +143,7 @@ export const interceptorRejected = async (
|
||||
Logout();
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
} catch (error) {
|
||||
Logout();
|
||||
}
|
||||
}
|
||||
@@ -160,7 +160,7 @@ export const interceptorRejected = async (
|
||||
|
||||
const interceptorRejectedBase = async (
|
||||
value: AxiosResponse<any>,
|
||||
): Promise<AxiosResponse<any>> => { throw value; };
|
||||
): Promise<AxiosResponse<any>> => Promise.reject(value);
|
||||
|
||||
const instance = axios.create({
|
||||
baseURL: `${ENVIRONMENT.baseURL}${apiV1}`,
|
||||
|
||||
@@ -39,8 +39,8 @@ jest.mock('axios', () => {
|
||||
describe('interceptorRejected', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
((axios as unknown) as jest.Mock).mockResolvedValue({ data: 'success' });
|
||||
((axios.isAxiosError as unknown) as jest.Mock).mockReturnValue(true);
|
||||
(axios as unknown as jest.Mock).mockResolvedValue({ data: 'success' });
|
||||
(axios.isAxiosError as unknown as jest.Mock).mockReturnValue(true);
|
||||
});
|
||||
|
||||
it('should preserve array payload structure when retrying a 401 request', async () => {
|
||||
@@ -49,7 +49,7 @@ describe('interceptorRejected', () => {
|
||||
{ relation: 'assignee', object: { resource: { name: 'editor' } } },
|
||||
];
|
||||
|
||||
const error = ({
|
||||
const error = {
|
||||
response: {
|
||||
status: 401,
|
||||
config: {
|
||||
@@ -67,7 +67,7 @@ describe('interceptorRejected', () => {
|
||||
headers: new AxiosHeaders(),
|
||||
data: JSON.stringify(arrayPayload),
|
||||
},
|
||||
} as unknown) as AxiosResponse;
|
||||
} as unknown as AxiosResponse;
|
||||
|
||||
try {
|
||||
await interceptorRejected(error);
|
||||
@@ -75,17 +75,17 @@ describe('interceptorRejected', () => {
|
||||
// Expected to reject after retry
|
||||
}
|
||||
|
||||
const mockAxiosFn = (axios as unknown) as jest.Mock;
|
||||
expect(mockAxiosFn.mock.calls).toHaveLength(1);
|
||||
const mockAxiosFn = axios as unknown as jest.Mock;
|
||||
expect(mockAxiosFn.mock.calls.length).toBe(1);
|
||||
const retryCallConfig = mockAxiosFn.mock.calls[0][0];
|
||||
expect(Array.isArray(JSON.parse(retryCallConfig.data))).toBe(true);
|
||||
expect(JSON.parse(retryCallConfig.data)).toStrictEqual(arrayPayload);
|
||||
expect(JSON.parse(retryCallConfig.data)).toEqual(arrayPayload);
|
||||
});
|
||||
|
||||
it('should preserve object payload structure when retrying a 401 request', async () => {
|
||||
const objectPayload = { key: 'value', nested: { data: 123 } };
|
||||
|
||||
const error = ({
|
||||
const error = {
|
||||
response: {
|
||||
status: 401,
|
||||
config: {
|
||||
@@ -103,7 +103,7 @@ describe('interceptorRejected', () => {
|
||||
headers: new AxiosHeaders(),
|
||||
data: JSON.stringify(objectPayload),
|
||||
},
|
||||
} as unknown) as AxiosResponse;
|
||||
} as unknown as AxiosResponse;
|
||||
|
||||
try {
|
||||
await interceptorRejected(error);
|
||||
@@ -111,14 +111,14 @@ describe('interceptorRejected', () => {
|
||||
// Expected to reject after retry
|
||||
}
|
||||
|
||||
const mockAxiosFn = (axios as unknown) as jest.Mock;
|
||||
expect(mockAxiosFn.mock.calls).toHaveLength(1);
|
||||
const mockAxiosFn = axios as unknown as jest.Mock;
|
||||
expect(mockAxiosFn.mock.calls.length).toBe(1);
|
||||
const retryCallConfig = mockAxiosFn.mock.calls[0][0];
|
||||
expect(JSON.parse(retryCallConfig.data)).toStrictEqual(objectPayload);
|
||||
expect(JSON.parse(retryCallConfig.data)).toEqual(objectPayload);
|
||||
});
|
||||
|
||||
it('should handle undefined data gracefully when retrying', async () => {
|
||||
const error = ({
|
||||
const error = {
|
||||
response: {
|
||||
status: 401,
|
||||
config: {
|
||||
@@ -136,7 +136,7 @@ describe('interceptorRejected', () => {
|
||||
headers: new AxiosHeaders(),
|
||||
data: undefined,
|
||||
},
|
||||
} as unknown) as AxiosResponse;
|
||||
} as unknown as AxiosResponse;
|
||||
|
||||
try {
|
||||
await interceptorRejected(error);
|
||||
@@ -144,8 +144,8 @@ describe('interceptorRejected', () => {
|
||||
// Expected to reject after retry
|
||||
}
|
||||
|
||||
const mockAxiosFn = (axios as unknown) as jest.Mock;
|
||||
expect(mockAxiosFn.mock.calls).toHaveLength(1);
|
||||
const mockAxiosFn = axios as unknown as jest.Mock;
|
||||
expect(mockAxiosFn.mock.calls.length).toBe(1);
|
||||
const retryCallConfig = mockAxiosFn.mock.calls[0][0];
|
||||
expect(retryCallConfig.data).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -9,9 +9,8 @@ const getRetentionV2 = async (): Promise<
|
||||
SuccessResponseV2<PayloadProps<'logs'>>
|
||||
> => {
|
||||
try {
|
||||
const response = await ApiV2Instance.get<PayloadProps<'logs'>>(
|
||||
`/settings/ttl`,
|
||||
);
|
||||
const response =
|
||||
await ApiV2Instance.get<PayloadProps<'logs'>>(`/settings/ttl`);
|
||||
|
||||
return {
|
||||
httpStatusCode: response.status,
|
||||
|
||||
@@ -37,11 +37,11 @@ export const downloadExportData = async (
|
||||
const filename =
|
||||
response.headers['content-disposition']
|
||||
?.split('filename=')[1]
|
||||
?.replaceAll(/["']/g, '') || `exported_data.${props.format || 'txt'}`;
|
||||
?.replace(/["']/g, '') || `exported_data.${props.format || 'txt'}`;
|
||||
|
||||
link.setAttribute('download', filename);
|
||||
|
||||
document.body.append(link);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
@@ -52,18 +52,18 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
alias: '__result_0',
|
||||
meta: {},
|
||||
series: [
|
||||
({
|
||||
{
|
||||
labels: [
|
||||
{
|
||||
key: ({ name: 'service.name' } as unknown) as TelemetryFieldKey,
|
||||
key: { name: 'service.name' } as unknown as TelemetryFieldKey,
|
||||
value: 'adservice',
|
||||
},
|
||||
],
|
||||
values: [
|
||||
({ timestamp: 1000, value: 10 } as unknown) as TimeSeriesValue,
|
||||
({ timestamp: 2000, value: 12 } as unknown) as TimeSeriesValue,
|
||||
{ timestamp: 1000, value: 10 } as unknown as TimeSeriesValue,
|
||||
{ timestamp: 2000, value: 12 } as unknown as TimeSeriesValue,
|
||||
],
|
||||
} as unknown) as TimeSeries,
|
||||
} as unknown as TimeSeries,
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -88,10 +88,8 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const input: SuccessResponse<
|
||||
MetricRangePayloadV5,
|
||||
QueryRangeRequestV5
|
||||
> = makeBaseSuccess({ data: v5Data }, params);
|
||||
const input: SuccessResponse<MetricRangePayloadV5, QueryRangeRequestV5> =
|
||||
makeBaseSuccess({ data: v5Data }, params);
|
||||
|
||||
const legendMap = { A: '{{service.name}}' };
|
||||
const result = convertV5ResponseToLegacy(input, legendMap, false);
|
||||
@@ -101,7 +99,7 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
const q = result.payload.data.result[0];
|
||||
expect(q.queryName).toBe('A');
|
||||
expect(q.legend).toBe('{{service.name}}');
|
||||
expect(q.series?.[0]).toStrictEqual(
|
||||
expect(q.series?.[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
labels: { 'service.name': 'adservice' },
|
||||
values: [
|
||||
@@ -121,33 +119,33 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
const scalar: ScalarData = {
|
||||
columns: [
|
||||
// group column
|
||||
({
|
||||
{
|
||||
name: 'service.name',
|
||||
queryName: 'A',
|
||||
aggregationIndex: 0,
|
||||
columnType: 'group',
|
||||
} as unknown) as ScalarData['columns'][number],
|
||||
} as unknown as ScalarData['columns'][number],
|
||||
// aggregation 0
|
||||
({
|
||||
{
|
||||
name: '__result_0',
|
||||
queryName: 'A',
|
||||
aggregationIndex: 0,
|
||||
columnType: 'aggregation',
|
||||
} as unknown) as ScalarData['columns'][number],
|
||||
} as unknown as ScalarData['columns'][number],
|
||||
// aggregation 1
|
||||
({
|
||||
{
|
||||
name: '__result_1',
|
||||
queryName: 'A',
|
||||
aggregationIndex: 1,
|
||||
columnType: 'aggregation',
|
||||
} as unknown) as ScalarData['columns'][number],
|
||||
} as unknown as ScalarData['columns'][number],
|
||||
// formula F1
|
||||
({
|
||||
{
|
||||
name: '__result',
|
||||
queryName: 'F1',
|
||||
aggregationIndex: 0,
|
||||
columnType: 'aggregation',
|
||||
} as unknown) as ScalarData['columns'][number],
|
||||
} as unknown as ScalarData['columns'][number],
|
||||
],
|
||||
data: [['adservice', 606, 1.452, 151.5]],
|
||||
};
|
||||
@@ -174,23 +172,21 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
},
|
||||
{
|
||||
type: 'builder_formula',
|
||||
spec: ({
|
||||
spec: {
|
||||
name: 'F1',
|
||||
expression: 'A * 0.25',
|
||||
} as unknown) as QueryBuilderFormula,
|
||||
} as unknown as QueryBuilderFormula,
|
||||
},
|
||||
]);
|
||||
|
||||
const input: SuccessResponse<
|
||||
MetricRangePayloadV5,
|
||||
QueryRangeRequestV5
|
||||
> = makeBaseSuccess({ data: v5Data }, params);
|
||||
const input: SuccessResponse<MetricRangePayloadV5, QueryRangeRequestV5> =
|
||||
makeBaseSuccess({ data: v5Data }, params);
|
||||
const legendMap = { A: '{{service.name}}', F1: '' };
|
||||
const result = convertV5ResponseToLegacy(input, legendMap, false);
|
||||
|
||||
expect(result.payload.data.resultType).toBe('scalar');
|
||||
const [tableEntry] = result.payload.data.result;
|
||||
expect(tableEntry.table?.columns).toStrictEqual([
|
||||
expect(tableEntry.table?.columns).toEqual([
|
||||
{
|
||||
name: 'service.name',
|
||||
queryName: 'A',
|
||||
@@ -206,7 +202,7 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
},
|
||||
{ name: 'F1', queryName: 'F1', isValueColumn: true, id: 'F1' },
|
||||
]);
|
||||
expect(tableEntry.table?.rows?.[0]).toStrictEqual({
|
||||
expect(tableEntry.table?.rows?.[0]).toEqual({
|
||||
data: {
|
||||
'service.name': 'adservice',
|
||||
'A.count()': 606,
|
||||
@@ -254,16 +250,14 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
},
|
||||
]);
|
||||
|
||||
const input: SuccessResponse<
|
||||
MetricRangePayloadV5,
|
||||
QueryRangeRequestV5
|
||||
> = makeBaseSuccess({ data: v5Data }, params);
|
||||
const input: SuccessResponse<MetricRangePayloadV5, QueryRangeRequestV5> =
|
||||
makeBaseSuccess({ data: v5Data }, params);
|
||||
const legendMap = { A: '{{service.name}}' };
|
||||
const result = convertV5ResponseToLegacy(input, legendMap, true);
|
||||
|
||||
expect(result.payload.data.resultType).toBe('scalar');
|
||||
const [tableEntry] = result.payload.data.result;
|
||||
expect(tableEntry.table?.columns).toStrictEqual([
|
||||
expect(tableEntry.table?.columns).toEqual([
|
||||
{
|
||||
name: 'service.name',
|
||||
queryName: 'A',
|
||||
@@ -273,7 +267,7 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
// Single aggregation: name resolves to legend, id resolves to queryName
|
||||
{ name: '{{service.name}}', queryName: 'A', isValueColumn: true, id: 'A' },
|
||||
]);
|
||||
expect(tableEntry.table?.rows?.[0]).toStrictEqual({
|
||||
expect(tableEntry.table?.rows?.[0]).toEqual({
|
||||
data: {
|
||||
'service.name': 'adservice',
|
||||
A: 580,
|
||||
|
||||
@@ -85,7 +85,7 @@ function convertTimeSeriesData(
|
||||
const { index, alias } = aggregation;
|
||||
const seriesData = aggregation[seriesKey];
|
||||
|
||||
if (!seriesData || seriesData.length === 0) {
|
||||
if (!seriesData || !seriesData.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ function convertTimeSeriesData(
|
||||
labels: series.labels
|
||||
? Object.fromEntries(
|
||||
series.labels.map((label: any) => [label.key.name, label.value]),
|
||||
)
|
||||
)
|
||||
: {},
|
||||
labelsArray: series.labels
|
||||
? series.labels.map((label: any) => ({ [label.key.name]: label.value }))
|
||||
@@ -358,16 +358,19 @@ export function convertV5ResponseToLegacy(
|
||||
const aggregationPerQuery =
|
||||
(params as QueryRangeRequestV5)?.compositeQuery?.queries
|
||||
?.filter((query) => query.type === 'builder_query')
|
||||
.reduce((acc, query) => {
|
||||
if (
|
||||
query.type === 'builder_query' &&
|
||||
'aggregations' in query.spec &&
|
||||
query.spec.name
|
||||
) {
|
||||
acc[query.spec.name] = query.spec.aggregations;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, any>) || {};
|
||||
.reduce(
|
||||
(acc, query) => {
|
||||
if (
|
||||
query.type === 'builder_query' &&
|
||||
'aggregations' in query.spec &&
|
||||
query.spec.name
|
||||
) {
|
||||
acc[query.spec.name] = query.spec.aggregations;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, any>,
|
||||
) || {};
|
||||
|
||||
// If formatForWeb is true, return as-is (like existing logic)
|
||||
if (formatForWeb && v5Data?.type === 'scalar') {
|
||||
|
||||
@@ -104,7 +104,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
|
||||
const result = prepareQueryRangePayloadV5(props);
|
||||
|
||||
expect(result).toStrictEqual(
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
legendMap: { A: 'Legend A', F1: 'Formula Legend' },
|
||||
queryPayload: expect.objectContaining({
|
||||
@@ -154,7 +154,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
);
|
||||
|
||||
// Legend map combines builder and formulas
|
||||
expect(result.legendMap).toStrictEqual({ A: 'Legend A', F1: 'Formula Legend' });
|
||||
expect(result.legendMap).toEqual({ A: 'Legend A', F1: 'Formula Legend' });
|
||||
|
||||
const payload: QueryRangePayloadV5 = result.queryPayload;
|
||||
|
||||
@@ -166,7 +166,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
expect(payload.formatOptions?.fillGaps).toBe(true);
|
||||
|
||||
// Variables mapped as { key: { value } }
|
||||
expect(payload.variables).toStrictEqual({
|
||||
expect(payload.variables).toEqual({
|
||||
svc: { value: 'api' },
|
||||
count: { value: 5 },
|
||||
flag: { value: true },
|
||||
@@ -226,7 +226,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
|
||||
const result = prepareQueryRangePayloadV5(props);
|
||||
|
||||
expect(result).toStrictEqual(
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
legendMap: { A: 'LP' },
|
||||
queryPayload: expect.objectContaining({
|
||||
@@ -255,7 +255,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.legendMap).toStrictEqual({ A: 'LP' });
|
||||
expect(result.legendMap).toEqual({ A: 'LP' });
|
||||
|
||||
const payload: QueryRangePayloadV5 = result.queryPayload;
|
||||
expect(payload.requestType).toBe('time_series');
|
||||
@@ -296,7 +296,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
|
||||
const result = prepareQueryRangePayloadV5(props);
|
||||
|
||||
expect(result).toStrictEqual(
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
legendMap: { Q: 'LC' },
|
||||
queryPayload: expect.objectContaining({
|
||||
@@ -324,7 +324,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result.legendMap).toStrictEqual({ Q: 'LC' });
|
||||
expect(result.legendMap).toEqual({ Q: 'LC' });
|
||||
|
||||
const payload: QueryRangePayloadV5 = result.queryPayload;
|
||||
expect(payload.requestType).toBe('scalar');
|
||||
@@ -353,7 +353,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
|
||||
const result = prepareQueryRangePayloadV5(props);
|
||||
|
||||
expect(result).toStrictEqual(
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
legendMap: {},
|
||||
queryPayload: expect.objectContaining({
|
||||
@@ -397,7 +397,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
|
||||
const result = prepareQueryRangePayloadV5(props);
|
||||
|
||||
expect(result).toStrictEqual(
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
legendMap: { A: 'Legend A' },
|
||||
queryPayload: expect.objectContaining({
|
||||
@@ -471,7 +471,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
|
||||
const result = prepareQueryRangePayloadV5(props);
|
||||
|
||||
expect(result).toStrictEqual(
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
legendMap: { A: 'Legend A' },
|
||||
queryPayload: expect.objectContaining({
|
||||
@@ -585,7 +585,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
|
||||
const result = prepareQueryRangePayloadV5(props);
|
||||
|
||||
expect(result).toStrictEqual(
|
||||
expect(result).toEqual(
|
||||
expect.objectContaining({
|
||||
legendMap: { A: '{{service.name}}' },
|
||||
queryPayload: expect.objectContaining({
|
||||
@@ -684,7 +684,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
|
||||
const result = prepareQueryRangePayloadV5(props);
|
||||
|
||||
expect(result.legendMap).toStrictEqual({ A: 'Legend A' });
|
||||
expect(result.legendMap).toEqual({ A: 'Legend A' });
|
||||
expect(result.queryPayload.compositeQuery.queries).toHaveLength(1);
|
||||
|
||||
const builderQuery = result.queryPayload.compositeQuery.queries.find(
|
||||
@@ -694,7 +694,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
|
||||
expect(logSpec.name).toBe('A');
|
||||
expect(logSpec.signal).toBe('logs');
|
||||
expect(logSpec.filter).toStrictEqual({
|
||||
expect(logSpec.filter).toEqual({
|
||||
expression:
|
||||
"service.name = 'payment-service' AND http.status_code >= 400 AND message contains 'error'",
|
||||
});
|
||||
@@ -713,7 +713,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
baseBuilderQuery({
|
||||
dataSource: DataSource.LOGS,
|
||||
filter: { expression: 'http.status_code >= 500' },
|
||||
filters: (undefined as unknown) as IBuilderQuery['filters'],
|
||||
filters: undefined as unknown as IBuilderQuery['filters'],
|
||||
}),
|
||||
],
|
||||
queryFormulas: [],
|
||||
@@ -731,7 +731,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
(q) => q.type === 'builder_query',
|
||||
) as QueryEnvelope;
|
||||
const logSpec = builderQuery.spec as LogBuilderQuery;
|
||||
expect(logSpec.filter).toStrictEqual({ expression: 'http.status_code >= 500' });
|
||||
expect(logSpec.filter).toEqual({ expression: 'http.status_code >= 500' });
|
||||
});
|
||||
|
||||
it('derives expression from filters when filter is undefined', () => {
|
||||
@@ -746,7 +746,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
queryData: [
|
||||
baseBuilderQuery({
|
||||
dataSource: DataSource.LOGS,
|
||||
filter: (undefined as unknown) as IBuilderQuery['filter'],
|
||||
filter: undefined as unknown as IBuilderQuery['filter'],
|
||||
filters: {
|
||||
items: [
|
||||
{
|
||||
@@ -775,7 +775,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
(q) => q.type === 'builder_query',
|
||||
) as QueryEnvelope;
|
||||
const logSpec = builderQuery.spec as LogBuilderQuery;
|
||||
expect(logSpec.filter).toStrictEqual({ expression: "service.name = 'checkout'" });
|
||||
expect(logSpec.filter).toEqual({ expression: "service.name = 'checkout'" });
|
||||
});
|
||||
|
||||
it('prefers filter.expression over filters when both are present', () => {
|
||||
@@ -819,7 +819,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
(q) => q.type === 'builder_query',
|
||||
) as QueryEnvelope;
|
||||
const logSpec = builderQuery.spec as LogBuilderQuery;
|
||||
expect(logSpec.filter).toStrictEqual({ expression: "service.name = 'frontend'" });
|
||||
expect(logSpec.filter).toEqual({ expression: "service.name = 'frontend'" });
|
||||
});
|
||||
|
||||
it('returns empty expression when neither filter nor filters provided', () => {
|
||||
@@ -834,8 +834,8 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
queryData: [
|
||||
baseBuilderQuery({
|
||||
dataSource: DataSource.LOGS,
|
||||
filter: (undefined as unknown) as IBuilderQuery['filter'],
|
||||
filters: (undefined as unknown) as IBuilderQuery['filters'],
|
||||
filter: undefined as unknown as IBuilderQuery['filter'],
|
||||
filters: undefined as unknown as IBuilderQuery['filters'],
|
||||
}),
|
||||
],
|
||||
queryFormulas: [],
|
||||
@@ -853,7 +853,7 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
(q) => q.type === 'builder_query',
|
||||
) as QueryEnvelope;
|
||||
const logSpec = builderQuery.spec as LogBuilderQuery;
|
||||
expect(logSpec.filter).toStrictEqual({ expression: '' });
|
||||
expect(logSpec.filter).toEqual({ expression: '' });
|
||||
});
|
||||
|
||||
it('returns empty expression when filters provided with empty items', () => {
|
||||
@@ -887,6 +887,6 @@ describe('prepareQueryRangePayloadV5', () => {
|
||||
(q) => q.type === 'builder_query',
|
||||
) as QueryEnvelope;
|
||||
const logSpec = builderQuery.spec as LogBuilderQuery;
|
||||
expect(logSpec.filter).toStrictEqual({ expression: '' });
|
||||
expect(logSpec.filter).toEqual({ expression: '' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -139,10 +139,9 @@ function createBaseSpec(
|
||||
requestType: RequestType,
|
||||
panelType?: PANEL_TYPES,
|
||||
): BaseBuilderQuery {
|
||||
const nonEmptySelectColumns = (queryData.selectColumns as (
|
||||
| BaseAutocompleteData
|
||||
| TelemetryFieldKey
|
||||
)[])?.filter((c) => ('key' in c ? c?.key : c?.name));
|
||||
const nonEmptySelectColumns = (
|
||||
queryData.selectColumns as (BaseAutocompleteData | TelemetryFieldKey)[]
|
||||
)?.filter((c) => ('key' in c ? c?.key : c?.name));
|
||||
|
||||
return {
|
||||
stepInterval: queryData?.stepInterval || null,
|
||||
@@ -160,7 +159,7 @@ function createBaseSpec(
|
||||
signal: item?.signal,
|
||||
materialized: item?.materialized,
|
||||
}),
|
||||
)
|
||||
)
|
||||
: undefined,
|
||||
limit:
|
||||
panelType === PANEL_TYPES.TABLE || panelType === PANEL_TYPES.LIST
|
||||
@@ -179,52 +178,48 @@ function createBaseSpec(
|
||||
},
|
||||
direction: order.order,
|
||||
}),
|
||||
)
|
||||
)
|
||||
: undefined,
|
||||
legend: isEmpty(queryData.legend) ? undefined : queryData.legend,
|
||||
having: isEmpty(queryData.having) ? undefined : (queryData?.having as Having),
|
||||
functions: isEmpty(queryData.functions)
|
||||
? undefined
|
||||
: queryData.functions.map(
|
||||
(func: QueryFunction): QueryFunction => {
|
||||
// Normalize function name to handle case sensitivity
|
||||
const normalizedName = normalizeFunctionName(func?.name);
|
||||
return {
|
||||
name: normalizedName as FunctionName,
|
||||
args: isEmpty(func.namedArgs)
|
||||
? func.args?.map((arg) => ({
|
||||
value: arg?.value,
|
||||
}))
|
||||
: Object.entries(func?.namedArgs || {}).map(([name, value]) => ({
|
||||
name,
|
||||
value,
|
||||
})),
|
||||
};
|
||||
},
|
||||
),
|
||||
: queryData.functions.map((func: QueryFunction): QueryFunction => {
|
||||
// Normalize function name to handle case sensitivity
|
||||
const normalizedName = normalizeFunctionName(func?.name);
|
||||
return {
|
||||
name: normalizedName as FunctionName,
|
||||
args: isEmpty(func.namedArgs)
|
||||
? func.args?.map((arg) => ({
|
||||
value: arg?.value,
|
||||
}))
|
||||
: Object.entries(func?.namedArgs || {}).map(([name, value]) => ({
|
||||
name,
|
||||
value,
|
||||
})),
|
||||
};
|
||||
}),
|
||||
selectFields: isEmpty(nonEmptySelectColumns)
|
||||
? undefined
|
||||
: nonEmptySelectColumns?.map(
|
||||
(column: any): TelemetryFieldKey => {
|
||||
const fieldName = column.name ?? column.key;
|
||||
const isDeprecated = isDeprecatedField(fieldName);
|
||||
: nonEmptySelectColumns?.map((column: any): TelemetryFieldKey => {
|
||||
const fieldName = column.name ?? column.key;
|
||||
const isDeprecated = isDeprecatedField(fieldName);
|
||||
|
||||
const fieldObj: TelemetryFieldKey = {
|
||||
name: fieldName,
|
||||
fieldDataType:
|
||||
column?.fieldDataType ?? (column?.dataType as FieldDataType),
|
||||
signal: column?.signal ?? undefined,
|
||||
};
|
||||
const fieldObj: TelemetryFieldKey = {
|
||||
name: fieldName,
|
||||
fieldDataType:
|
||||
column?.fieldDataType ?? (column?.dataType as FieldDataType),
|
||||
signal: column?.signal ?? undefined,
|
||||
};
|
||||
|
||||
// Only add fieldContext if the field is NOT deprecated
|
||||
if (!isDeprecated && fieldName !== 'name') {
|
||||
fieldObj.fieldContext =
|
||||
column?.fieldContext ?? (column?.type as FieldContext);
|
||||
}
|
||||
// Only add fieldContext if the field is NOT deprecated
|
||||
if (!isDeprecated && fieldName !== 'name') {
|
||||
fieldObj.fieldContext =
|
||||
column?.fieldContext ?? (column?.type as FieldContext);
|
||||
}
|
||||
|
||||
return fieldObj;
|
||||
},
|
||||
),
|
||||
return fieldObj;
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -236,14 +231,15 @@ export function parseAggregations(
|
||||
const result: { expression: string; alias?: string }[] = [];
|
||||
// Matches function calls like "count()" or "sum(field)" with optional alias like "as 'alias'"
|
||||
// Handles quoted ('alias'), dash-separated (field-name), and unquoted values after "as" keyword
|
||||
const regex = /([a-zA-Z0-9_]+\([^)]*\))(?:\s*as\s+((?:'[^']*'|"[^"]*"|[a-zA-Z0-9_-]+)))?/g;
|
||||
const regex =
|
||||
/([a-zA-Z0-9_]+\([^)]*\))(?:\s*as\s+((?:'[^']*'|"[^"]*"|[a-zA-Z0-9_-]+)))?/g;
|
||||
let match = regex.exec(expression);
|
||||
while (match !== null) {
|
||||
const expr = match[1];
|
||||
let alias = match[2] || availableAlias; // Use provided alias or availableAlias if not matched
|
||||
if (alias) {
|
||||
// Remove quotes if present
|
||||
alias = alias.replaceAll(/^['"]|['"]$/g, '');
|
||||
alias = alias.replace(/^['"]|['"]$/g, '');
|
||||
result.push({ expression: expr, alias });
|
||||
} else {
|
||||
result.push({ expression: expr });
|
||||
@@ -365,10 +361,9 @@ function createTraceOperatorBaseSpec(
|
||||
requestType: RequestType,
|
||||
panelType?: PANEL_TYPES,
|
||||
): BaseBuilderQuery {
|
||||
const nonEmptySelectColumns = (queryData.selectColumns as (
|
||||
| BaseAutocompleteData
|
||||
| TelemetryFieldKey
|
||||
)[])?.filter((c) => ('key' in c ? c?.key : c?.name));
|
||||
const nonEmptySelectColumns = (
|
||||
queryData.selectColumns as (BaseAutocompleteData | TelemetryFieldKey)[]
|
||||
)?.filter((c) => ('key' in c ? c?.key : c?.name));
|
||||
|
||||
const {
|
||||
stepInterval,
|
||||
@@ -395,7 +390,7 @@ function createTraceOperatorBaseSpec(
|
||||
signal: item?.signal,
|
||||
materialized: item?.materialized,
|
||||
}),
|
||||
)
|
||||
)
|
||||
: undefined,
|
||||
limit:
|
||||
panelType === PANEL_TYPES.TABLE || panelType === PANEL_TYPES.LIST
|
||||
@@ -411,7 +406,7 @@ function createTraceOperatorBaseSpec(
|
||||
},
|
||||
direction: order.order,
|
||||
}),
|
||||
)
|
||||
)
|
||||
: undefined,
|
||||
legend: isEmpty(legend) ? undefined : legend,
|
||||
having: isEmpty(having) ? undefined : (having as Having),
|
||||
@@ -425,7 +420,7 @@ function createTraceOperatorBaseSpec(
|
||||
fieldContext: column?.fieldContext ?? (column?.type as FieldContext),
|
||||
signal: column?.signal ?? undefined,
|
||||
}),
|
||||
),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -507,18 +502,22 @@ export function convertClickHouseQueriesToV5(
|
||||
/**
|
||||
* Helper function to reduce query arrays to objects
|
||||
*/
|
||||
function reduceQueriesToObject(
|
||||
queryArray: any[],
|
||||
): { queries: Record<string, any>; legends: Record<string, string> } {
|
||||
function reduceQueriesToObject(queryArray: any[]): {
|
||||
queries: Record<string, any>;
|
||||
legends: Record<string, string>;
|
||||
} {
|
||||
const legends: Record<string, string> = {};
|
||||
const queries = queryArray.reduce((acc, queryItem) => {
|
||||
if (!queryItem.query) {
|
||||
const queries = queryArray.reduce(
|
||||
(acc, queryItem) => {
|
||||
if (!queryItem.query) {
|
||||
return acc;
|
||||
}
|
||||
acc[queryItem.name] = queryItem;
|
||||
legends[queryItem.name] = queryItem.legend;
|
||||
return acc;
|
||||
}
|
||||
acc[queryItem.name] = queryItem;
|
||||
legends[queryItem.name] = queryItem.legend;
|
||||
return acc;
|
||||
}, {} as Record<string, any>);
|
||||
},
|
||||
{} as Record<string, any>,
|
||||
);
|
||||
|
||||
return { queries, legends };
|
||||
}
|
||||
@@ -554,7 +553,7 @@ export const prepareQueryRangePayloadV5 = ({
|
||||
queryTraceOperator && queryTraceOperator.length > 0
|
||||
? queryTraceOperator.filter((traceOperator) =>
|
||||
Boolean(traceOperator.expression.trim()),
|
||||
)
|
||||
)
|
||||
: [];
|
||||
|
||||
const currentTraceOperator = mapQueryDataToApi(
|
||||
@@ -634,8 +633,8 @@ export const prepareQueryRangePayloadV5 = ({
|
||||
// Create V5 payload
|
||||
const queryPayload: QueryRangePayloadV5 = {
|
||||
schemaVersion: 'v1',
|
||||
start: startTime ? startTime * 1e3 : Number.parseInt(start, 10) * 1e3,
|
||||
end: endTime ? endTime * 1e3 : Number.parseInt(end, 10) * 1e3,
|
||||
start: startTime ? startTime * 1e3 : parseInt(start, 10) * 1e3,
|
||||
end: endTime ? endTime * 1e3 : parseInt(end, 10) * 1e3,
|
||||
requestType,
|
||||
compositeQuery: {
|
||||
queries,
|
||||
@@ -648,15 +647,18 @@ export const prepareQueryRangePayloadV5 = ({
|
||||
: graphType === PANEL_TYPES.TABLE),
|
||||
fillGaps: fillGaps || false,
|
||||
},
|
||||
variables: Object.entries(variables).reduce((acc, [key, value]) => {
|
||||
acc[key] = {
|
||||
value,
|
||||
type: dynamicVariables
|
||||
?.find((v) => v.name === key)
|
||||
?.type?.toLowerCase() as VariableType,
|
||||
};
|
||||
return acc;
|
||||
}, {} as Record<string, VariableItem>),
|
||||
variables: Object.entries(variables).reduce(
|
||||
(acc, [key, value]) => {
|
||||
acc[key] = {
|
||||
value,
|
||||
type: dynamicVariables
|
||||
?.find((v) => v.name === key)
|
||||
?.type?.toLowerCase() as VariableType,
|
||||
};
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, VariableItem>,
|
||||
),
|
||||
};
|
||||
|
||||
return { legendMap, queryPayload };
|
||||
|
||||
3
frontend/src/assets/Logos/baseten.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" height="34" viewBox="0 0 131 34">
|
||||
<path fill="currentColor" d="M.36 8.6h16.7v5.6H6.04c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h11.02v5.6h-5.33c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h4.98c.2 0 .35-.15.35-.35V25.4h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34v-5.6h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34V3.35c0-.2-.16-.35-.35-.35H.36c-.2 0-.36.16-.36.35v4.9c0 .2.16.35.36.35ZM44.41 14.7c-.5-.5-1.1-.9-1.76-1.18a5.62 5.62 0 0 0-4.6.17c-.73.37-1.32.91-1.75 1.62h-.17V8.59H34.1v16.83h2.04v-1.81h.17c.21.36.47.67.77.94.31.25.65.48 1.01.67.37.18.77.31 1.18.39a6.2 6.2 0 0 0 3.39-.24 5.36 5.36 0 0 0 3.02-3.1c.29-.75.44-1.62.44-2.6v-.47c0-.96-.16-1.83-.47-2.58-.3-.75-.7-1.4-1.23-1.9v-.01Zm-5.87.66a3.9 3.9 0 0 1 4.34.84c.36.35.64.8.83 1.3.2.5.3 1.07.3 1.7v.47c0 .64-.1 1.23-.3 1.74a3.75 3.75 0 0 1-2.06 2.15 4.27 4.27 0 0 1-3.12-.03 3.86 3.86 0 0 1-2.09-2.2c-.2-.52-.3-1.11-.3-1.75v-.29c0-.62.1-1.2.3-1.7v-.01c.21-.53.5-.99.84-1.36.36-.37.78-.66 1.26-.86ZM97.04 8.59H95v4.86h-2.94v1.86H95v8.17c0 .56.17 1.03.53 1.4.37.35.84.54 1.4.54h4.18v-1.87h-3.5c-.2 0-.33-.05-.43-.15-.1-.1-.14-.27-.14-.49v-7.6h4.65v-1.86h-4.65V8.59ZM114.61 15a5.48 5.48 0 0 0-1.8-1.33 5.6 5.6 0 0 0-2.57-.56 6.17 6.17 0 0 0-4.26 1.7 5.6 5.6 0 0 0-1.72 4.2v.57c0 .9.15 1.75.44 2.5a5.58 5.58 0 0 0 5.5 3.67c1.55 0 2.8-.35 3.72-1.04a5.35 5.35 0 0 0 1.91-2.73l.03-.07-1.94-.52-.02.07c-.11.33-.27.64-.46.94-.17.27-.4.52-.7.74-.28.22-.63.39-1.04.51-.41.13-.9.19-1.46.19a3.8 3.8 0 0 1-2.84-1.05 4.07 4.07 0 0 1-1.1-2.7h9.68v-1.6c0-.54-.11-1.12-.34-1.75a5.04 5.04 0 0 0-1.03-1.74Zm-8.25 3.21a3.8 3.8 0 0 1 1.22-2.25 4.19 4.19 0 0 1 3.99-.7c.44.16.83.38 1.17.66.34.27.62.62.82 1.02.21.38.34.8.38 1.27h-7.58ZM129.09 14.42a4.47 4.47 0 0 0-3.37-1.3c-.93 0-1.73.2-2.4.59-.64.39-1.15.97-1.52 1.74h-.17v-2h-2.04v11.97h2.04v-6.23c0-1.26.32-2.28.95-3.02a3.31 3.31 0 0 1 2.65-1.14c.94 0 1.7.3 2.24.9.56.6.83 1.52.83 2.74v6.75h2.04v-7.13c0-1.71-.42-3.02-1.25-3.87ZM88.1 15a5.48 5.48 0 0 0-1.78-1.33 5.6 5.6 0 0 0-2.58-.56 6.17 6.17 0 0 0-4.27 1.7 5.59 5.59 0 0 0-1.71 4.2v.56c0 .92.14 1.76.44 2.51a5.6 5.6 0 0 0 5.5 3.67c1.55 0 2.8-.35 3.72-1.04a5.36 5.36 0 0 0 1.91-2.73l.03-.07-1.94-.52-.03.07c-.1.32-.26.64-.45.94-.17.27-.4.52-.7.74-.29.21-.64.39-1.05.51-.4.12-.9.19-1.45.19a3.8 3.8 0 0 1-2.85-1.05 4.07 4.07 0 0 1-1.09-2.7h9.68v-1.61c0-.53-.12-1.12-.34-1.74A5.03 5.03 0 0 0 88.1 15Zm-8.24 3.21a3.83 3.83 0 0 1 1.22-2.25 4.2 4.2 0 0 1 3.99-.7c.44.16.83.38 1.16.66.35.27.62.62.83 1.02.2.38.33.8.37 1.27h-7.57ZM73.65 19.42a6.11 6.11 0 0 0-3.23-1.02 6.63 6.63 0 0 1-2.68-.58c-.47-.3-.7-.7-.7-1.25 0-.27.08-.5.21-.7.14-.2.33-.38.56-.52a4.05 4.05 0 0 1 1.78-.42c.85 0 1.54.21 2.06.63.53.41.83 1 .91 1.73l.01.1 1.95-.47-.01-.07c-.07-.45-.22-.91-.45-1.36a3.46 3.46 0 0 0-.94-1.2 4.6 4.6 0 0 0-1.52-.84 6.05 6.05 0 0 0-2.1-.34c-.58 0-1.13.07-1.65.22-.52.14-1 .36-1.43.65-.41.3-.74.66-.99 1.1a2.9 2.9 0 0 0-.37 1.49v.14c0 1.06.38 1.87 1.14 2.42a6.2 6.2 0 0 0 3.24.97c1.18.08 2.04.26 2.56.54.5.28.75.72.75 1.36 0 .6-.25 1.06-.78 1.4-.52.32-1.22.48-2.1.48a3.68 3.68 0 0 1-2.46-.79 3.13 3.13 0 0 1-1.04-2.14v-.09l-1.93.46h-.02v.07a4.4 4.4 0 0 0 3.1 4c.7.24 1.52.36 2.46.36.7 0 1.35-.09 1.93-.27.6-.16 1.12-.4 1.53-.72A3.38 3.38 0 0 0 74.8 22v-.14c0-1.07-.39-1.9-1.14-2.44ZM60.25 23.4c-.1-.1-.14-.27-.14-.49v-9.46h-2.05v1.85h-.16a3.78 3.78 0 0 0-1.61-1.63 4.62 4.62 0 0 0-2.26-.56c-.77 0-1.5.14-2.19.41a5.27 5.27 0 0 0-3.02 3.12c-.29.75-.44 1.63-.44 2.6v.38c0 .99.15 1.87.44 2.63.3.75.7 1.4 1.2 1.93a5.48 5.48 0 0 0 4.05 1.57c.8 0 1.51-.2 2.2-.58.69-.38 1.24-.97 1.63-1.75h.16v.06c0 .56.18 1.03.53 1.4.37.35.85.54 1.41.54h1.36v-1.87h-.68c-.2 0-.34-.05-.43-.15Zm-4.46.13c-.46.2-.97.3-1.52.3a3.68 3.68 0 0 1-2.75-1.09 4.42 4.42 0 0 1-1.05-3.12v-.38c0-.62.1-1.2.29-1.71a3.65 3.65 0 0 1 5-2.17c.47.2.88.49 1.21.86.35.37.62.83.8 1.36.2.5.3 1.08.3 1.7v.3c0 .63-.1 1.23-.3 1.76-.18.5-.45.96-.78 1.33-.33.37-.73.66-1.2.86Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
13
frontend/src/assets/Logos/dbos.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg width="109" height="24" viewBox="0 0 109 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_125_22125)">
|
||||
<path d="M0 -2.08616e-07V24H17.9352C22.9911 24 26.0999 21.0858 26.0999 17.04V6.96C26.0999 2.91432 22.9911 -2.08616e-07 17.9352 -2.08616e-07H0ZM6.76413 5.82864H19.1992V18.1714H6.76413V5.82864Z" fill="currentColor"/>
|
||||
<path d="M46.7659 18.6172H35.0824V14.16H46.7659V18.6172ZM46.595 5.38296V9.5658H35.0824V5.38296H46.595ZM50.2846 12.1373V11.5886C52.5734 10.5258 53.7008 8.8458 53.7008 6.13728C53.7008 2.64012 50.9337 0.000116183 45.5361 0.000116183H28.3184V24H45.7752C51.1728 24 53.9399 21.8401 53.9399 18.0685C53.9399 15.0172 52.6418 13.2001 50.2846 12.1373Z" fill="currentColor"/>
|
||||
<path d="M62.397 18.1714H74.8319V5.82864H62.397V18.1714ZM63.6609 24C58.6049 24 55.4961 21.0858 55.4961 17.04V6.96012C55.4961 2.91432 58.6049 0.000116183 63.6609 0.000116183H73.568C78.6238 0.000116183 81.7326 2.91432 81.7326 6.96012V17.04C81.7326 21.0858 78.6238 24 73.568 24H63.6609Z" fill="currentColor"/>
|
||||
<path d="M101.66 15.12L90.8995 14.3658C85.5361 13.9886 83.418 11.1772 83.418 7.47432V6.96012C83.418 2.91432 86.5266 0.000116183 91.5827 0.000116183H100.157C105.214 0.000116183 108.323 2.91432 108.323 6.96012V7.98864H101.968V5.14284H90.2504V8.43432L100.601 9.18864C105.999 9.56568 108.493 12.8572 108.493 16.5257V17.04C108.493 20.7428 105.384 24 100.328 24H91.5827C86.5266 24 83.418 20.7428 83.418 17.04V16.0115H89.7722V18.8572H101.66V15.12Z" fill="currentColor"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_125_22125">
|
||||
<rect width="108.494" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1,70 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 142.5 145.6" style="enable-background:new 0 0 142.5 145.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#565656;}
|
||||
.st1{fill:url(#SVGID_1_);}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M28.7,131.5c-0.3,7.9-6.6,14.1-14.4,14.1C6.1,145.6,0,139,0,130.9s6.6-14.7,14.7-14.7c3.6,0,7.2,1.6,10.2,4.4
|
||||
l-2.3,2.9c-2.3-2-5.1-3.4-7.9-3.4c-5.9,0-10.8,4.8-10.8,10.8c0,6.1,4.6,10.8,10.4,10.8c5.2,0,9.3-3.8,10.2-8.8H12.6v-3.5h16.1
|
||||
V131.5z"/>
|
||||
<path class="st0" d="M42.3,129.5h-2.2c-2.4,0-4.4,2-4.4,4.4v11.4h-3.9v-19.6H35v1.6c1.1-1.1,2.7-1.6,4.6-1.6h4.2L42.3,129.5z"/>
|
||||
<path class="st0" d="M63.7,145.3h-3.4v-2.5c-2.6,2.5-6.6,3.7-10.7,1.9c-3-1.3-5.3-4.1-5.9-7.4c-1.2-6.3,3.7-11.9,9.9-11.9
|
||||
c2.6,0,5,1.1,6.7,2.8v-2.5h3.4V145.3z M59.7,137c0.9-4-2.1-7.6-6-7.6c-3.4,0-6.1,2.8-6.1,6.1c0,3.8,3.3,6.7,7.2,6.1
|
||||
C57.1,141.2,59.1,139.3,59.7,137z"/>
|
||||
<path class="st0" d="M71.5,124.7v1.1h6.2v3.4h-6.2v16.1h-3.8v-20.5c0-4.3,3.1-6.8,7-6.8h4.7l-1.6,3.7h-3.1
|
||||
C72.9,121.6,71.5,123,71.5,124.7z"/>
|
||||
<path class="st0" d="M98.5,145.3h-3.3v-2.5c-2.6,2.5-6.6,3.7-10.7,1.9c-3-1.3-5.3-4.1-5.9-7.4c-1.2-6.3,3.7-11.9,9.9-11.9
|
||||
c2.6,0,5,1.1,6.7,2.8v-2.5h3.4v19.6H98.5z M94.5,137c0.9-4-2.1-7.6-6-7.6c-3.4,0-6.1,2.8-6.1,6.1c0,3.8,3.3,6.7,7.2,6.1
|
||||
C92,141.2,93.9,139.3,94.5,137z"/>
|
||||
<path class="st0" d="M119.4,133.8v11.5h-3.9v-11.6c0-2.4-2-4.4-4.4-4.4c-2.5,0-4.4,2-4.4,4.4v11.6h-3.9v-19.6h3.2v1.7
|
||||
c1.4-1.3,3.3-2,5.2-2C115.8,125.5,119.4,129.2,119.4,133.8z"/>
|
||||
<path class="st0" d="M142.4,145.3h-3.3v-2.5c-2.6,2.5-6.6,3.7-10.7,1.9c-3-1.3-5.3-4.1-5.9-7.4c-1.2-6.3,3.7-11.9,9.9-11.9
|
||||
c2.6,0,5,1.1,6.7,2.8v-2.5h3.4v19.6H142.4z M138.4,137c0.9-4-2.1-7.6-6-7.6c-3.4,0-6.1,2.8-6.1,6.1c0,3.8,3.3,6.7,7.2,6.1
|
||||
C135.9,141.2,137.8,139.3,138.4,137z"/>
|
||||
</g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="71.25" y1="10.4893" x2="71.25" y2="113.3415" gradientTransform="matrix(1 0 0 -1 0 148.6)">
|
||||
<stop offset="0" style="stop-color:#FCEE1F"/>
|
||||
<stop offset="1" style="stop-color:#F15B2A"/>
|
||||
</linearGradient>
|
||||
<path class="st1" d="M122.9,49.9c-0.2-1.9-0.5-4.1-1.1-6.5c-0.6-2.4-1.6-5-2.9-7.8c-1.4-2.7-3.1-5.6-5.4-8.3
|
||||
c-0.9-1.1-1.9-2.1-2.9-3.2c1.6-6.3-1.9-11.8-1.9-11.8c-6.1-0.4-9.9,1.9-11.3,2.9c-0.2-0.1-0.5-0.2-0.7-0.3c-1-0.4-2.1-0.8-3.2-1.2
|
||||
c-1.1-0.3-2.2-0.7-3.3-0.9c-1.1-0.3-2.3-0.5-3.5-0.7c-0.2,0-0.4-0.1-0.6-0.1C83.5,3.6,75.9,0,75.9,0c-8.7,5.6-10.4,13.1-10.4,13.1
|
||||
s0,0.2-0.1,0.4c-0.5,0.1-0.9,0.3-1.4,0.4c-0.6,0.2-1.3,0.4-1.9,0.7c-0.6,0.3-1.3,0.5-1.9,0.8c-1.3,0.6-2.5,1.2-3.8,1.9
|
||||
c-1.2,0.7-2.4,1.4-3.5,2.2c-0.2-0.1-0.3-0.2-0.3-0.2c-11.7-4.5-22.1,0.9-22.1,0.9c-0.9,12.5,4.7,20.3,5.8,21.7
|
||||
c-0.3,0.8-0.5,1.5-0.8,2.3c-0.9,2.8-1.5,5.7-1.9,8.7c-0.1,0.4-0.1,0.9-0.2,1.3c-10.8,5.3-14,16.3-14,16.3c9,10.4,19.6,11,19.6,11
|
||||
l0,0c1.3,2.4,2.9,4.7,4.6,6.8c0.7,0.9,1.5,1.7,2.3,2.6c-3.3,9.4,0.5,17.3,0.5,17.3c10.1,0.4,16.7-4.4,18.1-5.5c1,0.3,2,0.6,3,0.9
|
||||
c3.1,0.8,6.3,1.3,9.4,1.4c0.8,0,1.6,0,2.4,0h0.4H80h0.5H81l0,0c4.7,6.8,13.1,7.7,13.1,7.7c5.9-6.3,6.3-12.4,6.3-13.8l0,0
|
||||
c0,0,0,0,0-0.1s0-0.2,0-0.2l0,0c0-0.1,0-0.2,0-0.3c1.2-0.9,2.4-1.8,3.6-2.8c2.4-2.1,4.4-4.6,6.2-7.2c0.2-0.2,0.3-0.5,0.5-0.7
|
||||
c6.7,0.4,11.4-4.2,11.4-4.2c-1.1-7-5.1-10.4-5.9-11l0,0c0,0,0,0-0.1-0.1l-0.1-0.1l0,0l-0.1-0.1c0-0.4,0.1-0.8,0.1-1.3
|
||||
c0.1-0.8,0.1-1.5,0.1-2.3v-0.6v-0.3v-0.1c0-0.2,0-0.1,0-0.2v-0.5v-0.6c0-0.2,0-0.4,0-0.6s0-0.4-0.1-0.6l-0.1-0.6l-0.1-0.6
|
||||
c-0.1-0.8-0.3-1.5-0.4-2.3c-0.7-3-1.9-5.9-3.4-8.4c-1.6-2.6-3.5-4.8-5.7-6.8c-2.2-1.9-4.6-3.5-7.2-4.6c-2.6-1.2-5.2-1.9-7.9-2.2
|
||||
c-1.3-0.2-2.7-0.2-4-0.2h-0.5h-0.1h-0.2h-0.2h-0.5c-0.2,0-0.4,0-0.5,0c-0.7,0.1-1.4,0.2-2,0.3c-2.7,0.5-5.2,1.5-7.4,2.8
|
||||
c-2.2,1.3-4.1,3-5.7,4.9s-2.8,3.9-3.6,6.1c-0.8,2.1-1.3,4.4-1.4,6.5c0,0.5,0,1.1,0,1.6c0,0.1,0,0.3,0,0.4v0.4c0,0.3,0,0.5,0.1,0.8
|
||||
c0.1,1.1,0.3,2.1,0.6,3.1c0.6,2,1.5,3.8,2.7,5.4s2.5,2.8,4,3.8s3,1.7,4.6,2.2c1.6,0.5,3.1,0.7,4.5,0.6c0.2,0,0.4,0,0.5,0
|
||||
c0.1,0,0.2,0,0.3,0s0.2,0,0.3,0c0.2,0,0.3,0,0.5,0h0.1h0.1c0.1,0,0.2,0,0.3,0c0.2,0,0.4-0.1,0.5-0.1c0.2,0,0.3-0.1,0.5-0.1
|
||||
c0.3-0.1,0.7-0.2,1-0.3c0.6-0.2,1.2-0.5,1.8-0.7c0.6-0.3,1.1-0.6,1.5-0.9c0.1-0.1,0.3-0.2,0.4-0.3c0.5-0.4,0.6-1.1,0.2-1.6
|
||||
c-0.4-0.4-1-0.5-1.5-0.3C88,74,87.9,74,87.7,74.1c-0.4,0.2-0.9,0.4-1.3,0.5c-0.5,0.1-1,0.3-1.5,0.4c-0.3,0-0.5,0.1-0.8,0.1
|
||||
c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0-0.4,0s-0.3,0-0.4,0c-0.2,0-0.3,0-0.5,0c0,0-0.1,0,0,0h-0.1h-0.1c-0.1,0-0.1,0-0.2,0
|
||||
s-0.3,0-0.4-0.1c-1.1-0.2-2.3-0.5-3.4-1c-1.1-0.5-2.2-1.2-3.1-2.1c-1-0.9-1.8-1.9-2.5-3.1c-0.7-1.2-1.1-2.5-1.3-3.8
|
||||
c-0.1-0.7-0.2-1.4-0.1-2.1c0-0.2,0-0.4,0-0.6c0,0.1,0,0,0,0v-0.1v-0.1c0-0.1,0-0.2,0-0.3c0-0.4,0.1-0.7,0.2-1.1c0.5-3,2-5.9,4.3-8.1
|
||||
c0.6-0.6,1.2-1.1,1.9-1.5c0.7-0.5,1.4-0.9,2.1-1.2c0.7-0.3,1.5-0.6,2.3-0.8s1.6-0.4,2.4-0.4c0.4,0,0.8-0.1,1.2-0.1
|
||||
c0.1,0,0.2,0,0.3,0h0.3h0.2c0.1,0,0,0,0,0h0.1h0.3c0.9,0.1,1.8,0.2,2.6,0.4c1.7,0.4,3.4,1,5,1.9c3.2,1.8,5.9,4.5,7.5,7.8
|
||||
c0.8,1.6,1.4,3.4,1.7,5.3c0.1,0.5,0.1,0.9,0.2,1.4v0.3V66c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3v0.3v0.3c0,0.2,0,0.6,0,0.8
|
||||
c0,0.5-0.1,1-0.1,1.5c-0.1,0.5-0.1,1-0.2,1.5s-0.2,1-0.3,1.5c-0.2,1-0.6,1.9-0.9,2.9c-0.7,1.9-1.7,3.7-2.9,5.3
|
||||
c-2.4,3.3-5.7,6-9.4,7.7c-1.9,0.8-3.8,1.5-5.8,1.8c-1,0.2-2,0.3-3,0.3H81h-0.2h-0.3H80h-0.3c0.1,0,0,0,0,0h-0.1
|
||||
c-0.5,0-1.1,0-1.6-0.1c-2.2-0.2-4.3-0.6-6.4-1.2c-2.1-0.6-4.1-1.4-6-2.4c-3.8-2-7.2-4.9-9.9-8.2c-1.3-1.7-2.5-3.5-3.5-5.4
|
||||
s-1.7-3.9-2.3-5.9c-0.6-2-0.9-4.1-1-6.2v-0.4v-0.1v-0.1v-0.2V60v-0.1v-0.1v-0.2v-0.5V59l0,0v-0.2c0-0.3,0-0.5,0-0.8
|
||||
c0-1,0.1-2.1,0.3-3.2c0.1-1.1,0.3-2.1,0.5-3.2c0.2-1.1,0.5-2.1,0.8-3.2c0.6-2.1,1.3-4.1,2.2-6c1.8-3.8,4.1-7.2,6.8-9.9
|
||||
c0.7-0.7,1.4-1.3,2.2-1.9c0.3-0.3,1-0.9,1.8-1.4c0.8-0.5,1.6-1,2.5-1.4c0.4-0.2,0.8-0.4,1.3-0.6c0.2-0.1,0.4-0.2,0.7-0.3
|
||||
c0.2-0.1,0.4-0.2,0.7-0.3c0.9-0.4,1.8-0.7,2.7-1c0.2-0.1,0.5-0.1,0.7-0.2c0.2-0.1,0.5-0.1,0.7-0.2c0.5-0.1,0.9-0.2,1.4-0.4
|
||||
c0.2-0.1,0.5-0.1,0.7-0.2c0.2,0,0.5-0.1,0.7-0.1c0.2,0,0.5-0.1,0.7-0.1l0.4-0.1l0.4-0.1c0.2,0,0.5-0.1,0.7-0.1
|
||||
c0.3,0,0.5-0.1,0.8-0.1c0.2,0,0.6-0.1,0.8-0.1c0.2,0,0.3,0,0.5-0.1h0.3h0.2h0.2c0.3,0,0.5,0,0.8-0.1h0.4c0,0,0.1,0,0,0h0.1h0.2
|
||||
c0.2,0,0.5,0,0.7,0c0.9,0,1.8,0,2.7,0c1.8,0.1,3.6,0.3,5.3,0.6c3.4,0.6,6.7,1.7,9.6,3.2c2.9,1.4,5.6,3.2,7.8,5.1
|
||||
c0.1,0.1,0.3,0.2,0.4,0.4c0.1,0.1,0.3,0.2,0.4,0.4c0.3,0.2,0.5,0.5,0.8,0.7c0.3,0.2,0.5,0.5,0.8,0.7c0.2,0.3,0.5,0.5,0.7,0.8
|
||||
c1,1,1.9,2.1,2.7,3.1c1.6,2.1,2.9,4.2,3.9,6.2c0.1,0.1,0.1,0.2,0.2,0.4c0.1,0.1,0.1,0.2,0.2,0.4s0.2,0.5,0.4,0.7
|
||||
c0.1,0.2,0.2,0.5,0.3,0.7c0.1,0.2,0.2,0.5,0.3,0.7c0.4,0.9,0.7,1.8,1,2.7c0.5,1.4,0.8,2.6,1.1,3.6c0.1,0.4,0.5,0.7,0.9,0.7
|
||||
c0.5,0,0.8-0.4,0.8-0.9C123,52.7,123,51.4,122.9,49.9z"/>
|
||||
</svg>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 142.5 145.6" style="enable-background:new 0 0 142.5 145.6;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#565656;}
|
||||
.st1{fill:url(#SVGID_1_);}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M28.7,131.5c-0.3,7.9-6.6,14.1-14.4,14.1C6.1,145.6,0,139,0,130.9s6.6-14.7,14.7-14.7c3.6,0,7.2,1.6,10.2,4.4
|
||||
l-2.3,2.9c-2.3-2-5.1-3.4-7.9-3.4c-5.9,0-10.8,4.8-10.8,10.8c0,6.1,4.6,10.8,10.4,10.8c5.2,0,9.3-3.8,10.2-8.8H12.6v-3.5h16.1
|
||||
V131.5z"/>
|
||||
<path class="st0" d="M42.3,129.5h-2.2c-2.4,0-4.4,2-4.4,4.4v11.4h-3.9v-19.6H35v1.6c1.1-1.1,2.7-1.6,4.6-1.6h4.2L42.3,129.5z"/>
|
||||
<path class="st0" d="M63.7,145.3h-3.4v-2.5c-2.6,2.5-6.6,3.7-10.7,1.9c-3-1.3-5.3-4.1-5.9-7.4c-1.2-6.3,3.7-11.9,9.9-11.9
|
||||
c2.6,0,5,1.1,6.7,2.8v-2.5h3.4V145.3z M59.7,137c0.9-4-2.1-7.6-6-7.6c-3.4,0-6.1,2.8-6.1,6.1c0,3.8,3.3,6.7,7.2,6.1
|
||||
C57.1,141.2,59.1,139.3,59.7,137z"/>
|
||||
<path class="st0" d="M71.5,124.7v1.1h6.2v3.4h-6.2v16.1h-3.8v-20.5c0-4.3,3.1-6.8,7-6.8h4.7l-1.6,3.7h-3.1
|
||||
C72.9,121.6,71.5,123,71.5,124.7z"/>
|
||||
<path class="st0" d="M98.5,145.3h-3.3v-2.5c-2.6,2.5-6.6,3.7-10.7,1.9c-3-1.3-5.3-4.1-5.9-7.4c-1.2-6.3,3.7-11.9,9.9-11.9
|
||||
c2.6,0,5,1.1,6.7,2.8v-2.5h3.4v19.6H98.5z M94.5,137c0.9-4-2.1-7.6-6-7.6c-3.4,0-6.1,2.8-6.1,6.1c0,3.8,3.3,6.7,7.2,6.1
|
||||
C92,141.2,93.9,139.3,94.5,137z"/>
|
||||
<path class="st0" d="M119.4,133.8v11.5h-3.9v-11.6c0-2.4-2-4.4-4.4-4.4c-2.5,0-4.4,2-4.4,4.4v11.6h-3.9v-19.6h3.2v1.7
|
||||
c1.4-1.3,3.3-2,5.2-2C115.8,125.5,119.4,129.2,119.4,133.8z"/>
|
||||
<path class="st0" d="M142.4,145.3h-3.3v-2.5c-2.6,2.5-6.6,3.7-10.7,1.9c-3-1.3-5.3-4.1-5.9-7.4c-1.2-6.3,3.7-11.9,9.9-11.9
|
||||
c2.6,0,5,1.1,6.7,2.8v-2.5h3.4v19.6H142.4z M138.4,137c0.9-4-2.1-7.6-6-7.6c-3.4,0-6.1,2.8-6.1,6.1c0,3.8,3.3,6.7,7.2,6.1
|
||||
C135.9,141.2,137.8,139.3,138.4,137z"/>
|
||||
</g>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="71.25" y1="10.4893" x2="71.25" y2="113.3415" gradientTransform="matrix(1 0 0 -1 0 148.6)">
|
||||
<stop offset="0" style="stop-color:#FCEE1F"/>
|
||||
<stop offset="1" style="stop-color:#F15B2A"/>
|
||||
</linearGradient>
|
||||
<path class="st1" d="M122.9,49.9c-0.2-1.9-0.5-4.1-1.1-6.5c-0.6-2.4-1.6-5-2.9-7.8c-1.4-2.7-3.1-5.6-5.4-8.3
|
||||
c-0.9-1.1-1.9-2.1-2.9-3.2c1.6-6.3-1.9-11.8-1.9-11.8c-6.1-0.4-9.9,1.9-11.3,2.9c-0.2-0.1-0.5-0.2-0.7-0.3c-1-0.4-2.1-0.8-3.2-1.2
|
||||
c-1.1-0.3-2.2-0.7-3.3-0.9c-1.1-0.3-2.3-0.5-3.5-0.7c-0.2,0-0.4-0.1-0.6-0.1C83.5,3.6,75.9,0,75.9,0c-8.7,5.6-10.4,13.1-10.4,13.1
|
||||
s0,0.2-0.1,0.4c-0.5,0.1-0.9,0.3-1.4,0.4c-0.6,0.2-1.3,0.4-1.9,0.7c-0.6,0.3-1.3,0.5-1.9,0.8c-1.3,0.6-2.5,1.2-3.8,1.9
|
||||
c-1.2,0.7-2.4,1.4-3.5,2.2c-0.2-0.1-0.3-0.2-0.3-0.2c-11.7-4.5-22.1,0.9-22.1,0.9c-0.9,12.5,4.7,20.3,5.8,21.7
|
||||
c-0.3,0.8-0.5,1.5-0.8,2.3c-0.9,2.8-1.5,5.7-1.9,8.7c-0.1,0.4-0.1,0.9-0.2,1.3c-10.8,5.3-14,16.3-14,16.3c9,10.4,19.6,11,19.6,11
|
||||
l0,0c1.3,2.4,2.9,4.7,4.6,6.8c0.7,0.9,1.5,1.7,2.3,2.6c-3.3,9.4,0.5,17.3,0.5,17.3c10.1,0.4,16.7-4.4,18.1-5.5c1,0.3,2,0.6,3,0.9
|
||||
c3.1,0.8,6.3,1.3,9.4,1.4c0.8,0,1.6,0,2.4,0h0.4H80h0.5H81l0,0c4.7,6.8,13.1,7.7,13.1,7.7c5.9-6.3,6.3-12.4,6.3-13.8l0,0
|
||||
c0,0,0,0,0-0.1s0-0.2,0-0.2l0,0c0-0.1,0-0.2,0-0.3c1.2-0.9,2.4-1.8,3.6-2.8c2.4-2.1,4.4-4.6,6.2-7.2c0.2-0.2,0.3-0.5,0.5-0.7
|
||||
c6.7,0.4,11.4-4.2,11.4-4.2c-1.1-7-5.1-10.4-5.9-11l0,0c0,0,0,0-0.1-0.1l-0.1-0.1l0,0l-0.1-0.1c0-0.4,0.1-0.8,0.1-1.3
|
||||
c0.1-0.8,0.1-1.5,0.1-2.3v-0.6v-0.3v-0.1c0-0.2,0-0.1,0-0.2v-0.5v-0.6c0-0.2,0-0.4,0-0.6s0-0.4-0.1-0.6l-0.1-0.6l-0.1-0.6
|
||||
c-0.1-0.8-0.3-1.5-0.4-2.3c-0.7-3-1.9-5.9-3.4-8.4c-1.6-2.6-3.5-4.8-5.7-6.8c-2.2-1.9-4.6-3.5-7.2-4.6c-2.6-1.2-5.2-1.9-7.9-2.2
|
||||
c-1.3-0.2-2.7-0.2-4-0.2h-0.5h-0.1h-0.2h-0.2h-0.5c-0.2,0-0.4,0-0.5,0c-0.7,0.1-1.4,0.2-2,0.3c-2.7,0.5-5.2,1.5-7.4,2.8
|
||||
c-2.2,1.3-4.1,3-5.7,4.9s-2.8,3.9-3.6,6.1c-0.8,2.1-1.3,4.4-1.4,6.5c0,0.5,0,1.1,0,1.6c0,0.1,0,0.3,0,0.4v0.4c0,0.3,0,0.5,0.1,0.8
|
||||
c0.1,1.1,0.3,2.1,0.6,3.1c0.6,2,1.5,3.8,2.7,5.4s2.5,2.8,4,3.8s3,1.7,4.6,2.2c1.6,0.5,3.1,0.7,4.5,0.6c0.2,0,0.4,0,0.5,0
|
||||
c0.1,0,0.2,0,0.3,0s0.2,0,0.3,0c0.2,0,0.3,0,0.5,0h0.1h0.1c0.1,0,0.2,0,0.3,0c0.2,0,0.4-0.1,0.5-0.1c0.2,0,0.3-0.1,0.5-0.1
|
||||
c0.3-0.1,0.7-0.2,1-0.3c0.6-0.2,1.2-0.5,1.8-0.7c0.6-0.3,1.1-0.6,1.5-0.9c0.1-0.1,0.3-0.2,0.4-0.3c0.5-0.4,0.6-1.1,0.2-1.6
|
||||
c-0.4-0.4-1-0.5-1.5-0.3C88,74,87.9,74,87.7,74.1c-0.4,0.2-0.9,0.4-1.3,0.5c-0.5,0.1-1,0.3-1.5,0.4c-0.3,0-0.5,0.1-0.8,0.1
|
||||
c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0-0.4,0s-0.3,0-0.4,0c-0.2,0-0.3,0-0.5,0c0,0-0.1,0,0,0h-0.1h-0.1c-0.1,0-0.1,0-0.2,0
|
||||
s-0.3,0-0.4-0.1c-1.1-0.2-2.3-0.5-3.4-1c-1.1-0.5-2.2-1.2-3.1-2.1c-1-0.9-1.8-1.9-2.5-3.1c-0.7-1.2-1.1-2.5-1.3-3.8
|
||||
c-0.1-0.7-0.2-1.4-0.1-2.1c0-0.2,0-0.4,0-0.6c0,0.1,0,0,0,0v-0.1v-0.1c0-0.1,0-0.2,0-0.3c0-0.4,0.1-0.7,0.2-1.1c0.5-3,2-5.9,4.3-8.1
|
||||
c0.6-0.6,1.2-1.1,1.9-1.5c0.7-0.5,1.4-0.9,2.1-1.2c0.7-0.3,1.5-0.6,2.3-0.8s1.6-0.4,2.4-0.4c0.4,0,0.8-0.1,1.2-0.1
|
||||
c0.1,0,0.2,0,0.3,0h0.3h0.2c0.1,0,0,0,0,0h0.1h0.3c0.9,0.1,1.8,0.2,2.6,0.4c1.7,0.4,3.4,1,5,1.9c3.2,1.8,5.9,4.5,7.5,7.8
|
||||
c0.8,1.6,1.4,3.4,1.7,5.3c0.1,0.5,0.1,0.9,0.2,1.4v0.3V66c0,0.1,0,0.2,0,0.3c0,0.1,0,0.2,0,0.3v0.3v0.3c0,0.2,0,0.6,0,0.8
|
||||
c0,0.5-0.1,1-0.1,1.5c-0.1,0.5-0.1,1-0.2,1.5s-0.2,1-0.3,1.5c-0.2,1-0.6,1.9-0.9,2.9c-0.7,1.9-1.7,3.7-2.9,5.3
|
||||
c-2.4,3.3-5.7,6-9.4,7.7c-1.9,0.8-3.8,1.5-5.8,1.8c-1,0.2-2,0.3-3,0.3H81h-0.2h-0.3H80h-0.3c0.1,0,0,0,0,0h-0.1
|
||||
c-0.5,0-1.1,0-1.6-0.1c-2.2-0.2-4.3-0.6-6.4-1.2c-2.1-0.6-4.1-1.4-6-2.4c-3.8-2-7.2-4.9-9.9-8.2c-1.3-1.7-2.5-3.5-3.5-5.4
|
||||
s-1.7-3.9-2.3-5.9c-0.6-2-0.9-4.1-1-6.2v-0.4v-0.1v-0.1v-0.2V60v-0.1v-0.1v-0.2v-0.5V59l0,0v-0.2c0-0.3,0-0.5,0-0.8
|
||||
c0-1,0.1-2.1,0.3-3.2c0.1-1.1,0.3-2.1,0.5-3.2c0.2-1.1,0.5-2.1,0.8-3.2c0.6-2.1,1.3-4.1,2.2-6c1.8-3.8,4.1-7.2,6.8-9.9
|
||||
c0.7-0.7,1.4-1.3,2.2-1.9c0.3-0.3,1-0.9,1.8-1.4c0.8-0.5,1.6-1,2.5-1.4c0.4-0.2,0.8-0.4,1.3-0.6c0.2-0.1,0.4-0.2,0.7-0.3
|
||||
c0.2-0.1,0.4-0.2,0.7-0.3c0.9-0.4,1.8-0.7,2.7-1c0.2-0.1,0.5-0.1,0.7-0.2c0.2-0.1,0.5-0.1,0.7-0.2c0.5-0.1,0.9-0.2,1.4-0.4
|
||||
c0.2-0.1,0.5-0.1,0.7-0.2c0.2,0,0.5-0.1,0.7-0.1c0.2,0,0.5-0.1,0.7-0.1l0.4-0.1l0.4-0.1c0.2,0,0.5-0.1,0.7-0.1
|
||||
c0.3,0,0.5-0.1,0.8-0.1c0.2,0,0.6-0.1,0.8-0.1c0.2,0,0.3,0,0.5-0.1h0.3h0.2h0.2c0.3,0,0.5,0,0.8-0.1h0.4c0,0,0.1,0,0,0h0.1h0.2
|
||||
c0.2,0,0.5,0,0.7,0c0.9,0,1.8,0,2.7,0c1.8,0.1,3.6,0.3,5.3,0.6c3.4,0.6,6.7,1.7,9.6,3.2c2.9,1.4,5.6,3.2,7.8,5.1
|
||||
c0.1,0.1,0.3,0.2,0.4,0.4c0.1,0.1,0.3,0.2,0.4,0.4c0.3,0.2,0.5,0.5,0.8,0.7c0.3,0.2,0.5,0.5,0.8,0.7c0.2,0.3,0.5,0.5,0.7,0.8
|
||||
c1,1,1.9,2.1,2.7,3.1c1.6,2.1,2.9,4.2,3.9,6.2c0.1,0.1,0.1,0.2,0.2,0.4c0.1,0.1,0.1,0.2,0.2,0.4s0.2,0.5,0.4,0.7
|
||||
c0.1,0.2,0.2,0.5,0.3,0.7c0.1,0.2,0.2,0.5,0.3,0.7c0.4,0.9,0.7,1.8,1,2.7c0.5,1.4,0.8,2.6,1.1,3.6c0.1,0.4,0.5,0.7,0.9,0.7
|
||||
c0.5,0,0.8-0.4,0.8-0.9C123,52.7,123,51.4,122.9,49.9z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.5 KiB |
1
frontend/src/assets/Logos/opencode.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width='234' height='42' viewBox='0 0 234 42' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M18 30H6V18H18V30Z' fill='#CFCECD'/><path d='M18 12H6V30H18V12ZM24 36H0V6H24V36Z' fill='#656363'/><path d='M48 30H36V18H48V30Z' fill='#CFCECD'/><path d='M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z' fill='#656363'/><path d='M84 24V30H66V24H84Z' fill='#CFCECD'/><path d='M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z' fill='#656363'/><path d='M108 36H96V18H108V36Z' fill='#CFCECD'/><path d='M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z' fill='#656363'/><path d='M144 30H126V18H144V30Z' fill='#CFCECD'/><path d='M144 12H126V30H144V36H120V6H144V12Z' fill='#211E1E'/><path d='M168 30H156V18H168V30Z' fill='#CFCECD'/><path d='M168 12H156V30H168V12ZM174 36H150V6H174V36Z' fill='#211E1E'/><path d='M198 30H186V18H198V30Z' fill='#CFCECD'/><path d='M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z' fill='#211E1E'/><path d='M234 24V30H216V24H234Z' fill='#CFCECD'/><path d='M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z' fill='#211E1E'/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,21 +1,21 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<polygon style="fill:#FFD500;" points="382.395,228.568 291.215,228.568 330.762,10.199 129.603,283.43 220.785,283.43
|
||||
181.238,501.799 "/>
|
||||
<g>
|
||||
<path style="fill:#3D3D3D;" d="M181.234,512c-1.355,0-2.726-0.271-4.033-0.833c-4.357-1.878-6.845-6.514-5.999-11.184
|
||||
l37.371-206.353h-78.969c-3.846,0-7.367-2.164-9.103-5.597c-1.735-3.433-1.391-7.55,0.889-10.648L322.548,4.153
|
||||
c2.814-3.822,7.891-5.196,12.25-3.32c4.357,1.878,6.845,6.514,5.999,11.184L303.427,218.37h78.969c3.846,0,7.367,2.164,9.103,5.597
|
||||
c1.735,3.433,1.391,7.55-0.889,10.648L189.451,507.846C187.481,510.523,184.399,512,181.234,512z M149.777,273.231h71.007
|
||||
c3.023,0,5.89,1.341,7.828,3.662c1.938,2.32,2.747,5.38,2.208,8.355l-31.704,175.065l163.105-221.545h-71.007
|
||||
c-3.023,0-5.89-1.341-7.828-3.661c-1.938-2.32-2.747-5.38-2.208-8.355l31.704-175.065L149.777,273.231z"/>
|
||||
<path style="fill:#3D3D3D;" d="M267.666,171.348c-0.604,0-1.215-0.054-1.829-0.165c-5.543-1.004-9.223-6.31-8.22-11.853l0.923-5.1
|
||||
c1.003-5.543,6.323-9.225,11.852-8.219c5.543,1.004,9.223,6.31,8.22,11.853l-0.923,5.1
|
||||
C276.797,167.892,272.503,171.348,267.666,171.348z"/>
|
||||
<path style="fill:#3D3D3D;" d="M255.455,238.77c-0.604,0-1.215-0.054-1.83-0.165c-5.543-1.004-9.222-6.31-8.218-11.853
|
||||
l7.037-38.864c1.004-5.543,6.317-9.225,11.854-8.219c5.543,1.004,9.222,6.31,8.219,11.853l-7.037,38.864
|
||||
C264.587,235.314,260.293,238.77,255.455,238.77z"/>
|
||||
</g>
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 512 512" xml:space="preserve">
|
||||
<polygon style="fill:#FFD500;" points="382.395,228.568 291.215,228.568 330.762,10.199 129.603,283.43 220.785,283.43
|
||||
181.238,501.799 "/>
|
||||
<g>
|
||||
<path style="fill:#3D3D3D;" d="M181.234,512c-1.355,0-2.726-0.271-4.033-0.833c-4.357-1.878-6.845-6.514-5.999-11.184
|
||||
l37.371-206.353h-78.969c-3.846,0-7.367-2.164-9.103-5.597c-1.735-3.433-1.391-7.55,0.889-10.648L322.548,4.153
|
||||
c2.814-3.822,7.891-5.196,12.25-3.32c4.357,1.878,6.845,6.514,5.999,11.184L303.427,218.37h78.969c3.846,0,7.367,2.164,9.103,5.597
|
||||
c1.735,3.433,1.391,7.55-0.889,10.648L189.451,507.846C187.481,510.523,184.399,512,181.234,512z M149.777,273.231h71.007
|
||||
c3.023,0,5.89,1.341,7.828,3.662c1.938,2.32,2.747,5.38,2.208,8.355l-31.704,175.065l163.105-221.545h-71.007
|
||||
c-3.023,0-5.89-1.341-7.828-3.661c-1.938-2.32-2.747-5.38-2.208-8.355l31.704-175.065L149.777,273.231z"/>
|
||||
<path style="fill:#3D3D3D;" d="M267.666,171.348c-0.604,0-1.215-0.054-1.829-0.165c-5.543-1.004-9.223-6.31-8.22-11.853l0.923-5.1
|
||||
c1.003-5.543,6.323-9.225,11.852-8.219c5.543,1.004,9.223,6.31,8.22,11.853l-0.923,5.1
|
||||
C276.797,167.892,272.503,171.348,267.666,171.348z"/>
|
||||
<path style="fill:#3D3D3D;" d="M255.455,238.77c-0.604,0-1.215-0.054-1.83-0.165c-5.543-1.004-9.222-6.31-8.218-11.853
|
||||
l7.037-38.864c1.004-5.543,6.317-9.225,11.854-8.219c5.543,1.004,9.222,6.31,8.219,11.853l-7.037,38.864
|
||||
C264.587,235.314,260.293,238.77,255.455,238.77z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
@@ -74,7 +74,7 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background: radial-gradient(circle, #fff 10%, transparent 0);
|
||||
background: radial-gradient(circle, var(--l1-foreground) 10%, transparent 0);
|
||||
background-size: 12px 12px;
|
||||
opacity: 1;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ function AppLoading(): JSX.Element {
|
||||
try {
|
||||
const theme = get(LOCALSTORAGE.THEME);
|
||||
return theme !== THEME_MODE.LIGHT; // Return true for dark, false for light
|
||||
} catch {
|
||||
} catch (error) {
|
||||
// If localStorage is not available, default to dark theme
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ jest.mock('../../../api/browser/localstorage/get', () => ({
|
||||
}));
|
||||
|
||||
// Access the mocked function
|
||||
const mockGet = (getLocal as unknown) as jest.Mock;
|
||||
const mockGet = getLocal as unknown as jest.Mock;
|
||||
|
||||
describe('AppLoading', () => {
|
||||
const SIGNOZ_TEXT = 'SigNoz';
|
||||
@@ -23,7 +23,7 @@ describe('AppLoading', () => {
|
||||
|
||||
it('should render loading screen with dark theme by default', () => {
|
||||
// Mock localStorage to return dark theme (or undefined for default)
|
||||
mockGet.mockReturnValue();
|
||||
mockGet.mockReturnValue(undefined);
|
||||
|
||||
render(<AppLoading />);
|
||||
|
||||
@@ -40,7 +40,7 @@ describe('AppLoading', () => {
|
||||
|
||||
it('should have proper structure and content', () => {
|
||||
// Mock localStorage to return dark theme
|
||||
mockGet.mockReturnValue();
|
||||
mockGet.mockReturnValue(undefined);
|
||||
|
||||
render(<AppLoading />);
|
||||
|
||||
|
||||
@@ -99,36 +99,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.auth-error-container {
|
||||
.error-content {
|
||||
&__error-code {
|
||||
color: var(--l2-foreground);
|
||||
}
|
||||
|
||||
&__error-message {
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
|
||||
&__message-badge-label-text {
|
||||
color: var(--l2-foreground);
|
||||
}
|
||||
|
||||
&__message-item {
|
||||
color: var(--l1-foreground);
|
||||
|
||||
&::before {
|
||||
background: var(--l3-background);
|
||||
}
|
||||
}
|
||||
|
||||
&__scroll-hint-text {
|
||||
color: var(--l2-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes horizontal-shaking {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
|
||||
@@ -87,23 +87,3 @@
|
||||
background: var(--l3-background);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.auth-footer-content {
|
||||
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.auth-footer-icon {
|
||||
filter: brightness(0) saturate(100%) invert(25%) sepia(8%) saturate(518%)
|
||||
hue-rotate(192deg) brightness(80%) contrast(95%);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.auth-footer-text {
|
||||
color: var(--text-neutral-light-200);
|
||||
}
|
||||
|
||||
.auth-footer-link-icon {
|
||||
color: var(--text-neutral-light-100);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,28 +143,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.bg-dot-pattern {
|
||||
background: radial-gradient(
|
||||
circle,
|
||||
var(--l3-background) 1px,
|
||||
transparent 1px
|
||||
);
|
||||
background-size: 12px 12px;
|
||||
}
|
||||
|
||||
.auth-page-gradient {
|
||||
background: radial-gradient(
|
||||
ellipse at center top,
|
||||
color-mix(in srgb, var(--primary-background) 12%, transparent) 0%,
|
||||
transparent 60%
|
||||
);
|
||||
opacity: 0.8;
|
||||
filter: blur(200px);
|
||||
|
||||
@media (min-width: 768px) {
|
||||
filter: blur(300px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,9 +31,8 @@ export function FilterSelect({
|
||||
onChange,
|
||||
isMultiple,
|
||||
}: SelectOptionConfig): JSX.Element {
|
||||
const { handleSearch, isFetching, options } = useCeleryFilterOptions(
|
||||
filterType,
|
||||
);
|
||||
const { handleSearch, isFetching, options } =
|
||||
useCeleryFilterOptions(filterType);
|
||||
|
||||
const urlQuery = useUrlQuery();
|
||||
const history = useHistory();
|
||||
@@ -51,7 +50,7 @@ export function FilterSelect({
|
||||
// Memoize options to include the typed value if not present
|
||||
const mergedOptions = useMemo(() => {
|
||||
if (
|
||||
searchValue.trim().length > 0 &&
|
||||
!!searchValue.trim().length &&
|
||||
!options.some((opt) => opt.value === searchValue)
|
||||
) {
|
||||
return [{ value: searchValue, label: searchValue }, ...options];
|
||||
|
||||
@@ -280,30 +280,28 @@ function getTableData(data: QueueOverviewResponse['data']): RowData[] {
|
||||
];
|
||||
|
||||
const tableData: RowData[] =
|
||||
data?.map(
|
||||
(row, index: number): RowData => {
|
||||
const rowData: Record<string, string | number> = {};
|
||||
columnOrder.forEach((key) => {
|
||||
const value = row.data[key as keyof typeof row.data];
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
rowData[key] = value;
|
||||
}
|
||||
});
|
||||
Object.entries(row.data).forEach(([key, value]) => {
|
||||
if (
|
||||
!columnOrder.includes(key) &&
|
||||
(typeof value === 'string' || typeof value === 'number')
|
||||
) {
|
||||
rowData[key] = value;
|
||||
}
|
||||
});
|
||||
data?.map((row, index: number): RowData => {
|
||||
const rowData: Record<string, string | number> = {};
|
||||
columnOrder.forEach((key) => {
|
||||
const value = row.data[key as keyof typeof row.data];
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
rowData[key] = value;
|
||||
}
|
||||
});
|
||||
Object.entries(row.data).forEach(([key, value]) => {
|
||||
if (
|
||||
!columnOrder.includes(key) &&
|
||||
(typeof value === 'string' || typeof value === 'number')
|
||||
) {
|
||||
rowData[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
...rowData,
|
||||
key: index,
|
||||
};
|
||||
},
|
||||
) || [];
|
||||
return {
|
||||
...rowData,
|
||||
key: index,
|
||||
};
|
||||
}) || [];
|
||||
|
||||
return tableData;
|
||||
}
|
||||
@@ -480,10 +478,10 @@ export default function CeleryOverviewTable({
|
||||
[searchText],
|
||||
);
|
||||
|
||||
const filteredData = useMemo(() => getFilteredData(tableData), [
|
||||
getFilteredData,
|
||||
tableData,
|
||||
]);
|
||||
const filteredData = useMemo(
|
||||
() => getFilteredData(tableData),
|
||||
[getFilteredData, tableData],
|
||||
);
|
||||
|
||||
const prevTableDataRef = useRef<string>();
|
||||
|
||||
|
||||
@@ -13,9 +13,8 @@ import { useCeleryFilterOptions } from '../useCeleryFilterOptions';
|
||||
import './CeleryTaskConfigOptions.styles.scss';
|
||||
|
||||
function CeleryTaskConfigOptions(): JSX.Element {
|
||||
const { handleSearch, isFetching, options } = useCeleryFilterOptions(
|
||||
'celery.task_name',
|
||||
);
|
||||
const { handleSearch, isFetching, options } =
|
||||
useCeleryFilterOptions('celery.task_name');
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 1px solid var(--l1-border);
|
||||
background: linear-gradient(0deg, transparent 0%, transparent 100%), #0b0c0e;
|
||||
|
||||
.ant-card-body {
|
||||
height: 100%;
|
||||
@@ -238,18 +237,7 @@
|
||||
height: 2px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: var(--l1-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.celery-task-graph-grid-container {
|
||||
.celery-task-graph-worker-count {
|
||||
background: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.configure-option-Info {
|
||||
border: 1px dashed var(--bg-robin-400);
|
||||
background-color: var(--l1-background);
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -469,8 +469,7 @@ export const celeryActiveTasksWidgetData = (
|
||||
{
|
||||
aggregateAttribute: {
|
||||
dataType: DataTypes.Float64,
|
||||
id:
|
||||
'flower_worker_number_of_currently_executing_tasks--float64--Gauge--true',
|
||||
id: 'flower_worker_number_of_currently_executing_tasks--float64--Gauge--true',
|
||||
key: 'flower_worker_number_of_currently_executing_tasks',
|
||||
type: 'Gauge',
|
||||
},
|
||||
|
||||
@@ -127,22 +127,17 @@ function CeleryTaskLatencyGraph({
|
||||
const onGraphClickHandler = useGraphClickHandler(handleSetTimeStamp);
|
||||
|
||||
const onGraphClick = useCallback(
|
||||
(type: string): OnClickPluginOpts['onClick'] => (
|
||||
xValue,
|
||||
yValue,
|
||||
mouseX,
|
||||
mouseY,
|
||||
data,
|
||||
): Promise<void> => {
|
||||
const [firstDataPoint] = Object.entries(data || {});
|
||||
const [entity, value] = firstDataPoint;
|
||||
setEntityData({
|
||||
entity,
|
||||
value,
|
||||
});
|
||||
(type: string): OnClickPluginOpts['onClick'] =>
|
||||
(xValue, yValue, mouseX, mouseY, data): Promise<void> => {
|
||||
const [firstDataPoint] = Object.entries(data || {});
|
||||
const [entity, value] = firstDataPoint;
|
||||
setEntityData({
|
||||
entity,
|
||||
value,
|
||||
});
|
||||
|
||||
return onGraphClickHandler(xValue, yValue, mouseX, mouseY, type);
|
||||
},
|
||||
return onGraphClickHandler(xValue, yValue, mouseX, mouseY, type);
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[handleSetTimeStamp],
|
||||
);
|
||||
|
||||
@@ -92,10 +92,10 @@ function CeleryTaskStateGraphConfig({
|
||||
{isLoading
|
||||
? '-'
|
||||
: isError
|
||||
? '-'
|
||||
: Number.isNaN(values[index])
|
||||
? '-'
|
||||
: Math.round(Number(values[index]))}
|
||||
? '-'
|
||||
: Number.isNaN(values[index])
|
||||
? '-'
|
||||
: Math.round(Number(values[index]))}
|
||||
</div>
|
||||
</div>
|
||||
{tab.key === barState && <div className="celery-task-states__indicator" />}
|
||||
|
||||
@@ -57,7 +57,7 @@ export const useGetValueFromWidget = (
|
||||
return 'Error';
|
||||
}
|
||||
|
||||
const value = Number.parseFloat(
|
||||
const value = parseFloat(
|
||||
query.data?.payload?.data?.newResult?.data?.result?.[0]?.series?.[0]
|
||||
?.values?.[0]?.value || 'NaN',
|
||||
);
|
||||
|
||||
@@ -65,7 +65,7 @@ export function applyCeleryFilterOnWidgetData(
|
||||
items: [...(queryItem.filters?.items || []), ...filters],
|
||||
op: queryItem.filters?.op || 'AND',
|
||||
},
|
||||
}
|
||||
}
|
||||
: queryItem,
|
||||
),
|
||||
},
|
||||
@@ -104,11 +104,11 @@ export const createFiltersFromData = (
|
||||
op: string;
|
||||
value: string;
|
||||
}> => {
|
||||
const excludeKeys = new Set(['A', 'A_without_unit']);
|
||||
const excludeKeys = ['A', 'A_without_unit'];
|
||||
|
||||
return (
|
||||
Object.entries(data)
|
||||
.filter(([key]) => !excludeKeys.has(key))
|
||||
.filter(([key]) => !excludeKeys.includes(key))
|
||||
// eslint-disable-next-line sonarjs/no-identical-functions
|
||||
.map(([key, value]) => ({
|
||||
id: uuidv4(),
|
||||
|
||||
@@ -9,7 +9,7 @@ import { paths } from './CeleryUtils';
|
||||
|
||||
interface UseGetGraphCustomSeriesProps {
|
||||
isDarkMode: boolean;
|
||||
drawStyle?: typeof drawStyles[keyof typeof drawStyles];
|
||||
drawStyle?: (typeof drawStyles)[keyof typeof drawStyles];
|
||||
colorMapping?: Record<string, string>;
|
||||
}
|
||||
|
||||
|
||||
@@ -37,9 +37,8 @@ function ChangelogModal({ changelog, onClose }: Props): JSX.Element {
|
||||
preference.name === USER_PREFERENCES.LAST_SEEN_CHANGELOG_VERSION,
|
||||
)?.value as string;
|
||||
|
||||
const { mutate: updateUserPreferenceMutation } = useMutation(
|
||||
updateUserPreference,
|
||||
);
|
||||
const { mutate: updateUserPreferenceMutation } =
|
||||
useMutation(updateUserPreference);
|
||||
|
||||
useEffect(() => {
|
||||
// Update the seen version
|
||||
@@ -60,11 +59,8 @@ function ChangelogModal({ changelog, onClose }: Props): JSX.Element {
|
||||
|
||||
const checkScroll = useCallback((): void => {
|
||||
if (changelogContentSectionRef.current) {
|
||||
const {
|
||||
scrollHeight,
|
||||
clientHeight,
|
||||
scrollTop,
|
||||
} = changelogContentSectionRef.current;
|
||||
const { scrollHeight, clientHeight, scrollTop } =
|
||||
changelogContentSectionRef.current;
|
||||
const isAtBottom = scrollHeight - clientHeight - scrollTop <= 8;
|
||||
setHasScroll(scrollHeight > clientHeight + 24 && !isAtBottom); // 24px - buffer height to show show more
|
||||
}
|
||||
|
||||
@@ -14,9 +14,8 @@ import { getBaseUrl } from 'utils/basePath';
|
||||
export default function ChatSupportGateway(): JSX.Element {
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] = useState(
|
||||
false,
|
||||
);
|
||||
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const handleBillingOnSuccess = (
|
||||
data: SuccessResponseV2<CheckoutSuccessPayloadProps>,
|
||||
|
||||
@@ -388,7 +388,7 @@ function ClientSideQBSearch(
|
||||
({
|
||||
label: key.key,
|
||||
value: key,
|
||||
} as Option),
|
||||
}) as Option,
|
||||
) || [],
|
||||
);
|
||||
}
|
||||
@@ -440,8 +440,8 @@ function ClientSideQBSearch(
|
||||
const values: Array<string | number | boolean> = [];
|
||||
const { tagValue } = getTagToken(searchValue);
|
||||
if (isArray(tagValue)) {
|
||||
if (!isEmpty(tagValue.at(-1))) {
|
||||
values.push(tagValue.at(-1));
|
||||
if (!isEmpty(tagValue[tagValue.length - 1])) {
|
||||
values.push(tagValue[tagValue.length - 1]);
|
||||
}
|
||||
} else if (!isEmpty(tagValue)) {
|
||||
values.push(tagValue);
|
||||
@@ -462,7 +462,7 @@ function ClientSideQBSearch(
|
||||
({
|
||||
label: checkCommaInValue(String(val)),
|
||||
value: val,
|
||||
} as Option),
|
||||
}) as Option,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
@@ -488,9 +488,9 @@ function ClientSideQBSearch(
|
||||
const computedTagValue =
|
||||
tag.value &&
|
||||
Array.isArray(tag.value) &&
|
||||
tag.value.at(-1) === ''
|
||||
tag.value[tag.value.length - 1] === ''
|
||||
? tag.value?.slice(0, -1)
|
||||
: tag.value ?? '';
|
||||
: (tag.value ?? '');
|
||||
filterTags.items.push({
|
||||
id: tag.id || uuid().slice(0, 8),
|
||||
key: tag.key,
|
||||
@@ -610,7 +610,7 @@ function ClientSideQBSearch(
|
||||
searchValue={searchValue}
|
||||
className={className}
|
||||
rootClassName="query-builder-search client-side-qb-search"
|
||||
disabled={attributeKeys.length === 0}
|
||||
disabled={!attributeKeys.length}
|
||||
style={selectStyle}
|
||||
onSearch={handleSearch}
|
||||
onSelect={handleDropdownSelect}
|
||||
|
||||
@@ -32,6 +32,7 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
SA_QUERY_PARAMS.CREATE_SA,
|
||||
parseAsBoolean.withDefault(false),
|
||||
);
|
||||
const [, setSelectedAccountId] = useQueryState(SA_QUERY_PARAMS.ACCOUNT);
|
||||
|
||||
const { showErrorModal, isErrorModalVisible } = useErrorModal();
|
||||
|
||||
@@ -47,29 +48,28 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
mutate: createServiceAccount,
|
||||
isLoading: isSubmitting,
|
||||
} = useCreateServiceAccount({
|
||||
mutation: {
|
||||
onSuccess: async () => {
|
||||
toast.success('Service account created successfully');
|
||||
reset();
|
||||
await setIsOpen(null);
|
||||
await invalidateListServiceAccounts(queryClient);
|
||||
const { mutate: createServiceAccount, isLoading: isSubmitting } =
|
||||
useCreateServiceAccount({
|
||||
mutation: {
|
||||
onSuccess: async (response) => {
|
||||
toast.success('Service account created successfully');
|
||||
reset();
|
||||
await setIsOpen(null);
|
||||
await invalidateListServiceAccounts(queryClient);
|
||||
await setSelectedAccountId(response.data.id);
|
||||
},
|
||||
onError: (err) => {
|
||||
const errMessage = convertToApiError(
|
||||
err as AxiosError<RenderErrorResponseDTO, unknown> | null,
|
||||
);
|
||||
showErrorModal(errMessage as APIError);
|
||||
},
|
||||
},
|
||||
onError: (err) => {
|
||||
const errMessage = convertToApiError(
|
||||
err as AxiosError<RenderErrorResponseDTO, unknown> | null,
|
||||
);
|
||||
showErrorModal(errMessage as APIError);
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
function handleClose(): void {
|
||||
reset();
|
||||
setIsOpen(null);
|
||||
void setIsOpen(null);
|
||||
}
|
||||
|
||||
function handleCreate(values: FormValues): void {
|
||||
|
||||
@@ -77,11 +77,11 @@
|
||||
width: 280px;
|
||||
|
||||
&::placeholder {
|
||||
color: white;
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
|
||||
&:focus::placeholder {
|
||||
color: rgba($color: #ffffff, $alpha: 0.4);
|
||||
color: rgba($color: var(--l1-foreground), $alpha: 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -113,42 +113,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.time-options-container {
|
||||
.time-options-item {
|
||||
&.active {
|
||||
background-color: rgba($color: #ffffff, $alpha: 0.2);
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: rgba($color: #ffffff, $alpha: 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
background-color: rgba($color: #ffffff, $alpha: 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeSelection-input {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
padding-left: 0px !important;
|
||||
|
||||
input::placeholder {
|
||||
color: var(---bg-ink-300);
|
||||
}
|
||||
|
||||
input:focus::placeholder {
|
||||
color: rgba($color: #000000, $alpha: 0.4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.date-time-popover__footer {
|
||||
border-top: 1px solid var(--l1-border);
|
||||
padding: 8px 14px;
|
||||
@@ -300,34 +264,3 @@
|
||||
background: color-mix(in srgb, var(--bg-robin-200) 8%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.timezone-container {
|
||||
.timezone {
|
||||
background: rgb(179 179 179 / 15%);
|
||||
&__icon {
|
||||
stroke: var(--l1-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-time-picker {
|
||||
.timeSelection-input {
|
||||
&:hover {
|
||||
border-color: var(--l1-border) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timezone-badge {
|
||||
background: rgb(179 179 179 / 15%);
|
||||
}
|
||||
|
||||
.time-input-suffix-icon-badge {
|
||||
background: rgb(179 179 179 / 15%);
|
||||
|
||||
&:hover {
|
||||
background: rgb(179 179 179 / 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,10 +96,8 @@ function CustomTimePicker({
|
||||
maxTime,
|
||||
isModalTimeSelection = false,
|
||||
}: CustomTimePickerProps): JSX.Element {
|
||||
const [
|
||||
selectedTimePlaceholderValue,
|
||||
setSelectedTimePlaceholderValue,
|
||||
] = useState('Select / Enter Time Range');
|
||||
const [selectedTimePlaceholderValue, setSelectedTimePlaceholderValue] =
|
||||
useState('Select / Enter Time Range');
|
||||
|
||||
const [inputValue, setInputValue] = useState('');
|
||||
const [inputStatus, setInputStatus] = useState<CustomTimePickerInputStatus>(
|
||||
@@ -147,7 +145,7 @@ function CustomTimePicker({
|
||||
return `Last ${selectedTime}`;
|
||||
}
|
||||
|
||||
const value = Number.parseInt(match[1], 10);
|
||||
const value = parseInt(match[1], 10);
|
||||
const unit = match[2];
|
||||
|
||||
// Map unit abbreviations to full words
|
||||
@@ -312,7 +310,7 @@ function CustomTimePicker({
|
||||
|
||||
const match = inputValue.match(/^(\d+)([mhdw])$/) as RegExpMatchArray;
|
||||
|
||||
const value = Number.parseInt(match[1], 10);
|
||||
const value = parseInt(match[1], 10);
|
||||
const unit = match[2];
|
||||
|
||||
const currentTime = dayjs();
|
||||
|
||||
@@ -116,9 +116,10 @@ function CustomTimePickerPopoverContent({
|
||||
}: CustomTimePickerPopoverContentProps): JSX.Element {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const isLogsExplorerPage = useMemo(() => pathname === ROUTES.LOGS_EXPLORER, [
|
||||
pathname,
|
||||
]);
|
||||
const isLogsExplorerPage = useMemo(
|
||||
() => pathname === ROUTES.LOGS_EXPLORER,
|
||||
[pathname],
|
||||
);
|
||||
|
||||
const url = new URLSearchParams(window.location.search);
|
||||
|
||||
@@ -154,8 +155,8 @@ function CustomTimePickerPopoverContent({
|
||||
if (!customDateTimeVisible) {
|
||||
const customTimeRanges = getCustomTimeRanges();
|
||||
|
||||
const formattedCustomTimeRanges: RecentlyUsedDateTimeRange[] = customTimeRanges.map(
|
||||
(range) => ({
|
||||
const formattedCustomTimeRanges: RecentlyUsedDateTimeRange[] =
|
||||
customTimeRanges.map((range) => ({
|
||||
label: `${dayjs(range.from)
|
||||
.tz(timezone.value)
|
||||
.format(DATE_TIME_FORMATS.DD_MMM_YYYY_HH_MM_SS)} - ${dayjs(range.to)
|
||||
@@ -165,8 +166,7 @@ function CustomTimePickerPopoverContent({
|
||||
to: range.to,
|
||||
value: range.timestamp,
|
||||
timestamp: range.timestamp,
|
||||
}),
|
||||
);
|
||||
}));
|
||||
|
||||
setRecentlyUsedTimeRanges(formattedCustomTimeRanges);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ interface RangePickerModalProps {
|
||||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
onCustomDateHandler: (
|
||||
dateTimeRange: DateTimeRangeType,
|
||||
lexicalContext?: LexicalContext ,
|
||||
lexicalContext?: LexicalContext | undefined,
|
||||
) => void;
|
||||
selectedTime: string;
|
||||
onTimeChange?: (
|
||||
|
||||
@@ -129,20 +129,3 @@ $item-spacing: 8px;
|
||||
width: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.timezone-picker {
|
||||
&__search {
|
||||
.search-icon {
|
||||
stroke: var(--l1-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
.timezone-name-wrapper {
|
||||
&__selected-icon {
|
||||
.check-icon {
|
||||
stroke: var(--l1-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ const TIMEZONE_TYPES = {
|
||||
STANDARD: 'STANDARD',
|
||||
} as const;
|
||||
|
||||
type TimezoneType = typeof TIMEZONE_TYPES[keyof typeof TIMEZONE_TYPES];
|
||||
type TimezoneType = (typeof TIMEZONE_TYPES)[keyof typeof TIMEZONE_TYPES];
|
||||
|
||||
export const UTC_TIMEZONE: Timezone = {
|
||||
name: 'Coordinated Universal Time — UTC, GMT',
|
||||
@@ -82,7 +82,7 @@ const createTimezoneEntry = (
|
||||
name: displayName,
|
||||
value,
|
||||
offset,
|
||||
searchIndex: offset.replaceAll(/ /g, ''),
|
||||
searchIndex: offset.replace(/ /g, ''),
|
||||
...(hasDivider && { hasDivider }),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ import '@testing-library/jest-dom';
|
||||
import { DownloadFormats, DownloadRowCounts } from './constants';
|
||||
import DownloadOptionsMenu from './DownloadOptionsMenu';
|
||||
|
||||
const mockDownloadExportData = jest.fn().mockResolvedValue();
|
||||
const mockDownloadExportData = jest.fn().mockResolvedValue(undefined);
|
||||
jest.mock('api/v1/download/downloadExportData', () => ({
|
||||
downloadExportData: (...args: any[]): any => mockDownloadExportData(...args),
|
||||
default: (...args: any[]): any => mockDownloadExportData(...args),
|
||||
@@ -94,7 +94,7 @@ describe.each([
|
||||
const testId = `periscope-btn-download-${dataSource}`;
|
||||
|
||||
beforeEach(() => {
|
||||
mockDownloadExportData.mockReset().mockResolvedValue();
|
||||
mockDownloadExportData.mockReset().mockResolvedValue(undefined);
|
||||
(message.success as jest.Mock).mockReset();
|
||||
(message.error as jest.Mock).mockReset();
|
||||
mockUseQueryBuilder.mockReturnValue({
|
||||
@@ -213,7 +213,7 @@ describe.each([
|
||||
const callArgs = mockDownloadExportData.mock.calls[0][0];
|
||||
const query = callArgs.body.compositeQuery.queries[0];
|
||||
expect(query.spec.groupBy).toBeUndefined();
|
||||
expect(query.spec.having).toStrictEqual({ expression: '' });
|
||||
expect(query.spec.having).toEqual({ expression: '' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -238,7 +238,7 @@ describe.each([
|
||||
expect(mockDownloadExportData).toHaveBeenCalledTimes(1);
|
||||
const callArgs = mockDownloadExportData.mock.calls[0][0];
|
||||
const query = callArgs.body.compositeQuery.queries[0];
|
||||
expect(query.spec.selectFields).toStrictEqual([
|
||||
expect(query.spec.selectFields).toEqual([
|
||||
expect.objectContaining({
|
||||
name: 'http.status',
|
||||
fieldDataType: 'int64',
|
||||
@@ -322,7 +322,7 @@ describe('DownloadOptionsMenu for traces with queryTraceOperator', () => {
|
||||
const testId = `periscope-btn-download-${dataSource}`;
|
||||
|
||||
beforeEach(() => {
|
||||
mockDownloadExportData.mockReset().mockResolvedValue();
|
||||
mockDownloadExportData.mockReset().mockResolvedValue(undefined);
|
||||
(message.success as jest.Mock).mockReset();
|
||||
});
|
||||
|
||||
|
||||
@@ -41,8 +41,7 @@ function DraggableTableRow({
|
||||
);
|
||||
}
|
||||
|
||||
interface DraggableTableRowProps
|
||||
extends React.HTMLAttributes<HTMLTableRowElement> {
|
||||
interface DraggableTableRowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
||||
index: number;
|
||||
moveRow: (dragIndex: number, hoverIndex: number) => void;
|
||||
}
|
||||
|
||||
@@ -6,39 +6,39 @@ jest.mock('react-dnd', () => ({
|
||||
}));
|
||||
|
||||
describe('Utils testing of DraggableTableRow component', () => {
|
||||
it('Should dropHandler return true', () => {
|
||||
test('Should dropHandler return true', () => {
|
||||
const monitor = {
|
||||
isOver: jest.fn().mockReturnValueOnce(true),
|
||||
} as never;
|
||||
const dropDataTruthy = dropHandler(monitor);
|
||||
|
||||
expect(dropDataTruthy).toStrictEqual({ isOver: true });
|
||||
expect(dropDataTruthy).toEqual({ isOver: true });
|
||||
});
|
||||
|
||||
it('Should dropHandler return false', () => {
|
||||
test('Should dropHandler return false', () => {
|
||||
const monitor = {
|
||||
isOver: jest.fn().mockReturnValueOnce(false),
|
||||
} as never;
|
||||
const dropDataFalsy = dropHandler(monitor);
|
||||
|
||||
expect(dropDataFalsy).toStrictEqual({ isOver: false });
|
||||
expect(dropDataFalsy).toEqual({ isOver: false });
|
||||
});
|
||||
|
||||
it('Should dragHandler return true', () => {
|
||||
test('Should dragHandler return true', () => {
|
||||
const monitor = {
|
||||
isDragging: jest.fn().mockReturnValueOnce(true),
|
||||
} as never;
|
||||
const dragDataTruthy = dragHandler(monitor);
|
||||
|
||||
expect(dragDataTruthy).toStrictEqual({ isDragging: true });
|
||||
expect(dragDataTruthy).toEqual({ isDragging: true });
|
||||
});
|
||||
|
||||
it('Should dragHandler return false', () => {
|
||||
test('Should dragHandler return false', () => {
|
||||
const monitor = {
|
||||
isDragging: jest.fn().mockReturnValueOnce(false),
|
||||
} as never;
|
||||
const dragDataFalsy = dragHandler(monitor);
|
||||
|
||||
expect(dragDataFalsy).toStrictEqual({ isDragging: false });
|
||||
expect(dragDataFalsy).toEqual({ isDragging: false });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,9 +6,9 @@ export function dropHandler(monitor: DropTargetMonitor): { isOver: boolean } {
|
||||
};
|
||||
}
|
||||
|
||||
export function dragHandler(
|
||||
monitor: DragSourceMonitor,
|
||||
): { isDragging: boolean } {
|
||||
export function dragHandler(monitor: DragSourceMonitor): {
|
||||
isDragging: boolean;
|
||||
} {
|
||||
return {
|
||||
isDragging: monitor.isDragging(),
|
||||
};
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
.dropdown-button {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dropdown-button--dark {
|
||||
color: #000;
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import { EllipsisOutlined } from '@ant-design/icons';
|
||||
import { Button, Dropdown, MenuProps } from 'antd';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
|
||||
import './DropDown.styles.scss';
|
||||
|
||||
@@ -12,8 +11,6 @@ function DropDown({
|
||||
element: JSX.Element[];
|
||||
onDropDownItemClick?: MenuProps['onClick'];
|
||||
}): JSX.Element {
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const items: MenuProps['items'] = element.map(
|
||||
(e: JSX.Element, index: number) => ({
|
||||
label: e,
|
||||
@@ -35,7 +32,7 @@ function DropDown({
|
||||
>
|
||||
<Button
|
||||
type="link"
|
||||
className={!isDarkMode ? 'dropdown-button--dark' : 'dropdown-button'}
|
||||
className={`dropdown-button`}
|
||||
onClick={(e): void => {
|
||||
e.preventDefault();
|
||||
setDdOpen(true);
|
||||
|
||||
@@ -158,10 +158,8 @@ function EditMemberDrawer({
|
||||
new Date(String(existingToken.expiresAt)) < new Date();
|
||||
|
||||
// Create/regenerate token mutation
|
||||
const {
|
||||
mutateAsync: createTokenMutation,
|
||||
isLoading: isGeneratingLink,
|
||||
} = useCreateResetPasswordToken();
|
||||
const { mutateAsync: createTokenMutation, isLoading: isGeneratingLink } =
|
||||
useCreateResetPasswordToken();
|
||||
|
||||
const fetchedDisplayName =
|
||||
fetchedUser?.data?.displayName ?? member?.name ?? '';
|
||||
@@ -221,22 +219,20 @@ function EditMemberDrawer({
|
||||
});
|
||||
|
||||
const makeRoleRetry = useCallback(
|
||||
(
|
||||
context: string,
|
||||
rawRetry: () => Promise<void>,
|
||||
) => async (): Promise<void> => {
|
||||
try {
|
||||
await rawRetry();
|
||||
setSaveErrors((prev) => prev.filter((e) => e.context !== context));
|
||||
refetchUser();
|
||||
} catch (err) {
|
||||
setSaveErrors((prev) =>
|
||||
prev.map((e) =>
|
||||
e.context === context ? { ...e, apiError: toSaveApiError(err) } : e,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
(context: string, rawRetry: () => Promise<void>) =>
|
||||
async (): Promise<void> => {
|
||||
try {
|
||||
await rawRetry();
|
||||
setSaveErrors((prev) => prev.filter((e) => e.context !== context));
|
||||
refetchUser();
|
||||
} catch (err) {
|
||||
setSaveErrors((prev) =>
|
||||
prev.map((e) =>
|
||||
e.context === context ? { ...e, apiError: toSaveApiError(err) } : e,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
[refetchUser],
|
||||
);
|
||||
|
||||
@@ -280,7 +276,7 @@ function EditMemberDrawer({
|
||||
: updateUser({
|
||||
pathParams: { id: member.id },
|
||||
data: { displayName: localDisplayName },
|
||||
})
|
||||
})
|
||||
: Promise.resolve();
|
||||
|
||||
const [nameResult, rolesResult] = await Promise.allSettled([
|
||||
@@ -389,7 +385,7 @@ function EditMemberDrawer({
|
||||
? formatTimezoneAdjustedTimestamp(
|
||||
String(response.data.expiresAt),
|
||||
DATE_TIME_FORMATS.DASH_DATETIME,
|
||||
)
|
||||
)
|
||||
: null,
|
||||
);
|
||||
setHasCopiedResetLink(false);
|
||||
@@ -498,8 +494,8 @@ function EditMemberDrawer({
|
||||
isRootUser
|
||||
? ROOT_USER_TOOLTIP
|
||||
: isDeleted
|
||||
? undefined
|
||||
: 'You cannot modify your own role'
|
||||
? undefined
|
||||
: 'You cannot modify your own role'
|
||||
}
|
||||
>
|
||||
<div className="edit-member-drawer__input-wrapper edit-member-drawer__input-wrapper--disabled">
|
||||
@@ -622,13 +618,13 @@ function EditMemberDrawer({
|
||||
{isGeneratingLink
|
||||
? 'Generating...'
|
||||
: isInvited
|
||||
? getInviteButtonLabel(
|
||||
isLoadingTokenStatus,
|
||||
existingToken,
|
||||
isTokenExpired,
|
||||
tokenNotFound,
|
||||
)
|
||||
: 'Generate Password Reset Link'}
|
||||
? getInviteButtonLabel(
|
||||
isLoadingTokenStatus,
|
||||
existingToken,
|
||||
isTokenExpired,
|
||||
tokenNotFound,
|
||||
)
|
||||
: 'Generate Password Reset Link'}
|
||||
</Button>
|
||||
</span>
|
||||
</Tooltip>
|
||||
|
||||
@@ -361,10 +361,12 @@ describe('EditMemberDrawer', () => {
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /delete member/i }));
|
||||
|
||||
await expect(screen.findByText(/are you sure you want to delete/i)).resolves.toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText(/are you sure you want to delete/i),
|
||||
).toBeInTheDocument();
|
||||
|
||||
const confirmBtns = screen.getAllByRole('button', { name: /delete member/i });
|
||||
await user.click(confirmBtns.at(-1));
|
||||
await user.click(confirmBtns[confirmBtns.length - 1]);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockDeleteMutate).toHaveBeenCalledWith({
|
||||
@@ -439,10 +441,12 @@ describe('EditMemberDrawer', () => {
|
||||
|
||||
await user.click(screen.getByRole('button', { name: /revoke invite/i }));
|
||||
|
||||
await expect(screen.findByText(/Are you sure you want to revoke the invite/i)).resolves.toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText(/Are you sure you want to revoke the invite/i),
|
||||
).toBeInTheDocument();
|
||||
|
||||
const confirmBtns = screen.getAllByRole('button', { name: /revoke invite/i });
|
||||
await user.click(confirmBtns.at(-1));
|
||||
await user.click(confirmBtns[confirmBtns.length - 1]);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockDeleteMutate).toHaveBeenCalledWith({
|
||||
@@ -549,7 +553,7 @@ describe('EditMemberDrawer', () => {
|
||||
const confirmBtns = screen.getAllByRole('button', {
|
||||
name: /delete member/i,
|
||||
});
|
||||
await user.click(confirmBtns.at(-1));
|
||||
await user.click(confirmBtns[confirmBtns.length - 1]);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(showErrorModal).toHaveBeenCalledWith(
|
||||
@@ -580,7 +584,7 @@ describe('EditMemberDrawer', () => {
|
||||
const confirmBtns = screen.getAllByRole('button', {
|
||||
name: /revoke invite/i,
|
||||
});
|
||||
await user.click(confirmBtns.at(-1));
|
||||
await user.click(confirmBtns[confirmBtns.length - 1]);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(showErrorModal).toHaveBeenCalledWith(
|
||||
|
||||
@@ -180,7 +180,7 @@ it('should close the modal when the onCancel event is triggered', async () => {
|
||||
|
||||
await waitFor(() => {
|
||||
// check if the modal is not visible
|
||||
const modal = document.querySelectorAll('.ant-modal');
|
||||
const modal = document.getElementsByClassName('ant-modal');
|
||||
const style = window.getComputedStyle(modal[0]);
|
||||
expect(style.display).toBe('none');
|
||||
});
|
||||
|
||||
@@ -168,9 +168,12 @@
|
||||
gap: 3px;
|
||||
background: var(--l1-border);
|
||||
border-radius: 20px;
|
||||
box-shadow: 0px 103px 12px 0px rgba(0, 0, 0, 0.01),
|
||||
0px 66px 18px 0px rgba(0, 0, 0, 0.01), 0px 37px 22px 0px rgba(0, 0, 0, 0.03),
|
||||
0px 17px 17px 0px rgba(0, 0, 0, 0.04), 0px 4px 9px 0px rgba(0, 0, 0, 0.04);
|
||||
box-shadow:
|
||||
0px 103px 12px 0px rgba(0, 0, 0, 0.01),
|
||||
0px 66px 18px 0px rgba(0, 0, 0, 0.01),
|
||||
0px 37px 22px 0px rgba(0, 0, 0, 0.03),
|
||||
0px 17px 17px 0px rgba(0, 0, 0, 0.04),
|
||||
0px 4px 9px 0px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
&__scroll-hint-text {
|
||||
|
||||
@@ -25,15 +25,14 @@ function ErrorContent({ error, icon }: ErrorContentProps): JSX.Element {
|
||||
errors: errorMessages,
|
||||
code: errorCode,
|
||||
message: errorMessage,
|
||||
} =
|
||||
error && 'error' in error
|
||||
? error?.error?.error || {}
|
||||
: {
|
||||
url: undefined,
|
||||
errors: [],
|
||||
code: error.code || 500,
|
||||
message: error.message || 'Something went wrong',
|
||||
};
|
||||
} = error && 'error' in error
|
||||
? error?.error?.error || {}
|
||||
: {
|
||||
url: undefined,
|
||||
errors: [],
|
||||
code: error.code || 500,
|
||||
message: error.message || 'Something went wrong',
|
||||
};
|
||||
return (
|
||||
<section className="error-content">
|
||||
{/* Summary Header */}
|
||||
|
||||
@@ -23,11 +23,8 @@ function MenuItemGenerator({
|
||||
refetchAllView,
|
||||
sourcePage,
|
||||
}: MenuItemLabelGeneratorProps): JSX.Element {
|
||||
const {
|
||||
panelType,
|
||||
redirectWithQueryBuilderData,
|
||||
updateAllQueriesOperators,
|
||||
} = useQueryBuilder();
|
||||
const { panelType, redirectWithQueryBuilderData, updateAllQueriesOperators } =
|
||||
useQueryBuilder();
|
||||
const { handleExplorerTabChange } = useHandleExplorerTabChange();
|
||||
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
@@ -17,11 +17,8 @@ function SaveViewWithName({
|
||||
}: SaveViewWithNameProps): JSX.Element {
|
||||
const [form] = Form.useForm<SaveViewFormProps>();
|
||||
const { t } = useTranslation(['explorer']);
|
||||
const {
|
||||
currentQuery,
|
||||
panelType,
|
||||
redirectWithQueryBuilderData,
|
||||
} = useQueryBuilder();
|
||||
const { currentQuery, panelType, redirectWithQueryBuilderData } =
|
||||
useQueryBuilder();
|
||||
const { notifications } = useNotifications();
|
||||
const compositeQuery = mapCompositeQueryFromQuery(currentQuery, panelType);
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { QueryParams } from 'constants/query';
|
||||
|
||||
export const ExploreHeaderToolTip = {
|
||||
url:
|
||||
'https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=new-query-builder',
|
||||
url: 'https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=new-query-builder',
|
||||
text: 'More details on how to use query builder',
|
||||
};
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export const getViewDetailsUsingViewKey: GetViewDetailsUsingViewKey = (
|
||||
const query = mapQueryDataFromApi(compositeQuery);
|
||||
return { query, name, id, panelType: compositeQuery.panelType, extraData };
|
||||
}
|
||||
return;
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const omitIdFromQuery = (query: Query | null): any => ({
|
||||
@@ -198,7 +198,7 @@ export const deleteViewHandler = ({
|
||||
|
||||
export const trimViewName = (viewName: string): string => {
|
||||
if (viewName.length > 20) {
|
||||
return `${viewName.slice(0, 20)}...`;
|
||||
return `${viewName.substring(0, 20)}...`;
|
||||
}
|
||||
return viewName;
|
||||
};
|
||||
|
||||
@@ -55,9 +55,8 @@ function createMousedownHandler(
|
||||
startDragPositionX = right;
|
||||
}
|
||||
|
||||
const startValuePositionX = chart.scales.x.getValueForPixel(
|
||||
startDragPositionX,
|
||||
);
|
||||
const startValuePositionX =
|
||||
chart.scales.x.getValueForPixel(startDragPositionX);
|
||||
|
||||
dragData.onDragStart(startDragPositionX, startValuePositionX);
|
||||
};
|
||||
@@ -109,9 +108,8 @@ function createMouseupHandler(
|
||||
endRelativePostionX = right;
|
||||
}
|
||||
|
||||
const endValuePositionX = chart.scales.x.getValueForPixel(
|
||||
endRelativePostionX,
|
||||
);
|
||||
const endValuePositionX =
|
||||
chart.scales.x.getValueForPixel(endRelativePostionX);
|
||||
|
||||
dragData.onDragEnd(endRelativePostionX, endValuePositionX);
|
||||
|
||||
|
||||
@@ -12,11 +12,12 @@ export type IntersectionCursorPluginOptions = {
|
||||
gapSize?: number;
|
||||
};
|
||||
|
||||
export const defaultIntersectionCursorPluginOptions: Required<IntersectionCursorPluginOptions> = {
|
||||
color: 'white',
|
||||
dashSize: 3,
|
||||
gapSize: 3,
|
||||
};
|
||||
export const defaultIntersectionCursorPluginOptions: Required<IntersectionCursorPluginOptions> =
|
||||
{
|
||||
color: 'white',
|
||||
dashSize: 3,
|
||||
gapSize: 3,
|
||||
};
|
||||
|
||||
export function createIntersectionCursorPluginOptions(
|
||||
isEnabled: boolean,
|
||||
|
||||
@@ -9,7 +9,7 @@ const getOrCreateLegendList = (
|
||||
id: string,
|
||||
isLonger: boolean,
|
||||
): HTMLUListElement => {
|
||||
const legendContainer = document.querySelector(`#${id}`);
|
||||
const legendContainer = document.getElementById(id);
|
||||
let listContainer = legendContainer?.querySelector('ul');
|
||||
|
||||
if (!listContainer) {
|
||||
@@ -26,7 +26,7 @@ const getOrCreateLegendList = (
|
||||
listContainer.style.flexWrap = 'wrap';
|
||||
listContainer.style.justifyContent = 'center';
|
||||
listContainer.style.fontSize = '0.75rem';
|
||||
legendContainer?.append(listContainer);
|
||||
legendContainer?.appendChild(listContainer);
|
||||
}
|
||||
|
||||
return listContainer;
|
||||
@@ -52,7 +52,7 @@ export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => ({
|
||||
])
|
||||
? get(chart, ['options', 'plugins', 'legend', 'labels', 'generateLabels'])(
|
||||
chart,
|
||||
)
|
||||
)
|
||||
: null;
|
||||
|
||||
items?.forEach((item: Record<any, any>, index: number) => {
|
||||
@@ -64,7 +64,7 @@ export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => ({
|
||||
// li.style.marginTop = '5px';
|
||||
|
||||
li.onclick = (): void => {
|
||||
// @ts-expect-error
|
||||
// @ts-ignore
|
||||
const { type } = chart.config;
|
||||
if (type === 'pie' || type === 'doughnut') {
|
||||
// Pie and doughnut charts only have a single dataset and visibility is per item
|
||||
@@ -101,11 +101,11 @@ export const legend = (id: string, isLonger: boolean): Plugin<ChartType> => ({
|
||||
textContainer.style.textDecoration = item.hidden ? 'line-through' : '';
|
||||
|
||||
const text = document.createTextNode(item.text);
|
||||
textContainer.append(text);
|
||||
textContainer.appendChild(text);
|
||||
|
||||
li.append(boxSpan);
|
||||
li.append(textContainer);
|
||||
ul.append(li);
|
||||
li.appendChild(boxSpan);
|
||||
li.appendChild(textContainer);
|
||||
ul.appendChild(li);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('xAxisConfig for Chart', () => {
|
||||
const start = dayjs();
|
||||
const end = start.add(10, 'millisecond');
|
||||
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toStrictEqual(
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||
TIME_UNITS.millisecond,
|
||||
);
|
||||
}
|
||||
@@ -17,7 +17,7 @@ describe('xAxisConfig for Chart', () => {
|
||||
const start = dayjs();
|
||||
const end = start.add(10, 'second');
|
||||
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toStrictEqual(
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||
TIME_UNITS.second,
|
||||
);
|
||||
}
|
||||
@@ -25,7 +25,7 @@ describe('xAxisConfig for Chart', () => {
|
||||
const start = dayjs();
|
||||
const end = start.add(10, 'minute');
|
||||
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toStrictEqual(
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||
TIME_UNITS.minute,
|
||||
);
|
||||
}
|
||||
@@ -33,7 +33,7 @@ describe('xAxisConfig for Chart', () => {
|
||||
const start = dayjs();
|
||||
const end = start.add(10, 'hour');
|
||||
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toStrictEqual(
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||
TIME_UNITS.hour,
|
||||
);
|
||||
}
|
||||
@@ -41,7 +41,7 @@ describe('xAxisConfig for Chart', () => {
|
||||
const start = dayjs();
|
||||
const end = start.add(10, 'day');
|
||||
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toStrictEqual(
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||
TIME_UNITS.day,
|
||||
);
|
||||
}
|
||||
@@ -49,7 +49,7 @@ describe('xAxisConfig for Chart', () => {
|
||||
const start = dayjs();
|
||||
const end = start.add(10, 'week');
|
||||
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toStrictEqual(
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||
TIME_UNITS.week,
|
||||
);
|
||||
}
|
||||
@@ -57,7 +57,7 @@ describe('xAxisConfig for Chart', () => {
|
||||
const start = dayjs();
|
||||
const end = start.add(10, 'month');
|
||||
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toStrictEqual(
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||
TIME_UNITS.month,
|
||||
);
|
||||
}
|
||||
@@ -65,7 +65,7 @@ describe('xAxisConfig for Chart', () => {
|
||||
const start = dayjs();
|
||||
const end = start.add(10, 'year');
|
||||
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toStrictEqual(
|
||||
expect(convertTimeRange(start.valueOf(), end.valueOf()).unitName).toEqual(
|
||||
TIME_UNITS.year,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ const testFullPrecisionGetYAxisFormattedValue = (
|
||||
): string => getYAxisFormattedValue(value, format, PrecisionOptionsEnum.FULL);
|
||||
|
||||
describe('getYAxisFormattedValue - none (full precision legacy assertions)', () => {
|
||||
it('large integers and decimals', () => {
|
||||
test('large integers and decimals', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('250034', 'none')).toBe(
|
||||
'250034',
|
||||
);
|
||||
@@ -22,7 +22,7 @@ describe('getYAxisFormattedValue - none (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('preserves leading zeros after decimal until first non-zero', () => {
|
||||
test('preserves leading zeros after decimal until first non-zero', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1.0000234', 'none')).toBe(
|
||||
'1.0000234',
|
||||
);
|
||||
@@ -31,7 +31,7 @@ describe('getYAxisFormattedValue - none (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('trims to three significant decimals and removes trailing zeros', () => {
|
||||
test('trims to three significant decimals and removes trailing zeros', () => {
|
||||
expect(
|
||||
testFullPrecisionGetYAxisFormattedValue('0.000000250034', 'none'),
|
||||
).toBe('0.000000250034');
|
||||
@@ -55,7 +55,7 @@ describe('getYAxisFormattedValue - none (full precision legacy assertions)', ()
|
||||
).toBe('0.00000025');
|
||||
});
|
||||
|
||||
it('whole numbers normalize', () => {
|
||||
test('whole numbers normalize', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1000', 'none')).toBe('1000');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('99.5458', 'none')).toBe(
|
||||
'99.5458',
|
||||
@@ -68,7 +68,7 @@ describe('getYAxisFormattedValue - none (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('strip redundant decimal zeros', () => {
|
||||
test('strip redundant decimal zeros', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1000.000', 'none')).toBe(
|
||||
'1000',
|
||||
);
|
||||
@@ -78,7 +78,7 @@ describe('getYAxisFormattedValue - none (full precision legacy assertions)', ()
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1.000', 'none')).toBe('1');
|
||||
});
|
||||
|
||||
it('edge values', () => {
|
||||
test('edge values', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('0', 'none')).toBe('0');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('-0', 'none')).toBe('0');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('Infinity', 'none')).toBe('∞');
|
||||
@@ -92,7 +92,7 @@ describe('getYAxisFormattedValue - none (full precision legacy assertions)', ()
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('abc123', 'none')).toBe('NaN');
|
||||
});
|
||||
|
||||
it('small decimals keep precision as-is', () => {
|
||||
test('small decimals keep precision as-is', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('0.0001', 'none')).toBe(
|
||||
'0.0001',
|
||||
);
|
||||
@@ -104,7 +104,7 @@ describe('getYAxisFormattedValue - none (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('simple decimals preserved', () => {
|
||||
test('simple decimals preserved', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('0.1', 'none')).toBe('0.1');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('0.2', 'none')).toBe('0.2');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('0.3', 'none')).toBe('0.3');
|
||||
@@ -115,7 +115,7 @@ describe('getYAxisFormattedValue - none (full precision legacy assertions)', ()
|
||||
});
|
||||
|
||||
describe('getYAxisFormattedValue - units (full precision legacy assertions)', () => {
|
||||
it('ms', () => {
|
||||
test('ms', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1500', 'ms')).toBe('1.5 s');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('500', 'ms')).toBe('500 ms');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('60000', 'ms')).toBe('1 min');
|
||||
@@ -127,19 +127,19 @@ describe('getYAxisFormattedValue - units (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('s', () => {
|
||||
test('s', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('90', 's')).toBe('1.5 mins');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('30', 's')).toBe('30 s');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('3600', 's')).toBe('1 hour');
|
||||
});
|
||||
|
||||
it('m', () => {
|
||||
test('m', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('90', 'm')).toBe('1.5 hours');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('30', 'm')).toBe('30 min');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1440', 'm')).toBe('1 day');
|
||||
});
|
||||
|
||||
it('bytes', () => {
|
||||
test('bytes', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1024', 'bytes')).toBe(
|
||||
'1 KiB',
|
||||
);
|
||||
@@ -149,7 +149,7 @@ describe('getYAxisFormattedValue - units (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('mbytes', () => {
|
||||
test('mbytes', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1024', 'mbytes')).toBe(
|
||||
'1 GiB',
|
||||
);
|
||||
@@ -161,7 +161,7 @@ describe('getYAxisFormattedValue - units (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('kbytes', () => {
|
||||
test('kbytes', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1024', 'kbytes')).toBe(
|
||||
'1 MiB',
|
||||
);
|
||||
@@ -173,7 +173,7 @@ describe('getYAxisFormattedValue - units (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('short', () => {
|
||||
test('short', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1000', 'short')).toBe('1 K');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('1500', 'short')).toBe(
|
||||
'1.5 K',
|
||||
@@ -201,7 +201,7 @@ describe('getYAxisFormattedValue - units (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('percent', () => {
|
||||
test('percent', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('0.15', 'percent')).toBe(
|
||||
'0.15%',
|
||||
);
|
||||
@@ -235,7 +235,7 @@ describe('getYAxisFormattedValue - units (full precision legacy assertions)', ()
|
||||
).toBe('1.005555555595959%');
|
||||
});
|
||||
|
||||
it('ratio', () => {
|
||||
test('ratio', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('0.5', 'ratio')).toBe(
|
||||
'0.5 ratio',
|
||||
);
|
||||
@@ -247,7 +247,7 @@ describe('getYAxisFormattedValue - units (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('temperature units', () => {
|
||||
test('temperature units', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('25', 'celsius')).toBe(
|
||||
'25 °C',
|
||||
);
|
||||
@@ -267,13 +267,13 @@ describe('getYAxisFormattedValue - units (full precision legacy assertions)', ()
|
||||
);
|
||||
});
|
||||
|
||||
it('ms edge cases', () => {
|
||||
test('ms edge cases', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('0', 'ms')).toBe('0 ms');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('-1500', 'ms')).toBe('-1.5 s');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('Infinity', 'ms')).toBe('∞');
|
||||
});
|
||||
|
||||
it('bytes edge cases', () => {
|
||||
test('bytes edge cases', () => {
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('0', 'bytes')).toBe('0 B');
|
||||
expect(testFullPrecisionGetYAxisFormattedValue('-1024', 'bytes')).toBe(
|
||||
'-1 KiB',
|
||||
@@ -282,7 +282,7 @@ describe('getYAxisFormattedValue - units (full precision legacy assertions)', ()
|
||||
});
|
||||
|
||||
describe('getYAxisFormattedValue - precision option tests', () => {
|
||||
it('precision 0 drops decimal part', () => {
|
||||
test('precision 0 drops decimal part', () => {
|
||||
expect(getYAxisFormattedValue('1.2345', 'none', 0)).toBe('1');
|
||||
expect(getYAxisFormattedValue('0.9999', 'none', 0)).toBe('0');
|
||||
expect(getYAxisFormattedValue('12345.6789', 'none', 0)).toBe('12345');
|
||||
@@ -294,7 +294,7 @@ describe('getYAxisFormattedValue - precision option tests', () => {
|
||||
// with unit
|
||||
expect(getYAxisFormattedValue('4353.81', 'ms', 0)).toBe('4 s');
|
||||
});
|
||||
it('precision 1,2,3,4 decimals', () => {
|
||||
test('precision 1,2,3,4 decimals', () => {
|
||||
expect(getYAxisFormattedValue('1.2345', 'none', 1)).toBe('1.2');
|
||||
expect(getYAxisFormattedValue('1.2345', 'none', 2)).toBe('1.23');
|
||||
expect(getYAxisFormattedValue('1.2345', 'none', 3)).toBe('1.234');
|
||||
@@ -345,7 +345,7 @@ describe('getYAxisFormattedValue - precision option tests', () => {
|
||||
expect(getYAxisFormattedValue('0.123456', 'percent', 4)).toBe('0.1235%'); // approximation
|
||||
});
|
||||
|
||||
it('precision full uses up to DEFAULT_SIGNIFICANT_DIGITS significant digits', () => {
|
||||
test('precision full uses up to DEFAULT_SIGNIFICANT_DIGITS significant digits', () => {
|
||||
expect(
|
||||
getYAxisFormattedValue(
|
||||
'0.00002625429914148441',
|
||||
|
||||
@@ -95,7 +95,7 @@ export const getGraphOptions = (
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
title: {
|
||||
display: title !== undefined,
|
||||
|
||||
@@ -109,8 +109,8 @@ export const useXAxisTimeUnit = (
|
||||
};
|
||||
const time = getTimeStamp(timeStamp as Date | number);
|
||||
|
||||
minTimeLocal = Math.min(Number.parseInt(time.toString(), 10), minTimeLocal);
|
||||
maxTimeLocal = Math.max(Number.parseInt(time.toString(), 10), maxTimeLocal);
|
||||
minTimeLocal = Math.min(parseInt(time.toString(), 10), minTimeLocal);
|
||||
maxTimeLocal = Math.max(parseInt(time.toString(), 10), maxTimeLocal);
|
||||
});
|
||||
|
||||
localTime = {
|
||||
|
||||
@@ -25,7 +25,7 @@ export const getYAxisFormattedValue = (
|
||||
format: string,
|
||||
precision: PrecisionOption = 2, // default precision requested
|
||||
): string => {
|
||||
const numValue = Number.parseFloat(value);
|
||||
const numValue = parseFloat(value);
|
||||
|
||||
// Handle non-numeric or special values first.
|
||||
if (isNaN(numValue)) {
|
||||
@@ -79,10 +79,10 @@ export const getYAxisFormattedValue = (
|
||||
}
|
||||
|
||||
const formatter = getValueFormat(format);
|
||||
const formattedValue = formatter(numValue, computeDecimals());
|
||||
const formattedValue = formatter(numValue, computeDecimals(), undefined);
|
||||
if (formattedValue.text && formattedValue.text.includes('.')) {
|
||||
formattedValue.text = formatDecimalWithLeadingZeros(
|
||||
Number.parseFloat(formattedValue.text),
|
||||
parseFloat(formattedValue.text),
|
||||
precision,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ describe('GuardAuthZ', () => {
|
||||
|
||||
expect(
|
||||
screen.getAllByText(
|
||||
new RegExp(permission.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&')),
|
||||
new RegExp(permission.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')),
|
||||
).length,
|
||||
).toBeGreaterThan(0);
|
||||
expect(screen.queryByText('Protected Content')).not.toBeInTheDocument();
|
||||
|
||||
@@ -177,7 +177,9 @@
|
||||
color: var(--destructive);
|
||||
opacity: 0.6;
|
||||
padding: 0;
|
||||
transition: background-color 0.2s, opacity 0.2s;
|
||||
transition:
|
||||
background-color 0.2s,
|
||||
opacity 0.2s;
|
||||
box-shadow: none;
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -90,9 +90,11 @@ describe('InviteMembersModal', () => {
|
||||
screen.getByRole('button', { name: /invite team members/i }),
|
||||
);
|
||||
|
||||
await expect(screen.findByText(
|
||||
expect(
|
||||
await screen.findByText(
|
||||
'Please enter valid emails and select roles for team members',
|
||||
)).resolves.toBeInTheDocument();
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows email-only message when email is invalid but role is selected', async () => {
|
||||
@@ -110,7 +112,9 @@ describe('InviteMembersModal', () => {
|
||||
screen.getByRole('button', { name: /invite team members/i }),
|
||||
);
|
||||
|
||||
await expect(screen.findByText('Please enter valid emails for team members')).resolves.toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText('Please enter valid emails for team members'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows role-only message when email is valid but role is missing', async () => {
|
||||
@@ -126,7 +130,9 @@ describe('InviteMembersModal', () => {
|
||||
screen.getByRole('button', { name: /invite team members/i }),
|
||||
);
|
||||
|
||||
await expect(screen.findByText('Please select roles for team members')).resolves.toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText('Please select roles for team members'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -198,7 +204,7 @@ describe('InviteMembersModal', () => {
|
||||
await user.type(emailInputs[1], 'bob@signoz.io');
|
||||
await user.click(screen.getAllByText('Select roles')[0]);
|
||||
const editorOptions = await screen.findAllByText('Editor');
|
||||
await user.click(editorOptions.at(-1));
|
||||
await user.click(editorOptions[editorOptions.length - 1]);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('button', { name: /invite team members/i }),
|
||||
@@ -250,7 +256,7 @@ describe('InviteMembersModal', () => {
|
||||
await user.type(emailInputs[1], 'bob@signoz.io');
|
||||
await user.click(screen.getAllByText('Select roles')[0]);
|
||||
const editorOptions = await screen.findAllByText('Editor');
|
||||
await user.click(editorOptions.at(-1));
|
||||
await user.click(editorOptions[editorOptions.length - 1]);
|
||||
|
||||
await user.click(
|
||||
screen.getByRole('button', { name: /invite team members/i }),
|
||||
|
||||
@@ -47,9 +47,8 @@ function LaunchChatSupport({
|
||||
featureFlagsFetchError,
|
||||
isLoggedIn,
|
||||
} = useAppContext();
|
||||
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] = useState(
|
||||
false,
|
||||
);
|
||||
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] =
|
||||
useState(false);
|
||||
|
||||
const { pathname } = useLocation();
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ function QueryBuilderSearchWrapper({
|
||||
const tagFiltersLength = tagFilters.items.length;
|
||||
|
||||
if (
|
||||
(!tagFiltersLength && (!filters || filters.items.length === 0)) ||
|
||||
(!tagFiltersLength && (!filters || !filters.items.length)) ||
|
||||
tagFiltersLength === filters?.items.length ||
|
||||
!contextQuery
|
||||
) {
|
||||
|
||||
@@ -5,7 +5,7 @@ export const VIEW_TYPES = {
|
||||
INFRAMETRICS: 'INFRAMETRICS',
|
||||
} as const;
|
||||
|
||||
export type VIEWS = typeof VIEW_TYPES[keyof typeof VIEW_TYPES];
|
||||
export type VIEWS = (typeof VIEW_TYPES)[keyof typeof VIEW_TYPES];
|
||||
|
||||
export const RESOURCE_KEYS = {
|
||||
CLUSTER_NAME: 'k8s.cluster.name',
|
||||
|
||||
@@ -10,6 +10,7 @@ import cx from 'classnames';
|
||||
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
|
||||
import QuerySearch from 'components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch';
|
||||
import { convertExpressionToFilters } from 'components/QueryBuilderV2/utils';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
@@ -46,6 +47,7 @@ import {
|
||||
TextSelect,
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Query, TagFilter } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||
@@ -79,6 +81,10 @@ function LogDetailInner({
|
||||
const [selectedView, setSelectedView] = useState<VIEWS>(selectedTab);
|
||||
|
||||
const [isFilterVisible, setIsFilterVisible] = useState<boolean>(false);
|
||||
const { featureFlags } = useAppContext();
|
||||
const isBodyJsonQueryEnabled =
|
||||
featureFlags?.find((flag) => flag.name === FeatureKeys.BODY_JSON_ENABLED)
|
||||
?.active || false;
|
||||
|
||||
const [filters, setFilters] = useState<TagFilter | null>(null);
|
||||
const [isEdit, setIsEdit] = useState<boolean>(false);
|
||||
@@ -163,7 +169,7 @@ function LogDetailInner({
|
||||
}, [log.id, logs, onNavigateLog, onScrollToLog, selectedView]);
|
||||
|
||||
const listQuery = useMemo(() => {
|
||||
if (!stagedQuery || stagedQuery.builder.queryData.length === 0) {
|
||||
if (!stagedQuery || stagedQuery.builder.queryData.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -208,11 +214,29 @@ function LogDetailInner({
|
||||
}
|
||||
};
|
||||
|
||||
const logBody = useMemo(() => {
|
||||
if (!isBodyJsonQueryEnabled) {
|
||||
return log?.body || '';
|
||||
}
|
||||
|
||||
try {
|
||||
const json = JSON.parse(log?.body || '');
|
||||
|
||||
if (typeof json?.message === 'string' && json.message !== '') {
|
||||
return json.message;
|
||||
}
|
||||
|
||||
return log?.body || '';
|
||||
} catch (error) {
|
||||
return log?.body || '';
|
||||
}
|
||||
}, [isBodyJsonQueryEnabled, log?.body]);
|
||||
|
||||
const htmlBody = useMemo(
|
||||
() => ({
|
||||
__html: getSanitizedLogBody(log?.body || '', { shouldEscapeHtml: true }),
|
||||
__html: getSanitizedLogBody(logBody || '', { shouldEscapeHtml: true }),
|
||||
}),
|
||||
[log?.body],
|
||||
[logBody],
|
||||
);
|
||||
|
||||
const handleJSONCopy = (): void => {
|
||||
@@ -261,7 +285,7 @@ function LogDetailInner({
|
||||
...query.filter,
|
||||
expression: value,
|
||||
},
|
||||
}
|
||||
}
|
||||
: query,
|
||||
),
|
||||
},
|
||||
@@ -418,7 +442,7 @@ function LogDetailInner({
|
||||
<div className="log-detail-drawer__log">
|
||||
<Divider type="vertical" className={cx('log-type-indicator', logType)} />
|
||||
<Tooltip
|
||||
title={removeEscapeCharacters(log?.body)}
|
||||
title={removeEscapeCharacters(logBody)}
|
||||
placement="left"
|
||||
mouseLeaveDelay={0}
|
||||
>
|
||||
|
||||
@@ -20,9 +20,10 @@ function AddToQueryHOC({
|
||||
onAddToQuery(fieldKey, fieldValue, OPERATORS['='], dataType);
|
||||
};
|
||||
|
||||
const popOverContent = useMemo(() => <span>Add to query: {fieldKey}</span>, [
|
||||
fieldKey,
|
||||
]);
|
||||
const popOverContent = useMemo(
|
||||
() => <span>Add to query: {fieldKey}</span>,
|
||||
[fieldKey],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={cx('addToQueryContainer', fontSize)} onClick={handleQueryAdd}>
|
||||
|
||||
@@ -104,7 +104,7 @@ type ListLogViewProps = {
|
||||
selectedFields: IField[];
|
||||
onSetActiveLog: (
|
||||
log: ILog,
|
||||
selectedTab?: typeof VIEW_TYPES[keyof typeof VIEW_TYPES],
|
||||
selectedTab?: (typeof VIEW_TYPES)[keyof typeof VIEW_TYPES],
|
||||
) => void;
|
||||
onAddToQuery: AddToQueryHOCProps['onAddToQuery'];
|
||||
activeLog?: ILog | null;
|
||||
@@ -166,11 +166,11 @@ function ListLogView({
|
||||
? formatTimezoneAdjustedTimestamp(
|
||||
flattenLogData.timestamp,
|
||||
DATE_TIME_FORMATS.ISO_DATETIME_MS,
|
||||
)
|
||||
)
|
||||
: formatTimezoneAdjustedTimestamp(
|
||||
flattenLogData.timestamp / 1e6,
|
||||
DATE_TIME_FORMATS.ISO_DATETIME_MS,
|
||||
),
|
||||
),
|
||||
[flattenLogData.timestamp, formatTimezoneAdjustedTimestamp],
|
||||
);
|
||||
|
||||
|
||||
@@ -24,10 +24,10 @@ export const Container = styled(Card)<{
|
||||
fontSize === FontSize.SMALL
|
||||
? `margin-bottom:0.1rem;`
|
||||
: fontSize === FontSize.MEDIUM
|
||||
? `margin-bottom: 0.2rem;`
|
||||
: fontSize === FontSize.LARGE
|
||||
? `margin-bottom:0.3rem;`
|
||||
: ``}
|
||||
? `margin-bottom: 0.2rem;`
|
||||
: fontSize === FontSize.LARGE
|
||||
? `margin-bottom:0.3rem;`
|
||||
: ``}
|
||||
cursor: pointer;
|
||||
|
||||
&:not(:hover) .log-line-action-buttons {
|
||||
@@ -41,10 +41,10 @@ export const Container = styled(Card)<{
|
||||
fontSize === FontSize.SMALL
|
||||
? `padding:0.1rem 0.6rem;`
|
||||
: fontSize === FontSize.MEDIUM
|
||||
? `padding: 0.2rem 0.6rem;`
|
||||
: fontSize === FontSize.LARGE
|
||||
? `padding:0.3rem 0.6rem;`
|
||||
: ``}
|
||||
? `padding: 0.2rem 0.6rem;`
|
||||
: fontSize === FontSize.LARGE
|
||||
? `padding:0.3rem 0.6rem;`
|
||||
: ``}
|
||||
|
||||
${({ $isActiveLog, $isDarkMode, $logType }): string =>
|
||||
getActiveLogBackground($isActiveLog, $isDarkMode, $logType)}
|
||||
@@ -65,8 +65,8 @@ export const LogContainer = styled.div<LogContainerProps>`
|
||||
fontSize === FontSize.SMALL
|
||||
? `gap: 2px;`
|
||||
: fontSize === FontSize.MEDIUM
|
||||
? ` gap:4px;`
|
||||
: `gap:6px;`}
|
||||
? ` gap:4px;`
|
||||
: `gap:6px;`}
|
||||
`;
|
||||
|
||||
export const LogText = styled.div<LogTextProps>`
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
.log-state-indicator {
|
||||
padding-left: 8px;
|
||||
|
||||
.line {
|
||||
margin: 0 8px;
|
||||
min-height: 24px;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
|
||||
import { LogType } from './LogStateIndicator';
|
||||
|
||||
export function getRowBackgroundColor(
|
||||
isDarkMode: boolean,
|
||||
logType?: string,
|
||||
): string {
|
||||
if (isDarkMode) {
|
||||
switch (logType) {
|
||||
case LogType.INFO:
|
||||
return `${Color.BG_ROBIN_500}40`;
|
||||
case LogType.WARN:
|
||||
return `${Color.BG_AMBER_500}40`;
|
||||
case LogType.ERROR:
|
||||
return `${Color.BG_CHERRY_500}40`;
|
||||
case LogType.TRACE:
|
||||
return `${Color.BG_FOREST_400}40`;
|
||||
case LogType.DEBUG:
|
||||
return `${Color.BG_AQUA_500}40`;
|
||||
case LogType.FATAL:
|
||||
return `${Color.BG_SAKURA_500}40`;
|
||||
default:
|
||||
return `${Color.BG_ROBIN_500}40`;
|
||||
}
|
||||
}
|
||||
switch (logType) {
|
||||
case LogType.INFO:
|
||||
return Color.BG_ROBIN_100;
|
||||
case LogType.WARN:
|
||||
return Color.BG_AMBER_100;
|
||||
case LogType.ERROR:
|
||||
return Color.BG_CHERRY_100;
|
||||
case LogType.TRACE:
|
||||
return Color.BG_FOREST_200;
|
||||
case LogType.DEBUG:
|
||||
return Color.BG_AQUA_100;
|
||||
case LogType.FATAL:
|
||||
return Color.BG_SAKURA_100;
|
||||
default:
|
||||
return Color.BG_VANILLA_300;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ describe('getLogIndicatorType', () => {
|
||||
expect(getLogIndicatorType(log)).toBe('TRACE');
|
||||
});
|
||||
|
||||
it('severity_text should be used when severity_number is absent', () => {
|
||||
it('severity_text should be used when severity_number is absent ', () => {
|
||||
const log = {
|
||||
date: '2024-02-29T12:34:46Z',
|
||||
timestamp: 1646115296,
|
||||
@@ -157,7 +157,7 @@ describe('logIndicatorBySeverityNumber', () => {
|
||||
];
|
||||
logLevelExpectations.forEach((e) => {
|
||||
for (let sevNum = e.minSevNumber; sevNum <= e.maxSevNumber; sevNum++) {
|
||||
const sevText = (Math.random() + 1).toString(36).slice(2);
|
||||
const sevText = (Math.random() + 1).toString(36).substring(2);
|
||||
|
||||
const log = {
|
||||
date: '2024-02-29T12:34:46Z',
|
||||
|
||||
@@ -88,11 +88,11 @@ function RawLogView({
|
||||
? formatTimezoneAdjustedTimestamp(
|
||||
data.timestamp,
|
||||
DATE_TIME_FORMATS.ISO_DATETIME_MS,
|
||||
)
|
||||
)
|
||||
: formatTimezoneAdjustedTimestamp(
|
||||
data.timestamp / 1e6,
|
||||
DATE_TIME_FORMATS.ISO_DATETIME_MS,
|
||||
);
|
||||
);
|
||||
parts.push(date);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,22 +39,22 @@ export const RawLogViewContainer = styled(Row)<{
|
||||
fontSize === FontSize.SMALL
|
||||
? `margin: 1px 0;`
|
||||
: fontSize === FontSize.MEDIUM
|
||||
? `margin: 1px 0;`
|
||||
: `margin: 2px 0;`}
|
||||
? `margin: 1px 0;`
|
||||
: `margin: 2px 0;`}
|
||||
}
|
||||
|
||||
${({ $isReadOnly, $isActiveLog, $isDarkMode, $logType }): string =>
|
||||
$isActiveLog
|
||||
? getActiveLogBackground($isActiveLog, $isDarkMode, $logType)
|
||||
: !$isReadOnly
|
||||
? `&:hover { ${getActiveLogBackground(true, $isDarkMode, $logType)} }`
|
||||
: ''}
|
||||
? `&:hover { ${getActiveLogBackground(true, $isDarkMode, $logType)} }`
|
||||
: ''}
|
||||
|
||||
${({ $isHightlightedLog, $isDarkMode }): string =>
|
||||
$isHightlightedLog
|
||||
? `background-color: ${
|
||||
$isDarkMode ? Color.BG_ROBIN_600 : Color.BG_VANILLA_400
|
||||
};
|
||||
};
|
||||
transition: background-color 2s ease-in;`
|
||||
: ''}
|
||||
|
||||
@@ -104,8 +104,8 @@ export const RawLogContent = styled.div<RawLogContentProps>`
|
||||
fontSize === FontSize.SMALL
|
||||
? `font-size:11px; line-height:16px; padding:1px;`
|
||||
: fontSize === FontSize.MEDIUM
|
||||
? `font-size:13px; line-height:20px; padding:1px;`
|
||||
: `font-size:14px; line-height:24px; padding:2px;`}
|
||||
? `font-size:13px; line-height:20px; padding:1px;`
|
||||
: `font-size:14px; line-height:24px; padding:2px;`}
|
||||
|
||||
cursor: ${({ $isActiveLog, $isReadOnly }): string =>
|
||||
$isActiveLog || $isReadOnly ? 'initial' : 'pointer'};
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface RawLogViewProps {
|
||||
handleChangeSelectedView?: ChangeViewFunctionType;
|
||||
onSetActiveLog?: (
|
||||
log: ILog,
|
||||
selectedTab?: typeof VIEW_TYPES[keyof typeof VIEW_TYPES],
|
||||
selectedTab?: (typeof VIEW_TYPES)[keyof typeof VIEW_TYPES],
|
||||
) => void;
|
||||
onClearActiveLog?: () => void;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,6 @@ export const TableBodyContent = styled.div<TableBodyContentProps>`
|
||||
fontSize === FontSize.SMALL
|
||||
? `font-size:11px; line-height:16px;`
|
||||
: fontSize === FontSize.MEDIUM
|
||||
? `font-size:13px; line-height:20px;`
|
||||
: `font-size:14px; line-height:24px;`}
|
||||
? `font-size:13px; line-height:20px;`
|
||||
: `font-size:14px; line-height:24px;`}
|
||||
`;
|
||||
|
||||
114
frontend/src/components/Logs/TableView/useLogsTableColumns.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import type { ReactElement } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import TanStackTable from 'components/TanStackTableView';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { getSanitizedLogBody } from 'container/LogDetailedView/utils';
|
||||
import { FontSize } from 'container/OptionsMenu/types';
|
||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||
import { useTimezone } from 'providers/Timezone';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
|
||||
import type { TableColumnDef } from '../../TanStackTableView/types';
|
||||
import LogStateIndicator from '../LogStateIndicator/LogStateIndicator';
|
||||
|
||||
type UseLogsTableColumnsProps = {
|
||||
fields: IField[];
|
||||
fontSize: FontSize;
|
||||
appendTo?: 'center' | 'end';
|
||||
};
|
||||
|
||||
export function useLogsTableColumns({
|
||||
fields,
|
||||
fontSize,
|
||||
appendTo = 'center',
|
||||
}: UseLogsTableColumnsProps): TableColumnDef<ILog>[] {
|
||||
const { formatTimezoneAdjustedTimestamp } = useTimezone();
|
||||
|
||||
return useMemo<TableColumnDef<ILog>[]>(() => {
|
||||
const stateIndicatorCol: TableColumnDef<ILog> = {
|
||||
id: 'state-indicator',
|
||||
header: '',
|
||||
pin: 'left',
|
||||
enableMove: false,
|
||||
enableResize: false,
|
||||
enableRemove: false,
|
||||
canBeHidden: false,
|
||||
width: { fixed: 24 },
|
||||
cell: ({ row }): ReactElement => (
|
||||
<LogStateIndicator
|
||||
fontSize={fontSize}
|
||||
severityText={row.severity_text as string}
|
||||
severityNumber={row.severity_number as number}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
const fieldColumns: TableColumnDef<ILog>[] = fields
|
||||
.filter((f): boolean => !['id', 'body', 'timestamp'].includes(f.name))
|
||||
.map(
|
||||
(f): TableColumnDef<ILog> => ({
|
||||
id: f.name,
|
||||
header: f.name,
|
||||
accessorFn: (log): unknown => FlatLogData(log)[f.name],
|
||||
enableRemove: true,
|
||||
width: { min: 192 },
|
||||
cell: ({ value }): ReactElement => (
|
||||
<TanStackTable.Text>{String(value ?? '')}</TanStackTable.Text>
|
||||
),
|
||||
}),
|
||||
);
|
||||
|
||||
const timestampCol: TableColumnDef<ILog> | null = fields.some(
|
||||
(f) => f.name === 'timestamp',
|
||||
)
|
||||
? {
|
||||
id: 'timestamp',
|
||||
header: 'Timestamp',
|
||||
accessorFn: (log): unknown => log.timestamp,
|
||||
width: { default: 170, min: 170 },
|
||||
cell: ({ value }): ReactElement => {
|
||||
const ts = value as string | number;
|
||||
const formatted =
|
||||
typeof ts === 'string'
|
||||
? formatTimezoneAdjustedTimestamp(ts, DATE_TIME_FORMATS.ISO_DATETIME_MS)
|
||||
: formatTimezoneAdjustedTimestamp(
|
||||
ts / 1e6,
|
||||
DATE_TIME_FORMATS.ISO_DATETIME_MS,
|
||||
);
|
||||
return <TanStackTable.Text>{formatted}</TanStackTable.Text>;
|
||||
},
|
||||
}
|
||||
: null;
|
||||
|
||||
const bodyCol: TableColumnDef<ILog> | null = fields.some(
|
||||
(f) => f.name === 'body',
|
||||
)
|
||||
? {
|
||||
id: 'body',
|
||||
header: 'Body',
|
||||
accessorFn: (log): string => log.body,
|
||||
canBeHidden: false,
|
||||
width: { default: '100%', min: 300 },
|
||||
cell: ({ value, isActive }): ReactElement => (
|
||||
<TanStackTable.Text
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: getSanitizedLogBody(value as string, {
|
||||
shouldEscapeHtml: true,
|
||||
}),
|
||||
}}
|
||||
data-active={isActive}
|
||||
/>
|
||||
),
|
||||
}
|
||||
: null;
|
||||
|
||||
return [
|
||||
stateIndicatorCol,
|
||||
...(timestampCol ? [timestampCol] : []),
|
||||
...(appendTo === 'center' ? fieldColumns : []),
|
||||
...(bodyCol ? [bodyCol] : []),
|
||||
...(appendTo === 'end' ? fieldColumns : []),
|
||||
];
|
||||
}, [fields, appendTo, fontSize, formatTimezoneAdjustedTimestamp]);
|
||||
}
|
||||