Compare commits

..

9 Commits

Author SHA1 Message Date
Abhi Kumar
efa71e46a3 chore: preetify 2026-04-25 01:17:17 +05:30
Abhi Kumar
c82748ce1a chore: minor ui fixes in tooltip 2026-04-25 00:00:00 +05:30
Vinicius Lourenço
cd16081a1e chore(oxlint): remove unicorn & other rules that breaks the code on --fix (#11097)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
2026-04-24 17:42:49 +00:00
Ashwin Bhatkal
c23a53c8d2 fix: add config path and fmt config path in vs code settings (#11093)
* fix: add config path vs code settings

* fix: add fmt config path as well

* fix: quiet instead of fix
2026-04-24 15:09:30 +00:00
Abhi kumar
dbfe47e757 chore: added tooltip events + minor ui fixes (#11086)
* chore: added tooltip events + minor ui fixes

* chore: pr review comments

* chore: added todo for event removal

* fix: added fix for formatting issues

---------

Co-authored-by: Ashwin Bhatkal <ashwin96@gmail.com>
2026-04-24 13:30:26 +00:00
Ashwin Bhatkal
e29b032e70 test(useResourceAttribute): add ResourceProvider behavior coverage (#11069)
* test(useResourceAttribute): add ResourceProvider behavior coverage

Covers initial state, URL hydration, step-machine transitions (Idle ->
TagKey -> Operator -> TagValue), handleBlur commit/purge paths,
handleClose/handleClearAll, handleEnvironmentChange (add, clear,
replace, dot-metrics feature flag, preserving unrelated URL params),
and SERVICE_MAP visibility filtering.

Tests exercise only the public IResourceAttributeProps contract so they
serve as a behavior pin for any future refactor of the internal state
machine.

* test(useResourceAttribute): close ResourceProvider coverage gaps

Adds four tests covering:
- URL re-hydration effect fires when router URL changes mid-session
- SERVICE_MAP filters fetched tag keys through whilelistedKeys
- In-flight GetTagKeys keeps loading=true and clears optionsData
- API error payload still flips loading=false and leaves options empty

* fix: format file
2026-04-24 12:59:59 +00:00
Vinicius Lourenço
a92871d704 chore(fmt): enable oxfmt (#11057)
* chore(fmt): enable oxfmt

* fix(prettify): update files due to main
2026-04-24 11:45:54 +00:00
Vinicius Lourenço
b55ae83993 feat(tanstack-table): promote logs table component to /components (#10946)
Some checks failed
build-staging / prepare (push) Has been cancelled
build-staging / js-build (push) Has been cancelled
build-staging / go-build (push) Has been cancelled
build-staging / staging (push) Has been cancelled
Release Drafter / update_release_draft (push) Has been cancelled
* refactor(tanstack): move table to components & convert to css modules

* refactor(table): extract table to own component

* refactor(table): optimize

* chore(tests): cleanup tests and components

* fix(styles): minor fixes on styles

* fix(tests): use find by text

* fix(row): missing active styles

* refactor(tanstack-table): refine component based on infra monitoring

* docs(tanstacktable): add more docs about usage

* refactor(table): cleanup and fixes related to column and old preferences of logs

* refactor(table): more cleanup

* refactor(table): removed deprecated api

* fix(tanstack): more cleanup

* fix(sonner): removed old dependency

* fix(resizing): better resizing support

* fix(tanstack-header-row): use our version of popover

* fix(column-view): remove unused file

* fix(oxlint): alerts and issues

* fix(test): rollback aria-sort and rename to data-sort
2026-04-24 10:44:13 +00:00
Pandey
7e7d7ab570 feat(global): add mcp_url to global config (#11085)
* feat(global): add mcp_url to global config

Adds an optional mcp_url field to the global config so the frontend can
gate the MCP settings page on its presence. When unset the API returns
"mcp_url": null (pointer + nullable:"true"); when set it emits the
parsed URL as a string.

* feat(global): surface mcp_url in frontend types

Adds mcp_url to the manual GlobalConfigData type and refreshes the
generated OpenAPI client so consumers can read the new field.

* docs(global): use <unset> placeholder for mcp_url example

Matches the style of external_url and ingestion_url above it.

* style(global): separate mcp_url prep from return in GetConfig

Adds a blank line between the nullable-conversion block and the return
statement so the two logical phases read as distinct blocks.

* feat(global): mark endpoint fields as required in the API schema

The backend always emits external_url, ingestion_url and mcp_url on
GET /api/v1/global/config (mcp_url as literal null when unset), so the
JSON keys are always present. Add required:"true" to all three and
regenerate the OpenAPI + frontend client so consumers get non-optional
types.

* revert(global): drop mcp_url from legacy GlobalConfigData type

The legacy hand-written type for the non-Orval getGlobalConfig client
should be left alone; consumers that need mcp_url go through the
generated Orval client.
2026-04-24 10:24:21 +00:00
707 changed files with 12454 additions and 10982 deletions

View File

@@ -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": []
}

View File

@@ -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:

View File

@@ -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:
@@ -4489,184 +4496,6 @@ components:
type: object
Sigv4SigV4Config:
type: object
SpantypesFieldContext:
enum:
- attribute
- resource
type: string
SpantypesGettableSpanMapperGroups:
properties:
items:
items:
$ref: '#/components/schemas/SpantypesSpanMapperGroup'
type: array
required:
- items
type: object
SpantypesPostableSpanMapper:
properties:
config:
$ref: '#/components/schemas/SpantypesSpanMapperConfig'
enabled:
type: boolean
field_context:
$ref: '#/components/schemas/SpantypesFieldContext'
name:
type: string
required:
- name
- field_context
- config
type: object
SpantypesPostableSpanMapperGroup:
properties:
category:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCategory'
condition:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCondition'
enabled:
type: boolean
name:
type: string
required:
- name
- category
- condition
type: object
SpantypesSpanMapper:
properties:
config:
$ref: '#/components/schemas/SpantypesSpanMapperConfig'
createdAt:
format: date-time
type: string
createdBy:
type: string
enabled:
type: boolean
field_context:
$ref: '#/components/schemas/SpantypesFieldContext'
group_id:
type: string
id:
type: string
name:
type: string
updatedAt:
format: date-time
type: string
updatedBy:
type: string
required:
- id
- group_id
- name
- field_context
- config
- enabled
type: object
SpantypesSpanMapperConfig:
properties:
sources:
items:
$ref: '#/components/schemas/SpantypesSpanMapperSource'
nullable: true
type: array
required:
- sources
type: object
SpantypesSpanMapperGroup:
properties:
category:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCategory'
condition:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCondition'
createdAt:
format: date-time
type: string
createdBy:
type: string
enabled:
type: boolean
id:
type: string
name:
type: string
orgId:
type: string
updatedAt:
format: date-time
type: string
updatedBy:
type: string
required:
- id
- orgId
- name
- category
- condition
- enabled
type: object
SpantypesSpanMapperGroupCategory:
type: object
SpantypesSpanMapperGroupCondition:
properties:
attributes:
items:
type: string
nullable: true
type: array
resource:
items:
type: string
nullable: true
type: array
required:
- attributes
- resource
type: object
SpantypesSpanMapperOperation:
enum:
- move
- copy
type: string
SpantypesSpanMapperSource:
properties:
context:
$ref: '#/components/schemas/SpantypesFieldContext'
key:
type: string
operation:
$ref: '#/components/schemas/SpantypesSpanMapperOperation'
priority:
type: integer
required:
- key
- context
- operation
- priority
type: object
SpantypesUpdatableSpanMapper:
properties:
config:
$ref: '#/components/schemas/SpantypesSpanMapperConfig'
enabled:
nullable: true
type: boolean
field_context:
$ref: '#/components/schemas/SpantypesFieldContext'
type: object
SpantypesUpdatableSpanMapperGroup:
properties:
condition:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCondition'
enabled:
nullable: true
type: boolean
name:
nullable: true
type: string
type: object
TelemetrytypesFieldContext:
enum:
- metric
@@ -9409,487 +9238,6 @@ paths:
summary: Updates my service account
tags:
- serviceaccount
/api/v1/span_mapper_groups:
get:
deprecated: false
description: Returns all span attribute mapping groups for the authenticated
org.
operationId: ListSpanMapperGroups
parameters:
- explode: true
in: query
name: category
schema:
$ref: '#/components/schemas/SpantypesSpanMapperGroupCategory'
style: deepObject
- in: query
name: enabled
schema:
nullable: true
type: boolean
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/SpantypesGettableSpanMapperGroups'
status:
type: string
required:
- status
- data
type: object
description: OK
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- VIEWER
- tokenizer:
- VIEWER
summary: List span attribute mapping groups
tags:
- spanmapper
post:
deprecated: false
description: Creates a new span attribute mapping group for the org.
operationId: CreateSpanMapperGroup
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SpantypesPostableSpanMapperGroup'
responses:
"201":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/SpantypesSpanMapperGroup'
status:
type: string
required:
- status
- data
type: object
description: Created
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"409":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Conflict
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Create a span attribute mapping group
tags:
- spanmapper
/api/v1/span_mapper_groups/{groupId}:
delete:
deprecated: false
description: Hard-deletes a mapping group and cascades to all its mappers.
operationId: DeleteSpanMapperGroup
parameters:
- in: path
name: groupId
required: true
schema:
type: string
responses:
"204":
description: No Content
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Delete a span attribute mapping group
tags:
- spanmapper
patch:
deprecated: false
description: Partially updates an existing mapping group's name, condition,
or enabled state.
operationId: UpdateSpanMapperGroup
parameters:
- in: path
name: groupId
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SpantypesUpdatableSpanMapperGroup'
responses:
"204":
description: No Content
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Update a span attribute mapping group
tags:
- spanmapper
/api/v1/span_mapper_groups/{groupId}/span_mappers:
get:
deprecated: false
description: Returns all mappers belonging to a mapping group.
operationId: ListSpanMappers
parameters:
- in: path
name: groupId
required: true
schema:
type: string
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/SpantypesGettableSpanMapperGroups'
status:
type: string
required:
- status
- data
type: object
description: OK
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- VIEWER
- tokenizer:
- VIEWER
summary: List span mappers for a group
tags:
- spanmapper
post:
deprecated: false
description: Adds a new mapper to the specified mapping group.
operationId: CreateSpanMapper
parameters:
- in: path
name: groupId
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SpantypesPostableSpanMapper'
responses:
"201":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/SpantypesSpanMapper'
status:
type: string
required:
- status
- data
type: object
description: Created
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"409":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Conflict
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Create a span mapper
tags:
- spanmapper
/api/v1/span_mapper_groups/{groupId}/span_mappers/{mapperId}:
delete:
deprecated: false
description: Hard-deletes a mapper from a mapping group.
operationId: DeleteSpanMapper
parameters:
- in: path
name: groupId
required: true
schema:
type: string
- in: path
name: mapperId
required: true
schema:
type: string
responses:
"204":
description: No Content
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Delete a span mapper
tags:
- spanmapper
patch:
deprecated: false
description: Partially updates an existing mapper's field context, config, or
enabled state.
operationId: UpdateSpanMapper
parameters:
- in: path
name: groupId
required: true
schema:
type: string
- in: path
name: mapperId
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/SpantypesUpdatableSpanMapper'
responses:
"204":
description: No Content
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Update a span mapper
tags:
- spanmapper
/api/v1/testChannel:
post:
deprecated: true

View File

@@ -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",
@@ -338,82 +339,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 +463,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 +539,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"
}
},
{

View File

@@ -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'

View File

@@ -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;
}
};

View File

@@ -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",
@@ -214,9 +214,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 +240,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"
]
},

View File

@@ -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' });
},

View File

@@ -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 {
@@ -5470,187 +5475,6 @@ export interface Sigv4SigV4ConfigDTO {
[key: string]: unknown;
}
export enum SpantypesFieldContextDTO {
attribute = 'attribute',
resource = 'resource',
}
export interface SpantypesGettableSpanMapperGroupsDTO {
/**
* @type array
*/
items: SpantypesSpanMapperGroupDTO[];
}
export interface SpantypesPostableSpanMapperDTO {
config: SpantypesSpanMapperConfigDTO;
/**
* @type boolean
*/
enabled?: boolean;
field_context: SpantypesFieldContextDTO;
/**
* @type string
*/
name: string;
}
export interface SpantypesPostableSpanMapperGroupDTO {
category: SpantypesSpanMapperGroupCategoryDTO;
condition: SpantypesSpanMapperGroupConditionDTO;
/**
* @type boolean
*/
enabled?: boolean;
/**
* @type string
*/
name: string;
}
export interface SpantypesSpanMapperDTO {
config: SpantypesSpanMapperConfigDTO;
/**
* @type string
* @format date-time
*/
createdAt?: Date;
/**
* @type string
*/
createdBy?: string;
/**
* @type boolean
*/
enabled: boolean;
field_context: SpantypesFieldContextDTO;
/**
* @type string
*/
group_id: string;
/**
* @type string
*/
id: string;
/**
* @type string
*/
name: string;
/**
* @type string
* @format date-time
*/
updatedAt?: Date;
/**
* @type string
*/
updatedBy?: string;
}
export interface SpantypesSpanMapperConfigDTO {
/**
* @type array
* @nullable true
*/
sources: SpantypesSpanMapperSourceDTO[] | null;
}
export interface SpantypesSpanMapperGroupDTO {
category: SpantypesSpanMapperGroupCategoryDTO;
condition: SpantypesSpanMapperGroupConditionDTO;
/**
* @type string
* @format date-time
*/
createdAt?: Date;
/**
* @type string
*/
createdBy?: string;
/**
* @type boolean
*/
enabled: boolean;
/**
* @type string
*/
id: string;
/**
* @type string
*/
name: string;
/**
* @type string
*/
orgId: string;
/**
* @type string
* @format date-time
*/
updatedAt?: Date;
/**
* @type string
*/
updatedBy?: string;
}
export interface SpantypesSpanMapperGroupCategoryDTO {
[key: string]: unknown;
}
export interface SpantypesSpanMapperGroupConditionDTO {
/**
* @type array
* @nullable true
*/
attributes: string[] | null;
/**
* @type array
* @nullable true
*/
resource: string[] | null;
}
export enum SpantypesSpanMapperOperationDTO {
move = 'move',
copy = 'copy',
}
export interface SpantypesSpanMapperSourceDTO {
context: SpantypesFieldContextDTO;
/**
* @type string
*/
key: string;
operation: SpantypesSpanMapperOperationDTO;
/**
* @type integer
*/
priority: number;
}
export interface SpantypesUpdatableSpanMapperDTO {
config?: SpantypesSpanMapperConfigDTO;
/**
* @type boolean
* @nullable true
*/
enabled?: boolean | null;
field_context?: SpantypesFieldContextDTO;
}
export interface SpantypesUpdatableSpanMapperGroupDTO {
condition?: SpantypesSpanMapperGroupConditionDTO;
/**
* @type boolean
* @nullable true
*/
enabled?: boolean | null;
/**
* @type string
* @nullable true
*/
name?: string | null;
}
export enum TelemetrytypesFieldContextDTO {
metric = 'metric',
log = 'log',
@@ -7101,71 +6925,6 @@ export type GetMyServiceAccount200 = {
status: string;
};
export type ListSpanMapperGroupsParams = {
/**
* @description undefined
*/
category?: SpantypesSpanMapperGroupCategoryDTO;
/**
* @type boolean
* @nullable true
* @description undefined
*/
enabled?: boolean | null;
};
export type ListSpanMapperGroups200 = {
data: SpantypesGettableSpanMapperGroupsDTO;
/**
* @type string
*/
status: string;
};
export type CreateSpanMapperGroup201 = {
data: SpantypesSpanMapperGroupDTO;
/**
* @type string
*/
status: string;
};
export type DeleteSpanMapperGroupPathParameters = {
groupId: string;
};
export type UpdateSpanMapperGroupPathParameters = {
groupId: string;
};
export type ListSpanMappersPathParameters = {
groupId: string;
};
export type ListSpanMappers200 = {
data: SpantypesGettableSpanMapperGroupsDTO;
/**
* @type string
*/
status: string;
};
export type CreateSpanMapperPathParameters = {
groupId: string;
};
export type CreateSpanMapper201 = {
data: SpantypesSpanMapperDTO;
/**
* @type string
*/
status: string;
};
export type DeleteSpanMapperPathParameters = {
groupId: string;
mapperId: string;
};
export type UpdateSpanMapperPathParameters = {
groupId: string;
mapperId: string;
};
export type ListUsersDeprecated200 = {
/**
* @type array

View File

@@ -1,787 +0,0 @@
/**
* ! Do not edit manually
* * The file has been auto-generated using Orval for SigNoz
* * regenerate with 'yarn generate:api'
* SigNoz
*/
import { useMutation, useQuery } from 'react-query';
import type {
InvalidateOptions,
MutationFunction,
QueryClient,
QueryFunction,
QueryKey,
UseMutationOptions,
UseMutationResult,
UseQueryOptions,
UseQueryResult,
} from 'react-query';
import type {
CreateSpanMapper201,
CreateSpanMapperGroup201,
CreateSpanMapperPathParameters,
DeleteSpanMapperGroupPathParameters,
DeleteSpanMapperPathParameters,
ListSpanMapperGroups200,
ListSpanMapperGroupsParams,
ListSpanMappers200,
ListSpanMappersPathParameters,
RenderErrorResponseDTO,
SpantypesPostableSpanMapperDTO,
SpantypesPostableSpanMapperGroupDTO,
SpantypesUpdatableSpanMapperDTO,
SpantypesUpdatableSpanMapperGroupDTO,
UpdateSpanMapperGroupPathParameters,
UpdateSpanMapperPathParameters,
} from '../sigNoz.schemas';
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
/**
* Returns all span attribute mapping groups for the authenticated org.
* @summary List span attribute mapping groups
*/
export const listSpanMapperGroups = (
params?: ListSpanMapperGroupsParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<ListSpanMapperGroups200>({
url: `/api/v1/span_mapper_groups`,
method: 'GET',
params,
signal,
});
};
export const getListSpanMapperGroupsQueryKey = (
params?: ListSpanMapperGroupsParams,
) => {
return [`/api/v1/span_mapper_groups`, ...(params ? [params] : [])] as const;
};
export const getListSpanMapperGroupsQueryOptions = <
TData = Awaited<ReturnType<typeof listSpanMapperGroups>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
params?: ListSpanMapperGroupsParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listSpanMapperGroups>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getListSpanMapperGroupsQueryKey(params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof listSpanMapperGroups>>
> = ({ signal }) => listSpanMapperGroups(params, signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof listSpanMapperGroups>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ListSpanMapperGroupsQueryResult = NonNullable<
Awaited<ReturnType<typeof listSpanMapperGroups>>
>;
export type ListSpanMapperGroupsQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary List span attribute mapping groups
*/
export function useListSpanMapperGroups<
TData = Awaited<ReturnType<typeof listSpanMapperGroups>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
params?: ListSpanMapperGroupsParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listSpanMapperGroups>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getListSpanMapperGroupsQueryOptions(params, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary List span attribute mapping groups
*/
export const invalidateListSpanMapperGroups = async (
queryClient: QueryClient,
params?: ListSpanMapperGroupsParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getListSpanMapperGroupsQueryKey(params) },
options,
);
return queryClient;
};
/**
* Creates a new span attribute mapping group for the org.
* @summary Create a span attribute mapping group
*/
export const createSpanMapperGroup = (
spantypesPostableSpanMapperGroupDTO: BodyType<SpantypesPostableSpanMapperGroupDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<CreateSpanMapperGroup201>({
url: `/api/v1/span_mapper_groups`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: spantypesPostableSpanMapperGroupDTO,
signal,
});
};
export const getCreateSpanMapperGroupMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createSpanMapperGroup>>,
TError,
{ data: BodyType<SpantypesPostableSpanMapperGroupDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof createSpanMapperGroup>>,
TError,
{ data: BodyType<SpantypesPostableSpanMapperGroupDTO> },
TContext
> => {
const mutationKey = ['createSpanMapperGroup'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof createSpanMapperGroup>>,
{ data: BodyType<SpantypesPostableSpanMapperGroupDTO> }
> = (props) => {
const { data } = props ?? {};
return createSpanMapperGroup(data);
};
return { mutationFn, ...mutationOptions };
};
export type CreateSpanMapperGroupMutationResult = NonNullable<
Awaited<ReturnType<typeof createSpanMapperGroup>>
>;
export type CreateSpanMapperGroupMutationBody =
BodyType<SpantypesPostableSpanMapperGroupDTO>;
export type CreateSpanMapperGroupMutationError =
ErrorType<RenderErrorResponseDTO>;
/**
* @summary Create a span attribute mapping group
*/
export const useCreateSpanMapperGroup = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createSpanMapperGroup>>,
TError,
{ data: BodyType<SpantypesPostableSpanMapperGroupDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof createSpanMapperGroup>>,
TError,
{ data: BodyType<SpantypesPostableSpanMapperGroupDTO> },
TContext
> => {
const mutationOptions = getCreateSpanMapperGroupMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Hard-deletes a mapping group and cascades to all its mappers.
* @summary Delete a span attribute mapping group
*/
export const deleteSpanMapperGroup = ({
groupId,
}: DeleteSpanMapperGroupPathParameters) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/span_mapper_groups/${groupId}`,
method: 'DELETE',
});
};
export const getDeleteSpanMapperGroupMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteSpanMapperGroup>>,
TError,
{ pathParams: DeleteSpanMapperGroupPathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof deleteSpanMapperGroup>>,
TError,
{ pathParams: DeleteSpanMapperGroupPathParameters },
TContext
> => {
const mutationKey = ['deleteSpanMapperGroup'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof deleteSpanMapperGroup>>,
{ pathParams: DeleteSpanMapperGroupPathParameters }
> = (props) => {
const { pathParams } = props ?? {};
return deleteSpanMapperGroup(pathParams);
};
return { mutationFn, ...mutationOptions };
};
export type DeleteSpanMapperGroupMutationResult = NonNullable<
Awaited<ReturnType<typeof deleteSpanMapperGroup>>
>;
export type DeleteSpanMapperGroupMutationError =
ErrorType<RenderErrorResponseDTO>;
/**
* @summary Delete a span attribute mapping group
*/
export const useDeleteSpanMapperGroup = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteSpanMapperGroup>>,
TError,
{ pathParams: DeleteSpanMapperGroupPathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof deleteSpanMapperGroup>>,
TError,
{ pathParams: DeleteSpanMapperGroupPathParameters },
TContext
> => {
const mutationOptions = getDeleteSpanMapperGroupMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Partially updates an existing mapping group's name, condition, or enabled state.
* @summary Update a span attribute mapping group
*/
export const updateSpanMapperGroup = (
{ groupId }: UpdateSpanMapperGroupPathParameters,
spantypesUpdatableSpanMapperGroupDTO: BodyType<SpantypesUpdatableSpanMapperGroupDTO>,
) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/span_mapper_groups/${groupId}`,
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
data: spantypesUpdatableSpanMapperGroupDTO,
});
};
export const getUpdateSpanMapperGroupMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateSpanMapperGroup>>,
TError,
{
pathParams: UpdateSpanMapperGroupPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperGroupDTO>;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof updateSpanMapperGroup>>,
TError,
{
pathParams: UpdateSpanMapperGroupPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperGroupDTO>;
},
TContext
> => {
const mutationKey = ['updateSpanMapperGroup'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof updateSpanMapperGroup>>,
{
pathParams: UpdateSpanMapperGroupPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperGroupDTO>;
}
> = (props) => {
const { pathParams, data } = props ?? {};
return updateSpanMapperGroup(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type UpdateSpanMapperGroupMutationResult = NonNullable<
Awaited<ReturnType<typeof updateSpanMapperGroup>>
>;
export type UpdateSpanMapperGroupMutationBody =
BodyType<SpantypesUpdatableSpanMapperGroupDTO>;
export type UpdateSpanMapperGroupMutationError =
ErrorType<RenderErrorResponseDTO>;
/**
* @summary Update a span attribute mapping group
*/
export const useUpdateSpanMapperGroup = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateSpanMapperGroup>>,
TError,
{
pathParams: UpdateSpanMapperGroupPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperGroupDTO>;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof updateSpanMapperGroup>>,
TError,
{
pathParams: UpdateSpanMapperGroupPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperGroupDTO>;
},
TContext
> => {
const mutationOptions = getUpdateSpanMapperGroupMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Returns all mappers belonging to a mapping group.
* @summary List span mappers for a group
*/
export const listSpanMappers = (
{ groupId }: ListSpanMappersPathParameters,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<ListSpanMappers200>({
url: `/api/v1/span_mapper_groups/${groupId}/span_mappers`,
method: 'GET',
signal,
});
};
export const getListSpanMappersQueryKey = ({
groupId,
}: ListSpanMappersPathParameters) => {
return [`/api/v1/span_mapper_groups/${groupId}/span_mappers`] as const;
};
export const getListSpanMappersQueryOptions = <
TData = Awaited<ReturnType<typeof listSpanMappers>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
{ groupId }: ListSpanMappersPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listSpanMappers>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getListSpanMappersQueryKey({ groupId });
const queryFn: QueryFunction<Awaited<ReturnType<typeof listSpanMappers>>> = ({
signal,
}) => listSpanMappers({ groupId }, signal);
return {
queryKey,
queryFn,
enabled: !!groupId,
...queryOptions,
} as UseQueryOptions<
Awaited<ReturnType<typeof listSpanMappers>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ListSpanMappersQueryResult = NonNullable<
Awaited<ReturnType<typeof listSpanMappers>>
>;
export type ListSpanMappersQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary List span mappers for a group
*/
export function useListSpanMappers<
TData = Awaited<ReturnType<typeof listSpanMappers>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
{ groupId }: ListSpanMappersPathParameters,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listSpanMappers>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getListSpanMappersQueryOptions({ groupId }, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
query.queryKey = queryOptions.queryKey;
return query;
}
/**
* @summary List span mappers for a group
*/
export const invalidateListSpanMappers = async (
queryClient: QueryClient,
{ groupId }: ListSpanMappersPathParameters,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getListSpanMappersQueryKey({ groupId }) },
options,
);
return queryClient;
};
/**
* Adds a new mapper to the specified mapping group.
* @summary Create a span mapper
*/
export const createSpanMapper = (
{ groupId }: CreateSpanMapperPathParameters,
spantypesPostableSpanMapperDTO: BodyType<SpantypesPostableSpanMapperDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<CreateSpanMapper201>({
url: `/api/v1/span_mapper_groups/${groupId}/span_mappers`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: spantypesPostableSpanMapperDTO,
signal,
});
};
export const getCreateSpanMapperMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createSpanMapper>>,
TError,
{
pathParams: CreateSpanMapperPathParameters;
data: BodyType<SpantypesPostableSpanMapperDTO>;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof createSpanMapper>>,
TError,
{
pathParams: CreateSpanMapperPathParameters;
data: BodyType<SpantypesPostableSpanMapperDTO>;
},
TContext
> => {
const mutationKey = ['createSpanMapper'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof createSpanMapper>>,
{
pathParams: CreateSpanMapperPathParameters;
data: BodyType<SpantypesPostableSpanMapperDTO>;
}
> = (props) => {
const { pathParams, data } = props ?? {};
return createSpanMapper(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type CreateSpanMapperMutationResult = NonNullable<
Awaited<ReturnType<typeof createSpanMapper>>
>;
export type CreateSpanMapperMutationBody =
BodyType<SpantypesPostableSpanMapperDTO>;
export type CreateSpanMapperMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Create a span mapper
*/
export const useCreateSpanMapper = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof createSpanMapper>>,
TError,
{
pathParams: CreateSpanMapperPathParameters;
data: BodyType<SpantypesPostableSpanMapperDTO>;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof createSpanMapper>>,
TError,
{
pathParams: CreateSpanMapperPathParameters;
data: BodyType<SpantypesPostableSpanMapperDTO>;
},
TContext
> => {
const mutationOptions = getCreateSpanMapperMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Hard-deletes a mapper from a mapping group.
* @summary Delete a span mapper
*/
export const deleteSpanMapper = ({
groupId,
mapperId,
}: DeleteSpanMapperPathParameters) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/span_mapper_groups/${groupId}/span_mappers/${mapperId}`,
method: 'DELETE',
});
};
export const getDeleteSpanMapperMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteSpanMapper>>,
TError,
{ pathParams: DeleteSpanMapperPathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof deleteSpanMapper>>,
TError,
{ pathParams: DeleteSpanMapperPathParameters },
TContext
> => {
const mutationKey = ['deleteSpanMapper'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof deleteSpanMapper>>,
{ pathParams: DeleteSpanMapperPathParameters }
> = (props) => {
const { pathParams } = props ?? {};
return deleteSpanMapper(pathParams);
};
return { mutationFn, ...mutationOptions };
};
export type DeleteSpanMapperMutationResult = NonNullable<
Awaited<ReturnType<typeof deleteSpanMapper>>
>;
export type DeleteSpanMapperMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Delete a span mapper
*/
export const useDeleteSpanMapper = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof deleteSpanMapper>>,
TError,
{ pathParams: DeleteSpanMapperPathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof deleteSpanMapper>>,
TError,
{ pathParams: DeleteSpanMapperPathParameters },
TContext
> => {
const mutationOptions = getDeleteSpanMapperMutationOptions(options);
return useMutation(mutationOptions);
};
/**
* Partially updates an existing mapper's field context, config, or enabled state.
* @summary Update a span mapper
*/
export const updateSpanMapper = (
{ groupId, mapperId }: UpdateSpanMapperPathParameters,
spantypesUpdatableSpanMapperDTO: BodyType<SpantypesUpdatableSpanMapperDTO>,
) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/span_mapper_groups/${groupId}/span_mappers/${mapperId}`,
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
data: spantypesUpdatableSpanMapperDTO,
});
};
export const getUpdateSpanMapperMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateSpanMapper>>,
TError,
{
pathParams: UpdateSpanMapperPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperDTO>;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof updateSpanMapper>>,
TError,
{
pathParams: UpdateSpanMapperPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperDTO>;
},
TContext
> => {
const mutationKey = ['updateSpanMapper'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof updateSpanMapper>>,
{
pathParams: UpdateSpanMapperPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperDTO>;
}
> = (props) => {
const { pathParams, data } = props ?? {};
return updateSpanMapper(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type UpdateSpanMapperMutationResult = NonNullable<
Awaited<ReturnType<typeof updateSpanMapper>>
>;
export type UpdateSpanMapperMutationBody =
BodyType<SpantypesUpdatableSpanMapperDTO>;
export type UpdateSpanMapperMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Update a span mapper
*/
export const useUpdateSpanMapper = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateSpanMapper>>,
TError,
{
pathParams: UpdateSpanMapperPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperDTO>;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof updateSpanMapper>>,
TError,
{
pathParams: UpdateSpanMapperPathParameters;
data: BodyType<SpantypesUpdatableSpanMapperDTO>;
},
TContext
> => {
const mutationOptions = getUpdateSpanMapperMutationOptions(options);
return useMutation(mutationOptions);
};

View File

@@ -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,7 +75,7 @@ describe('interceptorRejected', () => {
// Expected to reject after retry
}
const mockAxiosFn = (axios as unknown) as jest.Mock;
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);
@@ -85,7 +85,7 @@ describe('interceptorRejected', () => {
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;
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)).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,7 +144,7 @@ describe('interceptorRejected', () => {
// Expected to reject after retry
}
const mockAxiosFn = (axios as unknown) as jest.Mock;
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();

View File

@@ -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,

View File

@@ -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);
@@ -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,17 +172,15 @@ 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);
@@ -254,10 +250,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, true);

View File

@@ -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') {

View File

@@ -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: [],
@@ -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: [
{
@@ -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: [],

View File

@@ -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,7 +231,8 @@ 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];
@@ -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(
@@ -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 };

View File

@@ -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

View File

@@ -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

View File

@@ -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';

View File

@@ -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();

View File

@@ -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>();

View File

@@ -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();

View File

@@ -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',
},

View File

@@ -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],
);

View File

@@ -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" />}

View File

@@ -65,7 +65,7 @@ export function applyCeleryFilterOnWidgetData(
items: [...(queryItem.filters?.items || []), ...filters],
op: queryItem.filters?.op || 'AND',
},
}
}
: queryItem,
),
},

View File

@@ -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>;
}

View File

@@ -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
}

View File

@@ -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>,

View File

@@ -388,7 +388,7 @@ function ClientSideQBSearch(
({
label: key.key,
value: key,
} as Option),
}) as Option,
) || [],
);
}
@@ -462,7 +462,7 @@ function ClientSideQBSearch(
({
label: checkCommaInValue(String(val)),
value: val,
} as Option),
}) as Option,
),
);
} else {
@@ -490,7 +490,7 @@ function ClientSideQBSearch(
Array.isArray(tag.value) &&
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,

View File

@@ -47,25 +47,23 @@ 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 () => {
toast.success('Service account created successfully');
reset();
await setIsOpen(null);
await invalidateListServiceAccounts(queryClient);
},
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();

View File

@@ -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>(

View File

@@ -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);
}

View File

@@ -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',

View File

@@ -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;
}

View File

@@ -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(),
};

View File

@@ -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>

View File

@@ -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 {

View File

@@ -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 */}

View File

@@ -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();

View File

@@ -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);

View File

@@ -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',
};

View File

@@ -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);

View File

@@ -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,

View File

@@ -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) => {

View File

@@ -95,7 +95,7 @@ export const getGraphOptions = (
},
],
},
}
}
: {}),
title: {
display: title !== undefined,

View File

@@ -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 {

View File

@@ -47,9 +47,8 @@ function LaunchChatSupport({
featureFlagsFetchError,
isLoggedIn,
} = useAppContext();
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] = useState(
false,
);
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] =
useState(false);
const { pathname } = useLocation();

View File

@@ -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',

View File

@@ -261,7 +261,7 @@ function LogDetailInner({
...query.filter,
expression: value,
},
}
}
: query,
),
},

View File

@@ -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}>

View File

@@ -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],
);

View File

@@ -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>`

View File

@@ -1,6 +1,4 @@
.log-state-indicator {
padding-left: 8px;
.line {
margin: 0 8px;
min-height: 24px;

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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'};

View File

@@ -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;
}

View File

@@ -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;`}
`;

View 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]);
}

View File

@@ -34,9 +34,10 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
const isDarkMode = useIsDarkMode();
const flattenLogData = useMemo(() => logs.map((log) => FlatLogData(log)), [
logs,
]);
const flattenLogData = useMemo(
() => logs.map((log) => FlatLogData(log)),
[logs],
);
const { formatTimezoneAdjustedTimestamp } = useTimezone();
@@ -115,11 +116,11 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
? formatTimezoneAdjustedTimestamp(
field,
DATE_TIME_FORMATS.ISO_DATETIME_MS,
)
)
: formatTimezoneAdjustedTimestamp(
field / 1e6,
DATE_TIME_FORMATS.ISO_DATETIME_MS,
);
);
return {
children: (
<div className="table-timestamp">
@@ -129,7 +130,7 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
};
},
},
]
]
: []),
...(appendTo === 'center' ? fieldColumns : []),
...(fields.some((field) => field.name === 'body')
@@ -160,7 +161,7 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
),
}),
},
]
]
: []),
...(appendTo === 'end' ? fieldColumns : []),
];

View File

@@ -35,13 +35,11 @@ function OptionsMenu({
const [fontSizeValue, setFontSizeValue] = useState<FontSize>(
fontSize?.value || FontSize.SMALL,
);
const [isFontSizeOptionsOpen, setIsFontSizeOptionsOpen] = useState<boolean>(
false,
);
const [isFontSizeOptionsOpen, setIsFontSizeOptionsOpen] =
useState<boolean>(false);
const [showAddNewColumnContainer, setShowAddNewColumnContainer] = useState(
false,
);
const [showAddNewColumnContainer, setShowAddNewColumnContainer] =
useState(false);
const [selectedValue, setSelectedValue] = useState<string | null>(null);
const listRef = useRef<HTMLDivElement>(null);

View File

@@ -260,23 +260,28 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
/**
* Separates section and non-section options
*/
const splitOptions = useCallback((options: OptionData[]): {
sectionOptions: OptionData[];
nonSectionOptions: OptionData[];
} => {
const sectionOptions: OptionData[] = [];
const nonSectionOptions: OptionData[] = [];
const splitOptions = useCallback(
(
options: OptionData[],
): {
sectionOptions: OptionData[];
nonSectionOptions: OptionData[];
} => {
const sectionOptions: OptionData[] = [];
const nonSectionOptions: OptionData[] = [];
options.forEach((option) => {
if ('options' in option && Array.isArray(option.options)) {
sectionOptions.push(option);
} else {
nonSectionOptions.push(option);
}
});
options.forEach((option) => {
if ('options' in option && Array.isArray(option.options)) {
sectionOptions.push(option);
} else {
nonSectionOptions.push(option);
}
});
return { sectionOptions, nonSectionOptions };
}, []);
return { sectionOptions, nonSectionOptions };
},
[],
);
/**
* Apply search filtering to options
@@ -1629,7 +1634,7 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
}}
data={enhancedNonSectionOptions}
itemContent={(index, item): React.ReactNode =>
(mapOptions([item]) as unknown) as React.ReactElement
mapOptions([item]) as unknown as React.ReactElement
}
totalCount={enhancedNonSectionOptions.length}
itemSize={(): number => 40}
@@ -1671,7 +1676,7 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
}}
data={section.options || []}
itemContent={(index, item): React.ReactNode =>
(mapOptions([item]) as unknown) as React.ReactElement
mapOptions([item]) as unknown as React.ReactElement
}
totalCount={section.options?.length || 0}
itemSize={(): number => 40}
@@ -1936,7 +1941,7 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
? {
borderColor: Color.BG_ROBIN_500,
backgroundColor: Color.BG_SLATE_400,
}
}
: undefined
}
>

View File

@@ -112,23 +112,28 @@ const CustomSelect: React.FC<CustomSelectProps> = ({
/**
* Separates section and non-section options
*/
const splitOptions = useCallback((options: OptionData[]): {
sectionOptions: OptionData[];
nonSectionOptions: OptionData[];
} => {
const sectionOptions: OptionData[] = [];
const nonSectionOptions: OptionData[] = [];
const splitOptions = useCallback(
(
options: OptionData[],
): {
sectionOptions: OptionData[];
nonSectionOptions: OptionData[];
} => {
const sectionOptions: OptionData[] = [];
const nonSectionOptions: OptionData[] = [];
options.forEach((option) => {
if ('options' in option && Array.isArray(option.options)) {
sectionOptions.push(option);
} else {
nonSectionOptions.push(option);
}
});
options.forEach((option) => {
if ('options' in option && Array.isArray(option.options)) {
sectionOptions.push(option);
} else {
nonSectionOptions.push(option);
}
});
return { sectionOptions, nonSectionOptions };
}, []);
return { sectionOptions, nonSectionOptions };
},
[],
);
/**
* Apply search filtering to options
@@ -322,9 +327,8 @@ const CustomSelect: React.FC<CustomSelectProps> = ({
processedOptions = filterOptionsBySearch(processedOptions, searchText);
}
const { sectionOptions, nonSectionOptions } = splitOptions(
processedOptions,
);
const { sectionOptions, nonSectionOptions } =
splitOptions(processedOptions);
// Add custom option if needed
if (

View File

@@ -336,7 +336,7 @@ describe('CustomMultiSelect Component', () => {
renderWithVirtuoso(
<CustomMultiSelect
options={mockGroupedOptions}
value={('__ALL__' as unknown) as string[]}
value={'__ALL__' as unknown as string[]}
/>,
);

View File

@@ -180,7 +180,9 @@ $custom-border-color: #2c3044;
.custom-multiselect-dropdown-container {
z-index: 1050 !important;
padding: 0;
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.5), 0 6px 16px 0 rgba(0, 0, 0, 0.4),
box-shadow:
0 3px 6px -4px rgba(0, 0, 0, 0.5),
0 6px 16px 0 rgba(0, 0, 0, 0.4),
0 9px 28px 8px rgba(0, 0, 0, 0.3);
background-color: var(--l2-background);
border: 1px solid var(--l1-border);
@@ -804,8 +806,10 @@ $custom-border-color: #2c3044;
.custom-multiselect-dropdown-container {
background-color: var(--l1-background);
border: 1px solid var(--l1-border);
box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05);
box-shadow:
0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 6px 16px 0 rgba(0, 0, 0, 0.08),
0 9px 28px 8px rgba(0, 0, 0, 0.05);
.empty-message {
color: var(--l2-foreground);
@@ -976,7 +980,9 @@ $custom-border-color: #2c3044;
font-weight: 500;
z-index: 2;
pointer-events: none;
transition: opacity 0.2s ease, visibility 0.2s ease;
transition:
opacity 0.2s ease,
visibility 0.2s ease;
.lightMode & {
color: rgba(0, 0, 0, 0.85);

View File

@@ -40,8 +40,10 @@ export interface CustomTagProps {
onClose: () => void;
}
export interface CustomMultiSelectProps
extends Omit<SelectProps<string[] | string>, 'options'> {
export interface CustomMultiSelectProps extends Omit<
SelectProps<string[] | string>,
'options'
> {
placeholder?: string;
className?: string;
loading?: boolean;

View File

@@ -26,7 +26,7 @@ function OverlayScrollbar({
theme: isDarkMode ? 'os-theme-light' : 'os-theme-dark',
},
...(customOptions || {}),
} as PartialOptions),
}) as PartialOptions,
[customOptions, isDarkMode],
);

View File

@@ -10,8 +10,14 @@
border-bottom: 1px solid var(--l1-border);
border-top: 1px solid var(--l1-border);
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', sans-serif;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
sans-serif;
border-right: none;
border-left: none;

View File

@@ -76,32 +76,34 @@ export const QueryBuilderV2 = memo(function QueryBuilderV2({
[showTraceOperator, isListViewPanel],
);
const listViewLogFilterConfigs: QueryBuilderProps['filterConfigs'] = useMemo(() => {
const config: QueryBuilderProps['filterConfigs'] = {
stepInterval: { isHidden: true, isDisabled: true },
having: { isHidden: true, isDisabled: true },
filters: {
customKey: 'body',
customOp: OPERATORS.CONTAINS,
},
};
const listViewLogFilterConfigs: QueryBuilderProps['filterConfigs'] =
useMemo(() => {
const config: QueryBuilderProps['filterConfigs'] = {
stepInterval: { isHidden: true, isDisabled: true },
having: { isHidden: true, isDisabled: true },
filters: {
customKey: 'body',
customOp: OPERATORS.CONTAINS,
},
};
return config;
}, []);
return config;
}, []);
const listViewTracesFilterConfigs: QueryBuilderProps['filterConfigs'] = useMemo(() => {
const config: QueryBuilderProps['filterConfigs'] = {
stepInterval: { isHidden: true, isDisabled: true },
having: { isHidden: true, isDisabled: true },
limit: { isHidden: true, isDisabled: true },
filters: {
customKey: 'body',
customOp: OPERATORS.CONTAINS,
},
};
const listViewTracesFilterConfigs: QueryBuilderProps['filterConfigs'] =
useMemo(() => {
const config: QueryBuilderProps['filterConfigs'] = {
stepInterval: { isHidden: true, isDisabled: true },
having: { isHidden: true, isDisabled: true },
limit: { isHidden: true, isDisabled: true },
filters: {
customKey: 'body',
customOp: OPERATORS.CONTAINS,
},
};
return config;
}, []);
return config;
}, []);
const queryFilterConfigs = useMemo(() => {
if (isListViewPanel) {

View File

@@ -49,11 +49,8 @@ export const MetricsSelect = memo(function MetricsSelect({
entityVersion: version,
});
const {
updateAllQueriesOperators,
handleSetQueryData,
panelType,
} = useQueryBuilder();
const { updateAllQueriesOperators, handleSetQueryData, panelType } =
useQueryBuilder();
const source = useMemo(
() => (signalSource === 'meter' ? 'meter' : 'metrics'),
@@ -123,9 +120,8 @@ export const MetricsSelect = memo(function MetricsSelect({
signalSource: newSignalSource,
panelType: panelType || '',
});
const savedQuery: IBuilderQuery | null = getPreviousQueryFromKey(
newQueryKey,
);
const savedQuery: IBuilderQuery | null =
getPreviousQueryFromKey(newQueryKey);
// remove the new query key from session storage
removeKeyFromPreviousQuery(newQueryKey);

View File

@@ -464,36 +464,37 @@ function QueryAddOns({
</div>
)}
{selectedViews.find((view) => view.key === 'reduce_to') && showReduceTo && (
<div className="add-on-content" data-testid="reduce-to-content">
<div className="periscope-input-with-label">
<Tooltip
title={
<TooltipContent
label="Reduce to"
description="Apply mathematical operations like sum, average, min, max, or percentiles to reduce multiple time series into a single value."
docLink="https://signoz.io/docs/userguide/query-builder-v5/#reduce-operations"
/>
}
placement="top"
mouseEnterDelay={0.5}
>
<div className="label" style={{ cursor: 'help' }}>
Reduce to
{selectedViews.find((view) => view.key === 'reduce_to') &&
showReduceTo && (
<div className="add-on-content" data-testid="reduce-to-content">
<div className="periscope-input-with-label">
<Tooltip
title={
<TooltipContent
label="Reduce to"
description="Apply mathematical operations like sum, average, min, max, or percentiles to reduce multiple time series into a single value."
docLink="https://signoz.io/docs/userguide/query-builder-v5/#reduce-operations"
/>
}
placement="top"
mouseEnterDelay={0.5}
>
<div className="label" style={{ cursor: 'help' }}>
Reduce to
</div>
</Tooltip>
<div className="input">
<ReduceToFilter query={query} onChange={handleChangeReduceToV5} />
</div>
</Tooltip>
<div className="input">
<ReduceToFilter query={query} onChange={handleChangeReduceToV5} />
</div>
<Button
className="close-btn periscope-btn ghost"
icon={<ChevronUp size={16} />}
onClick={(): void => handleRemoveView('reduce_to')}
/>
<Button
className="close-btn periscope-btn ghost"
icon={<ChevronUp size={16} />}
onClick={(): void => handleRemoveView('reduce_to')}
/>
</div>
</div>
</div>
)}
)}
{selectedViews.find((view) => view.key === 'legend_format') && (
<div className="add-on-content" data-testid="legend-format-content">

View File

@@ -290,12 +290,12 @@ function QueryAggregationSelect({
}
const regex = /([a-zA-Z_][\w]*)\s*\(([^)]*)\)/g;
const oldMatches = [
...tr.startState.doc.toString().matchAll(regex),
].filter((match) => validFunctions.includes(match[1].toLowerCase()));
const newMatches = [
...tr.newDoc.toString().matchAll(regex),
].filter((match) => validFunctions.includes(match[1].toLowerCase()));
const oldMatches = [...tr.startState.doc.toString().matchAll(regex)].filter(
(match) => validFunctions.includes(match[1].toLowerCase()),
);
const newMatches = [...tr.newDoc.toString().matchAll(regex)].filter(
(match) => validFunctions.includes(match[1].toLowerCase()),
);
if (
newMatches.length > oldMatches.length &&
@@ -558,7 +558,7 @@ function QueryAggregationSelect({
? availableSuggestions
: availableSuggestions.filter((suggestion) =>
suggestion.label.toLowerCase().includes(inputText.toLowerCase()),
);
);
return {
from: startOfArg,

View File

@@ -5,8 +5,14 @@
display: flex;
flex-direction: column;
gap: 8px;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
'Helvetica Neue', sans-serif;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
sans-serif;
.query-where-clause-editor-container {
position: relative;

View File

@@ -194,10 +194,8 @@ function QuerySearch({
const [cursorPos, setCursorPos] = useState({ line: 0, ch: 0 });
const [
isFetchingCompleteValuesList,
setIsFetchingCompleteValuesList,
] = useState<boolean>(false);
const [isFetchingCompleteValuesList, setIsFetchingCompleteValuesList] =
useState<boolean>(false);
const lastPosRef = useRef<{ line: number; ch: number }>({ line: 0, ch: 0 });

View File

@@ -93,9 +93,10 @@ export const QueryV2 = forwardRef(function QueryV2(
[dataSource, panelType],
);
const showSpanScopeSelector = useMemo(() => dataSource === DataSource.TRACES, [
dataSource,
]);
const showSpanScopeSelector = useMemo(
() => dataSource === DataSource.TRACES,
[dataSource],
);
const showInlineQuerySearch = useMemo(() => {
if (!showTraceOperator) {
@@ -212,7 +213,7 @@ export const QueryV2 = forwardRef(function QueryV2(
icon: <Trash size={14} />,
onClick: handleDeleteQuery,
},
]
]
: []),
],
}}

View File

@@ -3,7 +3,7 @@ import { IBuilderTraceOperator } from 'types/api/queryBuilder/queryBuilderData';
import { getInvolvedQueriesInTraceOperator } from '../utils/utils';
const makeTraceOperator = (expression: string): IBuilderTraceOperator =>
(({ expression } as unknown) as IBuilderTraceOperator);
({ expression }) as unknown as IBuilderTraceOperator;
describe('getInvolvedQueriesInTraceOperator', () => {
it('returns empty array for empty input', () => {

View File

@@ -98,9 +98,7 @@ export function createTraceOperatorContext(
}
// Helper to determine token context
function determineTraceTokenContext(
token: IToken,
): {
function determineTraceTokenContext(token: IToken): {
isInAtom: boolean;
isInOperator: boolean;
isInParenthesis: boolean;

View File

@@ -36,14 +36,14 @@ beforeAll(() => {
const mockRange = {
// CodeMirror uses these for text measurement
getClientRects: (): DOMRectList =>
(({
({
length: 1,
item: (index: number): DOMRect | null => (index === 0 ? mockRect : null),
0: mockRect,
*[Symbol.iterator](): Generator<DOMRect> {
yield mockRect;
},
} as unknown) as DOMRectList),
}) as unknown as DOMRectList,
getBoundingClientRect: (): DOMRect => mockRect,
// CodeMirror calls these to set up text ranges
setStart: (node: Node, offset: number): void => {
@@ -72,7 +72,7 @@ beforeAll(() => {
},
commonAncestorContainer: document.body,
};
return (mockRange as unknown) as Range;
return mockRange as unknown as Range;
};
// Mock document.createRange to return a new Range instance each time

View File

@@ -94,13 +94,12 @@ describe('QueryBuilderV2 + QueryV2 - base render', () => {
},
};
const updateAllQueriesOperators: QueryBuilderContextType['updateAllQueriesOperators'] = (
q,
) => q;
const updateAllQueriesOperators: QueryBuilderContextType['updateAllQueriesOperators'] =
(q) => q;
const updateQueriesData: QueryBuilderContextType['updateQueriesData'] = (q) =>
q;
const baseContext = ({
const baseContext = {
currentQuery: currentQueryObj,
stagedQuery: null,
lastUsedQuery: null,
@@ -133,7 +132,7 @@ describe('QueryBuilderV2 + QueryV2 - base render', () => {
initQueryBuilderData: jest.fn(),
isStagedQueryUpdated: jest.fn(() => false),
isDefaultQuery: jest.fn(() => false),
} as unknown) as QueryBuilderContextType;
} as unknown as QueryBuilderContextType;
baseQBContext = baseContext;
mockedUseQueryBuilder.mockReturnValue(baseQBContext);
@@ -149,7 +148,8 @@ describe('QueryBuilderV2 + QueryV2 - base render', () => {
handleChangeAggregatorAttribute: jest.fn(),
handleChangeDataSource: jest.fn(),
handleDeleteQuery: jest.fn(),
handleChangeQueryData: (jest.fn() as unknown) as ReturnType<UseQueryOperations>['handleChangeQueryData'],
handleChangeQueryData:
jest.fn() as unknown as ReturnType<UseQueryOperations>['handleChangeQueryData'],
handleChangeFormulaData: jest.fn(),
handleQueryFunctionsUpdates: handleQueryFunctionsUpdatesMock,
listOfAdditionalFormulaFilters: [],

View File

@@ -57,7 +57,7 @@ describe('MetricsSelect - signal source switching (standalone)', () => {
beforeEach(() => {
clearPreviousQuery();
handleSetQueryDataMock = (jest.fn() as unknown) as jest.MockedFunction<
handleSetQueryDataMock = jest.fn() as unknown as jest.MockedFunction<
(index: number, q: IBuilderQuery) => void
>;
@@ -222,7 +222,7 @@ describe('DataSource change - Logs to Traces', () => {
beforeEach(() => {
clearPreviousQuery();
handleSetQueryDataMock = (jest.fn() as unknown) as jest.MockedFunction<
handleSetQueryDataMock = jest.fn() as unknown as jest.MockedFunction<
(i: number, q: IBuilderQuery) => void
>;

View File

@@ -947,7 +947,7 @@ describe('convertAggregationToExpression', () => {
it('should handle undefined aggregateAttribute parameter with traces', () => {
const result = convertAggregationToExpression({
aggregateOperator: 'noop',
aggregateAttribute: (undefined as unknown) as BaseAutocompleteData,
aggregateAttribute: undefined as unknown as BaseAutocompleteData,
dataSource: DataSource.TRACES,
});
@@ -961,7 +961,7 @@ describe('convertAggregationToExpression', () => {
it('should handle undefined aggregateAttribute parameter with logs', () => {
const result = convertAggregationToExpression({
aggregateOperator: 'noop',
aggregateAttribute: (undefined as unknown) as BaseAutocompleteData,
aggregateAttribute: undefined as unknown as BaseAutocompleteData,
dataSource: DataSource.LOGS,
});

View File

@@ -210,8 +210,8 @@ export const convertExpressionToFilters = (
type: '',
},
value: pair.isMultiValue
? formatValuesForFilter(pair.valueList as string[]) ?? []
: formatValuesForFilter(pair.value as string) ?? '',
? (formatValuesForFilter(pair.valueList as string[]) ?? [])
: (formatValuesForFilter(pair.value as string) ?? ''),
});
});
@@ -469,8 +469,8 @@ export const convertFiltersToExpressionWithExistingQuery = (
type: '',
},
value: pair.isMultiValue
? formatValuesForFilter(pair.valueList as string[]) ?? ''
: formatValuesForFilter(pair.value as string) ?? '',
? (formatValuesForFilter(pair.valueList as string[]) ?? '')
: (formatValuesForFilter(pair.value as string) ?? ''),
});
}
});
@@ -554,7 +554,7 @@ export const removeKeysFromExpression = (
}
const value = pair.value?.toString().trim();
return value && value.includes('$');
})
})
: existingQueryPairs;
// Build a map for quick lookup of query pairs by their lowercase trimmed keys
@@ -744,15 +744,18 @@ export function getQueryLabelWithAggregation(
const labels: { label: string; value: string }[] = [];
const aggregationPerQuery =
queryData.reduce((acc, query) => {
if (query.queryName && query.aggregations?.length) {
acc[query.queryName] = createAggregation(query).map((a: any) => ({
alias: a.alias,
expression: a.expression,
}));
}
return acc;
}, {} as Record<string, any>) || {};
queryData.reduce(
(acc, query) => {
if (query.queryName && query.aggregations?.length) {
acc[query.queryName] = createAggregation(query).map((a: any) => ({
alias: a.alias,
expression: a.expression,
}));
}
return acc;
},
{} as Record<string, any>,
) || {};
Object.entries(aggregationPerQuery).forEach(([queryName, aggregations]) => {
const isMultipleAggregations = aggregations.length > 1;

View File

@@ -83,7 +83,7 @@ const createMockQueryBuilderData = (hasActiveFilters = false): any => ({
op: 'in',
value: [OTEL_DEMO, SAMPLE_FLASK],
},
]
]
: [],
},
},
@@ -99,7 +99,7 @@ describe('CheckboxFilter - User Flows', () => {
jest.clearAllMocks();
// Default mock implementations for useGetAggregateValues
mockUseGetAggregateValues.mockReturnValue(({
mockUseGetAggregateValues.mockReturnValue({
data: {
payload: {
stringAttributeValues: MOCK_SERVICE_NAMES,
@@ -107,7 +107,7 @@ describe('CheckboxFilter - User Flows', () => {
},
isLoading: false,
refetch: jest.fn(),
} as unknown) as UseQueryResult<SuccessResponse<IAttributeValuesResponse>>);
} as unknown as UseQueryResult<SuccessResponse<IAttributeValuesResponse>>);
// Default mock implementations for useGetQueryKeyValueSuggestions
// Returns data in the format expected by the hook

View File

@@ -82,10 +82,8 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
// Check if this filter has active filters in the query
const isSomeFilterPresentForCurrentAttribute = useMemo(
() =>
currentQuery.builder.queryData?.[
activeQueryIndex
]?.filters?.items?.some((item) =>
isKeyMatch(item.key?.key, filter.attributeKey.key),
currentQuery.builder.queryData?.[activeQueryIndex]?.filters?.items?.some(
(item) => isKeyMatch(item.key?.key, filter.attributeKey.key),
),
[currentQuery.builder.queryData, activeQueryIndex, filter.attributeKey.key],
);
@@ -126,18 +124,16 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
},
);
const {
data: keyValueSuggestions,
isLoading: isLoadingKeyValueSuggestions,
} = useGetQueryKeyValueSuggestions({
key: filter.attributeKey.key,
signal: filter.dataSource || DataSource.LOGS,
signalSource: 'meter',
options: {
enabled: isOpen && source === QuickFiltersSource.METER_EXPLORER,
keepPreviousData: true,
},
});
const { data: keyValueSuggestions, isLoading: isLoadingKeyValueSuggestions } =
useGetQueryKeyValueSuggestions({
key: filter.attributeKey.key,
signal: filter.dataSource || DataSource.LOGS,
signalSource: 'meter',
options: {
enabled: isOpen && source === QuickFiltersSource.METER_EXPLORER,
keepPreviousData: true,
},
});
const attributeValues: string[] = useMemo(() => {
const dataType = filter.attributeKey.dataType || DataTypes.String;
@@ -282,7 +278,7 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
idx === activeQueryIndex
? item.filters?.items?.filter(
(fil) => !isKeyMatch(fil.key?.key, filter.attributeKey.key),
) || []
) || []
: [...(item.filters?.items || [])],
op: item.filters?.op || 'AND',
},

View File

@@ -36,12 +36,13 @@ function Duration({
onFilterChange?: (query: Query) => void;
source?: QuickFiltersSource;
}): JSX.Element {
const [selectedFilters, setSelectedFilters] = useState<
Record<
AllTraceFilterKeys,
{ values: string[] | string; keys: BaseAutocompleteData }
>
>();
const [selectedFilters, setSelectedFilters] =
useState<
Record<
AllTraceFilterKeys,
{ values: string[] | string; keys: BaseAutocompleteData }
>
>();
const [activeKeys, setActiveKeys] = useState<string[]>([
filter.defaultOpen ? 'durationNano' : '',
]);

View File

@@ -30,13 +30,8 @@ function SortableFilter({
allowDrag: boolean;
allowRemove: boolean;
}): JSX.Element {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
} = useSortable({ id: filter.key });
const { attributes, listeners, setNodeRef, transform, transition } =
useSortable({ id: filter.key });
const style = {
transform: CSS.Transform.toString(transform),

View File

@@ -48,52 +48,46 @@ function OtherFilters({
[signal],
);
const {
data: suggestionsData,
isFetching: isFetchingSuggestions,
} = useGetAttributeSuggestions(
{
searchText: inputValue,
dataSource: SIGNAL_DATA_SOURCE_MAP[signal as SignalType],
filters: {} as TagFilter,
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && isLogDataSource,
},
);
const { data: suggestionsData, isFetching: isFetchingSuggestions } =
useGetAttributeSuggestions(
{
searchText: inputValue,
dataSource: SIGNAL_DATA_SOURCE_MAP[signal as SignalType],
filters: {} as TagFilter,
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && isLogDataSource,
},
);
const {
data: aggregateKeysData,
isFetching: isFetchingAggregateKeys,
} = useGetAggregateKeys(
{
searchText: inputValue,
dataSource: SIGNAL_DATA_SOURCE_MAP[signal as SignalType],
aggregateOperator: 'noop',
aggregateAttribute: '',
tagType: '',
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && !isLogDataSource && !isMeterDataSource,
},
);
const { data: aggregateKeysData, isFetching: isFetchingAggregateKeys } =
useGetAggregateKeys(
{
searchText: inputValue,
dataSource: SIGNAL_DATA_SOURCE_MAP[signal as SignalType],
aggregateOperator: 'noop',
aggregateAttribute: '',
tagType: '',
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && !isLogDataSource && !isMeterDataSource,
},
);
const {
data: fieldKeysData,
isLoading: isLoadingFieldKeys,
} = useGetQueryKeySuggestions(
{
searchText: inputValue,
signal: SIGNAL_DATA_SOURCE_MAP[signal as SignalType],
signalSource: 'meter',
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && isMeterDataSource,
},
);
const { data: fieldKeysData, isLoading: isLoadingFieldKeys } =
useGetQueryKeySuggestions(
{
searchText: inputValue,
signal: SIGNAL_DATA_SOURCE_MAP[signal as SignalType],
signalSource: 'meter',
},
{
queryKey: [REACT_QUERY_KEY.GET_OTHER_FILTERS, inputValue],
enabled: !!signal && isMeterDataSource,
},
);
const otherFilters = useMemo(() => {
let filterAttributes;
@@ -110,7 +104,7 @@ function OtherFilters({
dataType: attr.fieldDataType,
type: attr.fieldContext,
signal: attr.signal,
} as BaseAutocompleteData),
}) as BaseAutocompleteData,
);
} else {
filterAttributes = aggregateKeysData?.payload?.attributeKeys || [];

View File

@@ -40,28 +40,26 @@ const useQuickFilterSettings = ({
const [addedFilters, setAddedFilters] = useState<FilterType[]>(customFilters);
const { notifications } = useNotifications();
const {
mutate: updateCustomFilters,
isLoading: isUpdatingCustomFilters,
} = useMutation(updateCustomFiltersAPI, {
onSuccess: () => {
setIsSettingsOpen(false);
refetchCustomFilters();
logEvent('Quick Filters Settings: changes saved', {
addedFilters,
});
notifications.success({
message: 'Quick filters updated successfully',
placement: 'bottomRight',
});
},
onError: (error: AxiosError) => {
notifications.error({
message: axios.isAxiosError(error) ? error.message : SOMETHING_WENT_WRONG,
placement: 'bottomRight',
});
},
});
const { mutate: updateCustomFilters, isLoading: isUpdatingCustomFilters } =
useMutation(updateCustomFiltersAPI, {
onSuccess: () => {
setIsSettingsOpen(false);
refetchCustomFilters();
logEvent('Quick Filters Settings: changes saved', {
addedFilters,
});
notifications.success({
message: 'Quick filters updated successfully',
placement: 'bottomRight',
});
},
onError: (error: AxiosError) => {
notifications.error({
message: axios.isAxiosError(error) ? error.message : SOMETHING_WENT_WRONG,
placement: 'bottomRight',
});
},
});
const debouncedUpdate = useDebouncedFn((value) => {
setDebouncedInputValue(value as string);
}, 400);

View File

@@ -38,9 +38,10 @@ const useFilterConfig = ({
},
);
const isDynamicFilters = useMemo(() => customFilters.length > 0, [
customFilters,
]);
const isDynamicFilters = useMemo(
() => customFilters.length > 0,
[customFilters],
);
const filterConfig = useMemo(
() => getFilterConfig(signal, customFilters, config),

View File

@@ -55,6 +55,6 @@ export const getFilterConfig = (
type: att.type,
},
defaultOpen: index < 2,
} as IQuickFiltersConfig),
}) as IQuickFiltersConfig,
);
};

View File

@@ -52,39 +52,38 @@ function DynamicColumnTable({
...prevColumns.slice(0, prevColumns.length - 1),
...visibleColumns,
prevColumns[prevColumns.length - 1],
]
]
: undefined,
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [columns, dynamicColumns]);
const onToggleHandler = (
index: number,
column: ColumnGroupType<any> | ColumnType<any>,
) => (checked: boolean, event: React.MouseEvent<HTMLButtonElement>): void => {
event.stopPropagation();
const onToggleHandler =
(index: number, column: ColumnGroupType<any> | ColumnType<any>) =>
(checked: boolean, event: React.MouseEvent<HTMLButtonElement>): void => {
event.stopPropagation();
if (shouldSendAlertsLogEvent) {
logEvent('Alert: Column toggled', {
column: column?.title,
action: checked ? 'Enable' : 'Disable',
});
}
setVisibleColumns({
tablesource,
dynamicColumns,
index,
checked,
});
setColumnsData((prevColumns) =>
getNewColumnData({
checked,
index,
prevColumns,
if (shouldSendAlertsLogEvent) {
logEvent('Alert: Column toggled', {
column: column?.title,
action: checked ? 'Enable' : 'Disable',
});
}
setVisibleColumns({
tablesource,
dynamicColumns,
}),
);
};
index,
checked,
});
setColumnsData((prevColumns) =>
getNewColumnData({
checked,
index,
prevColumns,
dynamicColumns,
}),
);
};
const items: MenuProps['items'] =
dynamicColumns?.map((column, index) => ({

View File

@@ -45,20 +45,18 @@ function ResizeTable({
}, [onColumnWidthsChange]);
const handleResize = useCallback(
(index: number) => (
e: SyntheticEvent<Element>,
{ size }: ResizeCallbackData,
): void => {
e.preventDefault();
e.stopPropagation();
(index: number) =>
(e: SyntheticEvent<Element>, { size }: ResizeCallbackData): void => {
e.preventDefault();
e.stopPropagation();
const newColumns = [...columnsData];
newColumns[index] = {
...newColumns[index],
width: size.width,
};
setColumns(newColumns);
},
const newColumns = [...columnsData];
newColumns[index] = {
...newColumns[index],
width: size.width,
};
setColumns(newColumns);
},
[columnsData],
);

View File

@@ -27,7 +27,7 @@ export interface ResizeTableProps extends TableProps<any> {
onColumnWidthsChange?: (widths: ColumnWidths) => void;
}
export interface DynamicColumnTableProps extends TableProps<any> {
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
tablesource: (typeof TableDataSource)[keyof typeof TableDataSource];
dynamicColumns: TableProps<any>['columns'];
onDragColumn?: (fromIndex: number, toIndex: number) => void;
facingIssueBtn?: LaunchChatSupportProps;
@@ -40,7 +40,7 @@ export type GetVisibleColumnsFunction = (
) => (ColumnGroupType<any> | ColumnType<any>)[];
export type GetVisibleColumnProps = {
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
tablesource: (typeof TableDataSource)[keyof typeof TableDataSource];
dynamicColumns?: ColumnsType<any>;
columnsData?: ColumnsType;
};
@@ -48,7 +48,7 @@ export type GetVisibleColumnProps = {
export type SetVisibleColumnsProps = {
checked: boolean;
index: number;
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
tablesource: (typeof TableDataSource)[keyof typeof TableDataSource];
dynamicColumns?: ColumnsType<any>;
};

View File

@@ -71,7 +71,7 @@ export const getNewColumnData: GetNewColumnDataFunction = ({
...prevColumns.slice(0, prevColumns.length - 1),
dynamicColumns[index],
prevColumns[prevColumns.length - 1],
]
]
: undefined;
}
return prevColumns && dynamicColumns

View File

@@ -37,10 +37,8 @@ function AddKeyModal(): JSX.Element {
const open = isAddKeyOpen && !!accountId;
const [phase, setPhase] = useState<Phase>(Phase.FORM);
const [
createdKey,
setCreatedKey,
] = useState<ServiceaccounttypesGettableFactorAPIKeyWithKeyDTO | null>(null);
const [createdKey, setCreatedKey] =
useState<ServiceaccounttypesGettableFactorAPIKeyWithKeyDTO | null>(null);
const [hasCopied, setHasCopied] = useState(false);
const {
@@ -67,30 +65,28 @@ function AddKeyModal(): JSX.Element {
}
}, [open, reset]);
const {
mutate: createKey,
isLoading: isSubmitting,
} = useCreateServiceAccountKey({
mutation: {
onSuccess: async (response) => {
const keyData = response?.data;
if (keyData) {
setCreatedKey(keyData);
setPhase(Phase.CREATED);
if (accountId) {
await invalidateListServiceAccountKeys(queryClient, { id: accountId });
const { mutate: createKey, isLoading: isSubmitting } =
useCreateServiceAccountKey({
mutation: {
onSuccess: async (response) => {
const keyData = response?.data;
if (keyData) {
setCreatedKey(keyData);
setPhase(Phase.CREATED);
if (accountId) {
await invalidateListServiceAccountKeys(queryClient, { id: accountId });
}
}
}
},
onError: (error) => {
showErrorModal(
convertToApiError(
error as AxiosError<RenderErrorResponseDTO, unknown> | null,
) as APIError,
);
},
},
onError: (error) => {
showErrorModal(
convertToApiError(
error as AxiosError<RenderErrorResponseDTO, unknown> | null,
) as APIError,
);
},
},
});
});
function handleCreate({
keyName,

View File

@@ -30,30 +30,28 @@ function DeleteAccountModal(): JSX.Element {
const cachedAccount = accountId
? queryClient.getQueryData<{
data: ServiceaccounttypesServiceAccountDTO;
}>(getGetServiceAccountQueryKey({ id: accountId }))
}>(getGetServiceAccountQueryKey({ id: accountId }))
: null;
const accountName = cachedAccount?.data?.name;
const {
mutate: deleteAccount,
isLoading: isDeleting,
} = useDeleteServiceAccount({
mutation: {
onSuccess: async () => {
toast.success('Service account deleted');
await setIsDeleteOpen(null);
await setAccountId(null);
await invalidateListServiceAccounts(queryClient);
const { mutate: deleteAccount, isLoading: isDeleting } =
useDeleteServiceAccount({
mutation: {
onSuccess: async () => {
toast.success('Service account deleted');
await setIsDeleteOpen(null);
await setAccountId(null);
await invalidateListServiceAccounts(queryClient);
},
onError: (error) => {
showErrorModal(
convertToApiError(
error as AxiosError<RenderErrorResponseDTO, unknown> | null,
) as APIError,
);
},
},
onError: (error) => {
showErrorModal(
convertToApiError(
error as AxiosError<RenderErrorResponseDTO, unknown> | null,
) as APIError,
);
},
},
});
});
function handleConfirm(): void {
if (!accountId) {

View File

@@ -89,31 +89,29 @@ function EditKeyModal({ keyItem }: EditKeyModalProps): JSX.Element {
},
});
const {
mutate: revokeKey,
isLoading: isRevoking,
} = useRevokeServiceAccountKey({
mutation: {
onSuccess: async () => {
toast.success('Key revoked successfully');
setIsRevokeConfirmOpen(false);
await setEditKeyId(null);
if (selectedAccountId) {
await invalidateListServiceAccountKeys(queryClient, {
id: selectedAccountId,
});
}
const { mutate: revokeKey, isLoading: isRevoking } =
useRevokeServiceAccountKey({
mutation: {
onSuccess: async () => {
toast.success('Key revoked successfully');
setIsRevokeConfirmOpen(false);
await setEditKeyId(null);
if (selectedAccountId) {
await invalidateListServiceAccountKeys(queryClient, {
id: selectedAccountId,
});
}
},
// eslint-disable-next-line sonarjs/no-identical-functions
onError: (error) => {
showErrorModal(
convertToApiError(
error as AxiosError<RenderErrorResponseDTO, unknown> | null,
) as APIError,
);
},
},
// eslint-disable-next-line sonarjs/no-identical-functions
onError: (error) => {
showErrorModal(
convertToApiError(
error as AxiosError<RenderErrorResponseDTO, unknown> | null,
) as APIError,
);
},
},
});
});
function handleClose(): void {
setEditKeyId(null);

View File

@@ -67,31 +67,29 @@ function RevokeKeyModal(): JSX.Element {
const cachedKeys = accountId
? queryClient.getQueryData<{
data: ServiceaccounttypesGettableFactorAPIKeyDTO[];
}>(getListServiceAccountKeysQueryKey({ id: accountId }))
}>(getListServiceAccountKeysQueryKey({ id: accountId }))
: null;
const keyName = cachedKeys?.data?.find((k) => k.id === revokeKeyId)?.name;
const {
mutate: revokeKey,
isLoading: isRevoking,
} = useRevokeServiceAccountKey({
mutation: {
onSuccess: async () => {
toast.success('Key revoked successfully');
await setRevokeKeyId(null);
if (accountId) {
await invalidateListServiceAccountKeys(queryClient, { id: accountId });
}
const { mutate: revokeKey, isLoading: isRevoking } =
useRevokeServiceAccountKey({
mutation: {
onSuccess: async () => {
toast.success('Key revoked successfully');
await setRevokeKeyId(null);
if (accountId) {
await invalidateListServiceAccountKeys(queryClient, { id: accountId });
}
},
onError: (error) => {
showErrorModal(
convertToApiError(
error as AxiosError<RenderErrorResponseDTO, unknown> | null,
) as APIError,
);
},
},
onError: (error) => {
showErrorModal(
convertToApiError(
error as AxiosError<RenderErrorResponseDTO, unknown> | null,
) as APIError,
);
},
},
});
});
function handleConfirm(): void {
if (!revokeKeyId || !accountId) {

View File

@@ -231,21 +231,19 @@ function ServiceAccountDrawer({
}, []);
const makeRoleRetry = useCallback(
(
context: string,
rawRetry: () => Promise<void>,
) => async (): Promise<void> => {
try {
await rawRetry();
setSaveErrors((prev) => prev.filter((e) => e.context !== context));
} 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));
} catch (err) {
setSaveErrors((prev) =>
prev.map((e) =>
e.context === context ? { ...e, apiError: toSaveApiError(err) } : e,
),
);
}
},
[],
);
@@ -302,7 +300,7 @@ function ServiceAccountDrawer({
? updateMutateAsync({
pathParams: { id: account.id },
data: { name: localName },
})
})
: Promise.resolve();
const [nameResult, rolesResult] = await Promise.allSettled([

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