Compare commits

..

1 Commits

Author SHA1 Message Date
Tushar Vats
8e7fcf1ab0 fix(querier): stop query cache growing past its limit
Drop cached buckets overlapping a new one instead of trimming them: the trimmed remainder is out-of-window data that would pile up until the TTL. Delete before set so ristretto evicts on updates, not only on new keys.
2026-10-09 14:00:51 +05:30
1409 changed files with 16342 additions and 46006 deletions

View File

@@ -17,4 +17,3 @@ For the Python integration suite under `tests/`. Setup, running, and suite layou
- **snake_case parametrize ids.** `ids=["fill_gaps", "fill_zero"]`, not camelCase.
- **Name suite files with the two-digit prefix (`NN_*.py`).** `pyproject.toml` restricts collection to `[0-9][0-9]_*.py` (plus the bootstrap `setup.py` / `run.py`) — a file that doesn't match is silently never collected.
- **Always run pytest from `tests/`.** `--import-mode=importlib` is what allows same-basename files across suites (`querier/01_logs.py` vs `rawexportdata/01_logs.py`), but it disables pytest's implicit `sys.path` injection — `import fixtures` only resolves via `pythonpath = ["."]` from that rootdir.
- **After changing any `.py` file, run `make py-fmt` and `make py-lint`** from the repo root and fix what they report.

1
.gitattributes vendored
View File

@@ -1,4 +1,3 @@
*.css linguist-detectable=false
*.stories.mocks.tsx linguist-generated=true
**/__story_mockdata__/** linguist-generated=true
tests/e2e/drivers/** linguist-generated=true

View File

@@ -3,44 +3,16 @@ name: e2eci
on:
pull_request:
types:
- opened
- synchronize
- reopened
- labeled
pull_request_target:
types:
- labeled
merge_group:
types:
- checks_requested
concurrency:
group: e2eci-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
changes:
if: github.event_name != 'merge_group'
runs-on: ubuntu-latest
outputs:
e2e: ${{ steps.changes.outputs.e2e }}
steps:
- name: changes
id: changes
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
LABELED: ${{ contains(github.event.pull_request.labels.*.name, 'safe-to-e2e') }}
run: |
files=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/files" --paginate --jq '.[].filename')
if [ "$LABELED" = true ] || grep -qE '^(frontend|tests/e2e)/' <<< "$files"; then
echo "e2e=true" >> "$GITHUB_OUTPUT"
fi
fmtlint:
needs: changes
if: |
((github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))) && needs.changes.outputs.e2e == 'true'
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))) && contains(github.event.pull_request.labels.*.name, 'safe-to-e2e')
runs-on: ubuntu-latest
steps:
- name: checkout
@@ -69,10 +41,9 @@ jobs:
project:
- chromium
shard: [1, 2, 3, 4]
needs: changes
if: |
((github.event_name == 'pull_request' && ! github.event.pull_request.head.repo.fork && github.event.pull_request.user.login != 'dependabot[bot]' && ! contains(github.event.pull_request.labels.*.name, 'safe-to-test')) ||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))) && needs.changes.outputs.e2e == 'true'
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))) && contains(github.event.pull_request.labels.*.name, 'safe-to-e2e')
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
@@ -181,17 +152,3 @@ jobs:
name: playwright-artifacts-${{ matrix.project }}-${{ matrix.shard }}
path: tests/e2e/artifacts/
retention-days: 5
# Required status check. Fails when e2e was needed but did not pass,
# including fork and dependabot PRs still waiting on safe-to-test.
e2e:
if: always()
needs: [changes, fmtlint, test]
runs-on: ubuntu-latest
steps:
- name: verdict
if: |
github.event_name != 'merge_group' && (needs.changes.result != 'success' ||
(needs.changes.outputs.e2e == 'true' && (needs.fmtlint.result != 'success' || needs.test.result != 'success')))
run: |
echo "::error::e2e did not pass. Fork and dependabot PRs need the safe-to-test label."
exit 1

View File

@@ -60,7 +60,6 @@ jobs:
- rawexportdata
- promqlconformance
- promapiconformance
- promote
- querierauthz
- role
- rootuser

View File

@@ -104,7 +104,6 @@ func runGenerateAuthz(_ context.Context) error {
coretypes.NewResourceRef(coretypes.ResourceTelemetryResourceMetrics).String(): true,
coretypes.NewResourceRef(coretypes.ResourceTelemetryResourceMeterMetrics).String(): true,
coretypes.NewResourceRef(coretypes.ResourceMetaResourceQuickFilter).String(): true,
coretypes.NewResourceRef(coretypes.ResourceMetaResourceSavedView).String(): true,
}
allowedTypes := map[string]bool{}

View File

@@ -1,219 +1,5 @@
components:
schemas:
AiobservabilitytypesBlobPart:
properties:
content:
type: string
mime_type:
nullable: true
type: string
modality:
type: string
type:
type: string
required:
- type
- modality
- content
type: object
AiobservabilitytypesChatMessage:
properties:
name:
nullable: true
type: string
parts:
$ref: '#/components/schemas/AiobservabilitytypesParts'
role:
type: string
required:
- role
- parts
type: object
AiobservabilitytypesCompactionPart:
properties:
content:
nullable: true
type: string
id:
nullable: true
type: string
type:
type: string
required:
- type
type: object
AiobservabilitytypesFilePart:
properties:
file_id:
type: string
mime_type:
nullable: true
type: string
modality:
type: string
type:
type: string
required:
- type
- modality
- file_id
type: object
AiobservabilitytypesGenericPart:
additionalProperties: {}
type: object
AiobservabilitytypesGenericServerToolCall:
additionalProperties: {}
type: object
AiobservabilitytypesGenericServerToolCallResponse:
additionalProperties: {}
type: object
AiobservabilitytypesInputMessages:
items:
$ref: '#/components/schemas/AiobservabilitytypesChatMessage'
type: array
AiobservabilitytypesOutputMessage:
properties:
finish_reason:
nullable: true
type: string
name:
nullable: true
type: string
parts:
$ref: '#/components/schemas/AiobservabilitytypesParts'
role:
type: string
required:
- role
- parts
type: object
AiobservabilitytypesOutputMessages:
items:
$ref: '#/components/schemas/AiobservabilitytypesOutputMessage'
type: array
AiobservabilitytypesPart:
discriminator:
mapping:
blob: '#/components/schemas/AiobservabilitytypesBlobPart'
compaction: '#/components/schemas/AiobservabilitytypesCompactionPart'
file: '#/components/schemas/AiobservabilitytypesFilePart'
reasoning: '#/components/schemas/AiobservabilitytypesReasoningPart'
server_tool_call: '#/components/schemas/AiobservabilitytypesServerToolCallPart'
server_tool_call_response: '#/components/schemas/AiobservabilitytypesServerToolCallResponsePart'
text: '#/components/schemas/AiobservabilitytypesTextPart'
tool_call: '#/components/schemas/AiobservabilitytypesToolCallRequestPart'
tool_call_response: '#/components/schemas/AiobservabilitytypesToolCallResponsePart'
uri: '#/components/schemas/AiobservabilitytypesUriPart'
propertyName: type
oneOf:
- $ref: '#/components/schemas/AiobservabilitytypesTextPart'
- $ref: '#/components/schemas/AiobservabilitytypesToolCallRequestPart'
- $ref: '#/components/schemas/AiobservabilitytypesToolCallResponsePart'
- $ref: '#/components/schemas/AiobservabilitytypesServerToolCallPart'
- $ref: '#/components/schemas/AiobservabilitytypesServerToolCallResponsePart'
- $ref: '#/components/schemas/AiobservabilitytypesBlobPart'
- $ref: '#/components/schemas/AiobservabilitytypesFilePart'
- $ref: '#/components/schemas/AiobservabilitytypesUriPart'
- $ref: '#/components/schemas/AiobservabilitytypesReasoningPart'
- $ref: '#/components/schemas/AiobservabilitytypesCompactionPart'
- $ref: '#/components/schemas/AiobservabilitytypesGenericPart'
type: object
AiobservabilitytypesParts:
items:
$ref: '#/components/schemas/AiobservabilitytypesPart'
type: array
AiobservabilitytypesReasoningPart:
properties:
content:
type: string
type:
type: string
required:
- type
- content
type: object
AiobservabilitytypesServerToolCallPart:
properties:
id:
nullable: true
type: string
name:
type: string
server_tool_call:
$ref: '#/components/schemas/AiobservabilitytypesGenericServerToolCall'
type:
type: string
required:
- type
- name
- server_tool_call
type: object
AiobservabilitytypesServerToolCallResponsePart:
properties:
id:
nullable: true
type: string
server_tool_call_response:
$ref: '#/components/schemas/AiobservabilitytypesGenericServerToolCallResponse'
type:
type: string
required:
- type
- server_tool_call_response
type: object
AiobservabilitytypesTextPart:
properties:
content:
type: string
type:
type: string
required:
- type
- content
type: object
AiobservabilitytypesToolCallRequestPart:
properties:
arguments: {}
id:
nullable: true
type: string
name:
type: string
type:
type: string
required:
- type
- name
type: object
AiobservabilitytypesToolCallResponsePart:
properties:
id:
nullable: true
type: string
response:
nullable: true
type:
type: string
required:
- type
- response
type: object
AiobservabilitytypesUriPart:
properties:
mime_type:
nullable: true
type: string
modality:
type: string
type:
type: string
uri:
type: string
required:
- type
- modality
- uri
type: object
AlertmanagertypesChannel:
properties:
createdAt:
@@ -3581,69 +3367,6 @@ components:
repeatVariable:
type: string
type: object
DashboardtypesAIBuilderQuerySpec:
properties:
aggregations:
items:
$ref: '#/components/schemas/Querybuildertypesv5TraceAggregation'
nullable: true
type: array
bucketOptions:
$ref: '#/components/schemas/Querybuildertypesv5BucketOptions'
cursor:
type: string
disabled:
type: boolean
filter:
$ref: '#/components/schemas/Querybuildertypesv5Filter'
functions:
items:
$ref: '#/components/schemas/Querybuildertypesv5Function'
nullable: true
type: array
groupBy:
items:
$ref: '#/components/schemas/Querybuildertypesv5GroupByKey'
nullable: true
type: array
having:
$ref: '#/components/schemas/Querybuildertypesv5Having'
legend:
type: string
limit:
type: integer
limitBy:
$ref: '#/components/schemas/Querybuildertypesv5LimitBy'
name:
type: string
offset:
type: integer
order:
items:
$ref: '#/components/schemas/Querybuildertypesv5OrderBy'
nullable: true
type: array
secondaryAggregations:
items:
$ref: '#/components/schemas/Querybuildertypesv5SecondaryAggregation'
nullable: true
type: array
selectFields:
items:
$ref: '#/components/schemas/TelemetrytypesTelemetryFieldKey'
nullable: true
type: array
signal:
enum:
- traces
type: string
source:
$ref: '#/components/schemas/TelemetrytypesSource'
stepInterval:
$ref: '#/components/schemas/Querybuildertypesv5Step'
required:
- signal
type: object
DashboardtypesAreaChartAppearance:
properties:
fillMode:
@@ -4697,7 +4420,6 @@ components:
DashboardtypesQueryPlugin:
discriminator:
mapping:
signoz/AIBuilderQuery: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpec'
signoz/BuilderQuery: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec'
signoz/ClickHouseSQL: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5ClickHouseQuery'
signoz/CompositeQuery: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQuery'
@@ -4707,7 +4429,6 @@ components:
propertyName: kind
oneOf:
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec'
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpec'
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQuery'
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5QueryBuilderFormula'
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5PromQuery'
@@ -4717,25 +4438,12 @@ components:
DashboardtypesQueryPluginKind:
enum:
- signoz/BuilderQuery
- signoz/AIBuilderQuery
- signoz/CompositeQuery
- signoz/Formula
- signoz/PromQLQuery
- signoz/ClickHouseSQL
- signoz/TraceOperator
type: string
DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpec:
properties:
kind:
enum:
- signoz/AIBuilderQuery
type: string
spec:
$ref: '#/components/schemas/DashboardtypesAIBuilderQuerySpec'
required:
- kind
- spec
type: object
DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec:
properties:
kind:
@@ -8046,35 +7754,8 @@ components:
- metric
- value
type: object
PromotetypesIndexMaterializedPathsResult:
properties:
indexed:
items:
$ref: '#/components/schemas/PromotetypesPromotePath'
nullable: true
type: array
skipped:
items:
$ref: '#/components/schemas/PromotetypesPromotePath'
nullable: true
type: array
required:
- indexed
- skipped
type: object
PromotetypesPostableIndexMaterializedPaths:
properties:
dryRun:
type: boolean
signal:
type: string
required:
- signal
type: object
PromotetypesPromotePath:
properties:
context:
type: string
indexes:
items:
$ref: '#/components/schemas/PromotetypesWrappedIndex'
@@ -8083,12 +7764,6 @@ components:
type: string
promote:
type: boolean
signal:
type: string
required:
- signal
- context
- path
type: object
PromotetypesWrappedIndex:
properties:
@@ -10542,14 +10217,6 @@ components:
items:
$ref: '#/components/schemas/SpantypesEvent'
type: array
formattedInput:
$ref: '#/components/schemas/AiobservabilitytypesInputMessages'
formattedOutput:
$ref: '#/components/schemas/AiobservabilitytypesOutputMessages'
formatterWarnings:
items:
type: string
type: array
has_error:
type: boolean
kind_string:
@@ -10592,9 +10259,6 @@ components:
- attributes
- events
- references
- formattedInput
- formattedOutput
- formatterWarnings
type: object
SpantypesTraceAISummary:
properties:
@@ -10675,14 +10339,6 @@ components:
flags:
minimum: 0
type: integer
formattedInput:
$ref: '#/components/schemas/AiobservabilitytypesInputMessages'
formattedOutput:
$ref: '#/components/schemas/AiobservabilitytypesOutputMessages'
formatterWarnings:
items:
type: string
type: array
has_children:
type: boolean
has_error:
@@ -13776,6 +13432,110 @@ paths:
tags:
- llmpricingrules
x-signoz-stability: alpha
/api/v1/logs/promote_paths:
get:
deprecated: false
description: This endpoints promotes and indexes paths
operationId: ListPromotedAndIndexedPaths
responses:
"200":
content:
application/json:
schema:
properties:
data:
items:
$ref: '#/components/schemas/PromotetypesPromotePath'
nullable: true
type: array
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: Promote and index paths
tags:
- logs
x-signoz-stability: alpha
post:
deprecated: false
description: This endpoints promotes and indexes paths
operationId: HandlePromoteAndIndexPaths
requestBody:
content:
application/json:
schema:
items:
$ref: '#/components/schemas/PromotetypesPromotePath'
nullable: true
type: array
responses:
"201":
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
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- EDITOR
- tokenizer:
- EDITOR
summary: Promote and index paths
tags:
- logs
x-signoz-stability: alpha
/api/v1/org/preferences:
get:
deprecated: false
@@ -13947,217 +13707,6 @@ paths:
tags:
- preferences
x-signoz-stability: alpha
/api/v1/promoted_paths:
get:
deprecated: false
description: This endpoint lists the promoted paths of every JSON column, each
annotated with its signal and context. The signal, context, promoted and indexes
query parameters filter the listing.
operationId: ListPromotedPaths
parameters:
- in: query
name: signal
schema:
type: string
- in: query
name: context
schema:
type: string
- in: query
name: promoted
schema:
nullable: true
type: boolean
- in: query
name: indexes
schema:
nullable: true
type: boolean
responses:
"200":
content:
application/json:
schema:
properties:
data:
items:
$ref: '#/components/schemas/PromotetypesPromotePath'
nullable: true
type: array
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:
- logs-field:list
- traces-field:list
- tokenizer:
- logs-field:list
- traces-field:list
summary: List promoted paths
tags:
- promote
x-signoz-stability: alpha
post:
deprecated: false
description: This endpoint promotes paths of JSON columns to their promoted
columns. Each path names its promotion target with its signal and context,
e.g. traces/attribute.
operationId: PromotePaths
requestBody:
content:
application/json:
examples:
logs_body:
summary: 'Logs body: promote a path and index it'
value:
- context: body
indexes:
- fieldDataType: string
granularity: 1
type: ngrambf_v1(4, 1024, 2, 0)
path: user.name
promote: true
signal: logs
traces_attribute:
summary: 'Traces attribute: promote a path'
value:
- context: attribute
path: http.method
promote: true
signal: traces
schema:
items:
$ref: '#/components/schemas/PromotetypesPromotePath'
nullable: true
type: array
responses:
"201":
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
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- logs-field:update
- traces-field:update
- tokenizer:
- logs-field:update
- traces-field:update
summary: Promote paths
tags:
- promote
x-signoz-stability: alpha
/api/v1/promoted_paths/materialized:
post:
deprecated: false
description: This endpoint indexes the JSON sub-columns of materialized paths
whose columns are indexed and returns the indexed and skipped paths.
operationId: IndexMaterializedPaths
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PromotetypesPostableIndexMaterializedPaths'
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/PromotetypesIndexMaterializedPathsResult'
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:
- logs-field:update
- traces-field:update
- tokenizer:
- logs-field:update
- traces-field:update
summary: Index materialized paths
tags:
- promote
x-signoz-stability: alpha
/api/v1/roles:
get:
deprecated: false
@@ -16150,8 +15699,10 @@ paths:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key: []
- tokenizer: []
- api_key:
- VIEWER
- tokenizer:
- VIEWER
summary: Get stats
tags:
- stats
@@ -16529,12 +16080,9 @@ paths:
/api/v1/traces/{traceID}/thread:
get:
deprecated: false
description: Returns the gen_ai spans of a trace, model calls, tool calls and
agent spans, in timestamp order. The messages a span carries are decoded into
formattedInput and formattedOutput, with formatterWarnings holding what the
converter could not resolve; a span without messages has both empty. Pass
nextCursor as after or prevCursor as before to page, or spanId to open the
page around a span.
description: Returns the spans carrying gen_ai input or output messages in timestamp
order. Pass nextCursor as after or prevCursor as before to page, or spanId
to open the page around a span.
operationId: GetTraceThread
parameters:
- description: Page size, at most 100. 0 means 20.
@@ -19000,9 +18548,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: Run Infra Monitoring Setup Checks
tags:
- inframonitoring
@@ -19074,9 +18622,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List Clusters for Infra Monitoring
tags:
- inframonitoring
@@ -19158,9 +18706,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List DaemonSets for Infra Monitoring
tags:
- inframonitoring
@@ -19236,9 +18784,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List Deployments for Infra Monitoring
tags:
- inframonitoring
@@ -19304,9 +18852,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List Hosts for Infra Monitoring
tags:
- inframonitoring
@@ -19386,9 +18934,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List Jobs for Infra Monitoring
tags:
- inframonitoring
@@ -19467,9 +19015,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List Kubernetes Containers for Infra Monitoring
tags:
- inframonitoring
@@ -19538,9 +19086,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List Namespaces for Infra Monitoring
tags:
- inframonitoring
@@ -19613,9 +19161,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List Nodes for Infra Monitoring
tags:
- inframonitoring
@@ -19689,9 +19237,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List Pods for Infra Monitoring
tags:
- inframonitoring
@@ -19760,9 +19308,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List Volumes for Infra Monitoring
tags:
- inframonitoring
@@ -19838,9 +19386,9 @@ paths:
description: Internal Server Error
security:
- api_key:
- metrics:read
- VIEWER
- tokenizer:
- metrics:read
- VIEWER
summary: List StatefulSets for Infra Monitoring
tags:
- inframonitoring
@@ -25008,75 +24556,6 @@ paths:
tags:
- rules
x-signoz-stability: alpha
/api/v2/rules/{id}/clone:
post:
deprecated: false
description: This endpoint creates a new alert rule from the stored definition
of an existing one. The clone keeps every modeled field of the source, including
its enabled/disabled state, and gets the source's name suffixed with " - Copy"
(or a bumped " - Copy (n)" counter when the source is itself a copy). The
clone is owned by the caller.
operationId: CloneRuleByID
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
"201":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/RuletypesRule'
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
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- EDITOR
- tokenizer:
- EDITOR
summary: Clone alert rule
tags:
- rules
x-signoz-stability: alpha
/api/v2/rules/{id}/history/filter_keys:
get:
deprecated: false
@@ -28747,9 +28226,7 @@ paths:
deprecated: false
description: Returns the waterfall view of spans including all spans if total
spans are under a limit, a max count otherwise. Aggregations are dropped compared
to v3. A span carrying gen_ai input or output messages also has them decoded
into formattedInput and formattedOutput, with formatterWarnings holding what
the converter could not resolve.
to v3
operationId: GetWaterfallV4
parameters:
- in: path

View File

@@ -20,7 +20,6 @@ We **recommend** (almost enforce) reviewing these guides before contributing to
- [Provider](provider.md) - Dependency injection and provider patterns
- [Packages](packages.md) - Naming, layout, and conventions for `pkg/` packages
- [Service](service.md) - Managed service lifecycle with `factory.Service`
- [Span Mapper](spanmapper.md) - Provenance of the shipped span mapper definitions and what is left unmapped
- [SQL](sql.md) - Database and SQL patterns
- [DSL Filtering to SQL](dslfilteringtosql.md) - Compiling the list filter DSL to relational-store WHERE clauses
- [Types](types.md) - Domain types, request/response bodies, and storage rows in `pkg/types/`

View File

@@ -1,62 +0,0 @@
# Span Mapper
Where each source key in the shipped definitions under `pkg/modules/spanmapper/implspanmapper/fs/definitions/` comes from. A group runs on spans with an attribute key containing one of its condition substrings; sources are tried by priority and the first present wins; an existing target is never overwritten. Bump `version` on every change.
## gen_ai.llm
Condition: `model`, `llm.input_messages` (OpenInference sets `llm.model_name` from the response, so a failed call has no model key).
| Source | Emitted by |
|---|---|
| `llm.model_name`, `llm.provider`, `llm.system`, `llm.token_count.prompt`, `llm.token_count.completion`, `llm.token_count.prompt_details.cache_read`, `llm.token_count.prompt_details.cache_write`, `llm.token_count.completion_details.reasoning`, `llm.finish_reason`, `input.value`, `output.value`, `embedding.model_name`, `reranker.model_name`, `openinference.span.kind` | OpenInference |
| `llm.request.model`, `llm.response.model`, `llm.vendor`, `llm.usage.prompt_tokens`, `llm.usage.completion_tokens`, `llm.response.finish_reason`, `llm.response.stop_reason` | OpenLLMetry before the `gen_ai` rename |
| `gen_ai.system`, `gen_ai.usage.prompt_tokens`, `gen_ai.usage.completion_tokens`, `gen_ai.usage.cache_read_input_tokens`, `gen_ai.usage.cache_creation_input_tokens`, `gen_ai.usage.reasoning_tokens`, `gen_ai.prompt`, `gen_ai.completion`, `llm.request.type`, `traceloop.span.kind` | OpenLLMetry, LiteLLM, CrewAI, Mistral, LangSmith, OpenRouter gateway |
| `gen_ai.usage.cache_write.input_tokens` | OTel semconv draft spelling, Codex |
| `gen_ai.response.finish_reason`, `gen_ai.usage.prompt_token_details.cached_read_tokens`, `gen_ai.usage.completion_token_details.reasoning_tokens` | Bifrost gateway |
| `gen_ai.usage.input_tokens.cached` | OpenRouter gateway |
| `gen_ai.response.model`, last source for the request model | vLLM, which writes only the response model |
| `ai.model.id`, `ai.model.provider`, `ai.response.model`, `ai.response.finishReason`, `ai.prompt.messages`, `ai.response.text`, `ai.response.toolCalls`, `ai.usage.promptTokens`, `ai.usage.completionTokens` | Vercel AI SDK v4, Mastra |
| `ai.usage.inputTokens`, `ai.usage.outputTokens`, `ai.usage.cachedInputTokens`, `ai.usage.reasoningTokens` | Vercel AI SDK v5 |
| `langfuse.observation.model.name`, `langfuse.session.id` | Langfuse SDK v3 |
| `session.id` | OpenInference, Langfuse, Claude Code |
| `gen_ai.client.session_id` | opentelemetry-hooks runner (Cursor, Copilot, Claude Code hooks) |
| `model`, `input_tokens`, `output_tokens`, `cache_read_tokens`, `cache_creation_tokens` | Claude Code `claude_code.llm_request` spans ([docs](https://code.claude.com/docs/en/monitoring-usage#span-attributes)) |
## gen_ai.tool
Condition: `tool.name` (OpenInference), `ai.toolCall.` (Vercel, Mastra), `tool_call_args`, `tool_response` (Google ADK), `tool_use_id` (Claude Code, opentelemetry-hooks). Not `tool_name`: Codex records it on the model call span.
| Source | Emitted by |
|---|---|
| `tool.name`, `tool.id`, `tool.description`, `input.value`, `output.value`, `openinference.span.kind` | OpenInference |
| `ai.toolCall.name`, `ai.toolCall.id`, `ai.toolCall.args`, `ai.toolCall.result` | Vercel AI SDK, Mastra |
| `traceloop.entity.input`, `traceloop.entity.output`, `traceloop.span.kind`, `llm.request.type` | OpenLLMetry |
| `gcp.vertex.agent.tool_call_args`, `gcp.vertex.agent.tool_response` | Google ADK |
| `tool_name` | Claude Code `claude_code.tool` spans |
| `gen_ai.client.tool_name`, `gen_ai.client.tool_use_id` | opentelemetry-hooks runner (Cursor, Copilot, Claude Code hooks) |
## gen_ai.agent
Condition: `agent`.
| Source | Emitted by |
|---|---|
| `agent.name`, `agent.id`, `agent.description`, `openinference.span.kind` | OpenInference; `agent.id` and `agent.description` seen in no capture |
| `agent_name`, `final_result` | Pydantic AI |
| `crewai.agent.role`, `crewai.agent.id` | CrewAI |
| `traceloop.span.kind`, `llm.request.type` | OpenLLMetry |
| `agent_id` | Claude Code |
## Coverage of documented integrations
Every integration under [signoz.io/docs/llm-observability](https://signoz.io/docs/llm-observability/) was checked by looking at the instrumentation its page installs and the keys that instrumentation writes.
- Most pages install OpenInference or OpenLLMetry, or an SDK that writes OTel GenAI semconv itself (OpenAI, OpenAI Agents, Pydantic AI, AutoGen, Microsoft Agent Framework, Semantic Kernel, Copilot Chat, LiteLLM, OpenRouter). Their keys are the rows above.
- Three write their own keys and have their own rows: Claude Code and Claude Agent SDK, Cursor through opentelemetry-hooks, vLLM (response model only).
- Codex cannot be priced by renaming: `model` sits on the sampling function spans, `gen_ai.usage.*` on the child `handle_responses` span, `conversation.id` on the `codex.sampling` phase span, never together on one span.
To check a new integration, capture a trace and look for a model, usage, tool or messages key that no row above lists.
## Left unmapped on purpose
LiteLLM `llm.openai.messages` and `llm.openai.choices` (debug echo of the parent span's messages, Python repr). Total tokens and vendor cost keys (no target; the pricing processor owns cost). `ai.response.msToFirstChunk` and `gen_ai.latency.time_to_first_token` (milliseconds and seconds under one target).

View File

@@ -86,9 +86,6 @@ tests/
├── .env.local # generated by bootstrap/setup.py (gitignored)
├── bootstrap/
│ └── setup.py # test_setup / test_teardown — pytest lifecycle
├── drivers/ # the driver kit and one driver per feature
│ ├── kit.ts
│ └── routing.ts
├── fixtures/ # Playwright test fixtures (test.extend) only
│ └── auth.ts
├── helpers/ # function helpers + the constants they share with tests
@@ -115,24 +112,6 @@ These two folders look similar but mean different things:
Rule of thumb: if it's a `test.extend` fixture, put it in `fixtures/`. If it's a function you call explicitly (or a constant the function uses), put it in `helpers/`. If it's a static file the helpers read, put it in `testdata/`.
### Drivers
New suites use a driver rather than loose helpers. A driver is a function that closes over the page and returns the feature's actions and locators. `drivers/kit.ts` turns it into a fixture, so a spec imports one module and never passes a `page` around:
```ts
import { expect, PATH, test } from '../../drivers/routing';
test('TC-09 RouteTab switches the url', async ({ routing }) => {
await routing.goto(PATH.metricsSummary);
await expect(routing.activeRouteTab(PATH.metricsSummary)).toBeVisible();
});
```
- `driver(name, build)` adds a test-scoped fixture built on `authedPage`.
- `seeded(name, open, close)` adds a worker-scoped seed. `open` and `close` run on a throwaway admin page.
- A driver reuses helpers by calling them, never by reaching for another fixture.
- `drivers/` is AI-owned: coding agents generate and maintain it with the specs, and `.gitattributes` collapses it in diffs.
### Extended fixtures
For features needing complex setup (API-seeded data, ruler evaluation waits, cleanup), create domain-specific fixtures that extend `auth`. Group them in `fixtures/<domain>/`.

View File

@@ -423,7 +423,7 @@ func (r *AnomalyRule) Eval(ctx context.Context, ts time.Time) (int, error) {
func (r *AnomalyRule) String() string {
ar := ruletypes.PostableRule{
AlertName: valuer.NewDisplayName(r.Name()),
AlertName: r.Name(),
RuleCondition: r.Condition(),
EvalWindow: r.EvalWindow(),
Labels: r.Labels().Map(),

View File

@@ -46,7 +46,7 @@ func TestAnomalyRule_NoData_AlertOnAbsent(t *testing.T) {
target := 500.0
postableRule := ruletypes.PostableRule{
AlertName: valuer.NewDisplayName("Test anomaly no data"),
AlertName: "Test anomaly no data",
AlertType: ruletypes.AlertTypeMetric,
RuleType: ruletypes.RuleTypeAnomaly,
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{
@@ -149,7 +149,7 @@ func TestAnomalyRule_NoData_AbsentFor(t *testing.T) {
target := 500.0
postableRule := ruletypes.PostableRule{
AlertName: valuer.NewDisplayName("Test anomaly no data with AbsentFor"),
AlertName: "Test anomaly no data with AbsentFor",
AlertType: ruletypes.AlertTypeMetric,
RuleType: ruletypes.RuleTypeAnomaly,
Evaluation: &ruletypes.EvaluationEnvelope{Kind: ruletypes.RollingEvaluation, Spec: ruletypes.RollingWindow{

View File

@@ -13,7 +13,6 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
baserules "github.com/SigNoz/signoz/pkg/query-service/rules"
"github.com/SigNoz/signoz/pkg/types/ruletypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
func PrepareTaskFunc(opts baserules.PrepareTaskOptions) (baserules.Task, error) {
@@ -119,7 +118,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, error) {
}
parsedRule := opts.Rule
var alertname = parsedRule.AlertName.StringValue()
var alertname = parsedRule.AlertName
if alertname == "" {
// alertname is not mandatory for testing, so picking
// a random string here
@@ -127,7 +126,7 @@ func TestNotification(opts baserules.PrepareTestRuleOptions) (int, error) {
}
// append name to indicate this is test alert
parsedRule.AlertName = valuer.NewDisplayName(fmt.Sprintf("%s%s", alertname, ruletypes.TestAlertPostFix))
parsedRule.AlertName = fmt.Sprintf("%s%s", alertname, ruletypes.TestAlertPostFix)
var rule baserules.Rule
var err error

View File

@@ -43,11 +43,6 @@ const mockAliases = [
find: /^@signozhq\/ui\/tooltip$/,
replacement: `${srcPath}/storybook/mocks/tooltip.mock.tsx`,
},
{
// jest: not replaced, a test asserts on the toast it fires.
find: /^@signozhq\/ui\/sonner$/,
replacement: `${srcPath}/storybook/mocks/sonner.mock.tsx`,
},
];
/**

View File

@@ -70,49 +70,3 @@
Object.defineProperty(window, 'Date', { value: FrozenDate, writable: true });
})();
</script>
<script>
// Storybook has no microphone. Chromium's recognizer asks for one and for
// Google's speech service, and the `not-allowed` or `network` it reports makes
// the assistant hide its mic for the rest of the tab. This one starts
// listening at once and hears only what a story passes to `hear`.
(() => {
class StorySpeechRecognition extends EventTarget {
static listening = null;
lang = 'en-US';
continuous = false;
interimResults = false;
onstart = null;
onresult = null;
onerror = null;
onend = null;
start() {
StorySpeechRecognition.listening = this;
setTimeout(() => this.onstart?.());
}
stop() {
if (StorySpeechRecognition.listening === this) {
StorySpeechRecognition.listening = null;
}
setTimeout(() => this.onend?.());
}
abort() {
this.stop();
}
/** An interim result: the words so far, still listening. */
hear(transcript) {
const result = Object.assign([{ transcript, confidence: 1 }], {
isFinal: false,
});
this.onresult?.({ resultIndex: 0, results: [result] });
}
}
window.SpeechRecognition = StorySpeechRecognition;
window.webkitSpeechRecognition = StorySpeechRecognition;
})();
</script>

View File

@@ -122,21 +122,7 @@ const preview: Preview = {
['List', 'New', 'Edit'],
],
'Dashboards',
[
'List',
'Detail',
[
'Overview',
'Settings',
'Variables',
'New Variable',
'Edit Variable',
'Authz',
],
'Panel Editor',
['Overview', 'Chart Options', 'Thresholds and Links'],
'Public',
],
['List', 'Detail', 'Panel Editor', 'Public'],
'Services',
['List', 'Detail', 'Top Level Operations', 'Service Map'],
'Logs',

View File

@@ -23,13 +23,6 @@ const IGNORED_MESSAGES = [
// (YouTube embeds, the docs pane) so they hit the real network instead of
// an unanswered msw request; the block is the point, not a bug.
/violates the following Content Security Policy directive/,
// The filter editor's ANTLR parser reports every syntax error through
// `console.error` (`line 1:14 missing ...`), so each partial expression
// typed into it logs one; the editor shows the same errors on screen.
/^line \d+:\d+ /,
// Chromium's echo of a 4xx/5xx response, which the `[response]` entry below
// already reports with its URL and the time its request left.
/^Failed to load resource: the server responded with a status of /,
];
interface CapturedMessage {
@@ -93,9 +86,7 @@ const config: TestRunnerConfig = {
page.on('response', (response) => {
if (response.status() >= 400) {
messages.push({
// The browser's clock, when the request left: a busy runner receives
// the outgoing story's responses after the next story has started.
at: response.request().timing().startTime,
at: Date.now(),
text: `[response] ${response.status()} ${response.url()}`,
});
}

View File

@@ -25,7 +25,6 @@ You are operating within a constrained context window and strict system prompts.
- Never create barrel files.
- When writing new css, prefer CSS Modules
- Use ./docs/css-modules-guide.md as reference on how to write good CSS Modules.
- Before styling a `@signozhq/ui` component, check its props in `node_modules/@signozhq/ui/dist/<component>/*.d.ts` for `className`/`style` support; some omit them. Avoid overriding the component's CSS vars unless explicitly needed.
- When writing code that could need authorization checks, read ./src/lib/authz/README.md
3. FORCED VERIFICATION: Your internal tools mark file writes as successful even if the code does not compile. You are FORBIDDEN from reporting a task as complete until you have:

View File

@@ -41,12 +41,6 @@ These hold for every page. The per-pattern sections below only add to them.
`create` on API Keys, so blocking the outer container would hide work they are allowed to do.
7. **Verbs not covered here** (`attach`, `detach`, `assignee`) behave like `delete`: gate the control that triggers
them, not the surrounding content.
8. **Expose the selector.** A role is scoped to a resource by pasting its selector into the role editor, so every
resource a role can be scoped to shows that selector with a copy control on its detail view or drawer. The selector
is whatever the editor accepts for the resource (dashboard ID, service account ID, role name, ...), listed per
resource in [permissions.config.ts](../src/container/RolesSettings/permissions.config.ts). It is already known from
the row or URL, so it stays visible and copyable without `read`. Reference: the ID field in
[ServiceAccountDrawer/OverviewTab.tsx](../src/components/ServiceAccountDrawer/OverviewTab.tsx).
## Page patterns
@@ -142,8 +136,6 @@ Use the existing components. Create a new one only if none fit.
- [ ] Am I gating a shared component rather than a page or section?
- If so, it must stay functional with no permission. Example: the Query Builder works without field suggestions when
the user cannot read them.
- [ ] Does the page expose the resource's selector with a copy control (rule 8)?
- The expected format is the `selectorPlaceholder` for the resource in `permissions.config.ts`.
## Testing

View File

@@ -41,20 +41,6 @@ if (!HTMLElement.prototype.releasePointerCapture) {
HTMLElement.prototype.releasePointerCapture = function (): void {};
}
// jsdom has no PointerEvent; Base UI Switch constructs one on click.
if (typeof window.PointerEvent === 'undefined') {
class PointerEventMock extends MouseEvent {
pointerId: number;
pointerType: string;
constructor(type: string, init: PointerEventInit = {}) {
super(type, init);
this.pointerId = init.pointerId ?? 0;
this.pointerType = init.pointerType ?? '';
}
}
(window as any).PointerEvent = PointerEventMock;
}
if (typeof window.IntersectionObserver === 'undefined') {
class IntersectionObserverMock {
observe(): void {}

View File

@@ -49,13 +49,12 @@
"@dnd-kit/sortable": "8.0.0",
"@dnd-kit/utilities": "3.2.2",
"@grafana/data": "^11.6.15",
"@lezer/highlight": "1.2.1",
"@monaco-editor/react": "^4.7.0",
"@sentry/react": "10.57.0",
"@sentry/vite-plugin": "5.3.0",
"@signozhq/design-tokens": "2.2.1",
"@signozhq/design-tokens": "2.1.6",
"@signozhq/icons": "0.4.0",
"@signozhq/ui": "0.2.0",
"@signozhq/ui": "0.1.0",
"@tanstack/react-table": "8.21.3",
"@tanstack/react-virtual": "3.13.22",
"@uiw/codemirror-theme-copilot": "4.23.11",

441
frontend/pnpm-lock.yaml generated
View File

@@ -62,9 +62,6 @@ importers:
'@grafana/data':
specifier: ^11.6.15
version: 11.6.15(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@lezer/highlight':
specifier: 1.2.1
version: 1.2.1
'@monaco-editor/react':
specifier: ^4.7.0
version: 4.7.0(monaco-editor@0.55.1)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -75,14 +72,14 @@ importers:
specifier: 5.3.0
version: 5.3.0
'@signozhq/design-tokens':
specifier: 2.2.1
version: 2.2.1
specifier: 2.1.6
version: 2.1.6
'@signozhq/icons':
specifier: 0.4.0
version: 0.4.0
'@signozhq/ui':
specifier: 0.2.0
version: 0.2.0(@date-fns/tz@1.4.1)(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(date-fns@4.1.0)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)
specifier: 0.1.0
version: 0.1.0(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)
'@tanstack/react-table':
specifier: 8.21.3
version: 8.21.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -1287,33 +1284,6 @@ packages:
resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
engines: {node: '>=6.9.0'}
'@base-ui/react@1.7.0':
resolution: {integrity: sha512-j+8QjX44C32jrXD/qyEAGpFr70FRpGL2CY61mQd9nBPWN737CK0xxD1ceJ055rW4RtdvFDT1e7otzdlfxvsYug==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@date-fns/tz': ^1.2.0
'@types/react': ^17 || ^18 || ^19
date-fns: ^4.0.0
react: ^17 || ^18 || ^19
react-dom: ^17 || ^18 || ^19
peerDependenciesMeta:
'@date-fns/tz':
optional: true
'@types/react':
optional: true
date-fns:
optional: true
'@base-ui/utils@0.3.2':
resolution: {integrity: sha512-oWy1aq/I2GmYjpl4PhEAhzflF8VPGKgZeq0xAWTbfD5KBWyxcN0ZP2+WHSUm/5Z6lVMBDLReLcoXwSYoRc/zNQ==}
peerDependencies:
'@types/react': ^17 || ^18 || ^19
react: ^17 || ^18 || ^19
react-dom: ^17 || ^18 || ^19
peerDependenciesMeta:
'@types/react':
optional: true
'@bcoe/v8-coverage@0.2.3':
resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
@@ -1756,20 +1726,20 @@ packages:
resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==}
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
'@floating-ui/core@1.8.0':
resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==}
'@floating-ui/core@1.5.2':
resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==}
'@floating-ui/dom@1.8.0':
resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==}
'@floating-ui/dom@1.5.3':
resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==}
'@floating-ui/react-dom@2.1.9':
resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==}
'@floating-ui/react-dom@2.0.4':
resolution: {integrity: sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
'@floating-ui/utils@0.2.12':
resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==}
'@floating-ui/utils@0.1.6':
resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==}
'@gerrit0/mini-shiki@3.23.0':
resolution: {integrity: sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==}
@@ -2950,6 +2920,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-checkbox@1.3.11':
resolution: {integrity: sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-collection@1.1.15':
resolution: {integrity: sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==}
peerDependencies:
@@ -2963,6 +2946,15 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-compose-refs@1.1.2':
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-compose-refs@1.1.5':
resolution: {integrity: sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==}
peerDependencies:
@@ -3016,6 +3008,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-dropdown-menu@2.1.24':
resolution: {integrity: sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-focus-guards@1.1.6':
resolution: {integrity: sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==}
peerDependencies:
@@ -3043,6 +3048,15 @@ packages:
peerDependencies:
react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc
'@radix-ui/react-id@1.1.1':
resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-id@1.1.4':
resolution: {integrity: sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==}
peerDependencies:
@@ -3052,6 +3066,19 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-menu@2.1.24':
resolution: {integrity: sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-popover@1.1.23':
resolution: {integrity: sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==}
peerDependencies:
@@ -3117,6 +3144,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-primitive@2.1.4':
resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-progress@1.1.16':
resolution: {integrity: sha512-5XnomAsoZZCY+KNTxbIghpGqPruZvKFNlvcAljVAOdDRDsH4/OZQxhtwo5wdtoDM5R6MhJBb2sPnDuRFep3lzg==}
peerDependencies:
@@ -3182,6 +3222,15 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-slot@1.2.4':
resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-slot@1.3.3':
resolution: {integrity: sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==}
peerDependencies:
@@ -3191,6 +3240,19 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-switch@1.3.7':
resolution: {integrity: sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-tabs@1.1.21':
resolution: {integrity: sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==}
peerDependencies:
@@ -3204,6 +3266,45 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-toggle-group@1.1.19':
resolution: {integrity: sha512-OtnwuSVjd1Ofi+AdnvhsjQdyuhCDwYs1w9RyB5BN/OavXOVQo42SYqQjwUnbPnaiPFBpQ9aX70dWeee+v2oBLA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-toggle@1.1.18':
resolution: {integrity: sha512-7lonPlKfSacd20GlOBx2ltuVKz9oqWYZz+oMQyOltw6t1y2nyftj2ZmwwUHYn49kqfDWcp8dNZm5NgV+5Z+mug==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-tooltip@1.2.16':
resolution: {integrity: sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-use-callback-ref@1.1.4':
resolution: {integrity: sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==}
peerDependencies:
@@ -3240,6 +3341,15 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-use-layout-effect@1.1.1':
resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-layout-effect@1.1.4':
resolution: {integrity: sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==}
peerDependencies:
@@ -3611,14 +3721,14 @@ packages:
'@sideway/pinpoint@2.0.0':
resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
'@signozhq/design-tokens@2.2.1':
resolution: {integrity: sha512-ulf6lhWUP6qxqsyemQySUMfOIKRvefvZ5rBRv+wlhOpPfYPk3uatPmPkKEhAjISpX8f9g1Gg8wgRZxUCysf20Q==}
'@signozhq/design-tokens@2.1.6':
resolution: {integrity: sha512-n0cH4TRS1Xj7BR38HGiwIBDekIOAnDgl2JyYmAyUvws0L1nlE0txSHO5NiOBJ22Dm8uMva0v5/rOHxpKsGQbsw==}
'@signozhq/icons@0.4.0':
resolution: {integrity: sha512-QctNvNPlQlsefFdIVmSy/P/7a1IcQ3IryKP0KPMqtoqew3q89FGUmFa5IfEJ7Z3CHhtG1zaXwBKqP72QkLyA3g==}
'@signozhq/ui@0.2.0':
resolution: {integrity: sha512-j20ldiOkR150ON0BCDhlq6fXGoPN++7w6e4t2HtM2XMM4buJO7aHzPzNKvDRZ2UUf/LGPULPAW8w6sg6gUVHyw==}
'@signozhq/ui@0.1.0':
resolution: {integrity: sha512-FjqmQhz+WtpGl29gPGZey5YwNe4p++TCyX4jaGUhAgvlBGRmxmAKW0lFRrSOYTPbgFL5UaDCW0wR62aiGHZ+FA==}
peerDependencies:
'@signozhq/icons': 0.3.0
react: ^18.2.0
@@ -8410,11 +8520,6 @@ packages:
react: ^16.8.5 || ^17.0.0 || ^18.0.0
react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0
react-compiler-runtime@1.0.0:
resolution: {integrity: sha512-rRfjYv66HlG8896yPUDONgKzG5BxZD1nV9U6rkm+7VCuvQc903C4MjcoZR4zPw53IKSOX9wMQVpA1IAbRtzQ7w==}
peerDependencies:
react: ^17.0.0 || ^18.0.0 || ^19.0.0 || ^0.0.0-experimental
react-day-picker@9.9.0:
resolution: {integrity: sha512-NtkJbuX6cl/VaGNb3sVVhmMA6LSMnL5G3xNL+61IyoZj0mUZFWTg4hmj7PHjIQ8MXN9dHWhUHFoJWG6y60DKSg==}
engines: {node: '>=18'}
@@ -8802,9 +8907,6 @@ packages:
requires-port@1.0.0:
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
reselect@5.3.0:
resolution: {integrity: sha512-XGoLeRAVzUTcJ1qkxPQhDJyIZ5d6zzZD9nT7AEZOaaU9UbWclhycElmhO+VD5bFeLuzhPBaOV2oXC8uG35ZSpg==}
reserved-words@0.1.2:
resolution: {integrity: sha512-0S5SrIUJ9LfpbVl4Yzij6VipUdafHrOTzvmfazSw/jeZrZtQK303OPZW+obtkaw7jQlTQppy0UvZWm9872PbRw==}
@@ -11052,31 +11154,6 @@ snapshots:
'@babel/helper-string-parser': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
'@base-ui/react@1.7.0(@date-fns/tz@1.4.1)(@types/react@18.0.26)(date-fns@4.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@babel/runtime': 7.29.2
'@base-ui/utils': 0.3.2(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@floating-ui/react-dom': 2.1.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@floating-ui/utils': 0.2.12
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
use-sync-external-store: 1.6.0(react@18.2.0)
optionalDependencies:
'@date-fns/tz': 1.4.1
'@types/react': 18.0.26
date-fns: 4.1.0
'@base-ui/utils@0.3.2(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@babel/runtime': 7.29.2
'@floating-ui/utils': 0.2.12
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
reselect: 5.3.0
use-sync-external-store: 1.6.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.0.26
'@bcoe/v8-coverage@0.2.3': {}
'@braintree/sanitize-url@7.0.1': {}
@@ -11521,22 +11598,22 @@ snapshots:
'@eslint/core': 1.2.1
levn: 0.4.1
'@floating-ui/core@1.8.0':
'@floating-ui/core@1.5.2':
dependencies:
'@floating-ui/utils': 0.2.12
'@floating-ui/utils': 0.1.6
'@floating-ui/dom@1.8.0':
'@floating-ui/dom@1.5.3':
dependencies:
'@floating-ui/core': 1.8.0
'@floating-ui/utils': 0.2.12
'@floating-ui/core': 1.5.2
'@floating-ui/utils': 0.1.6
'@floating-ui/react-dom@2.1.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
'@floating-ui/react-dom@2.0.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@floating-ui/dom': 1.8.0
'@floating-ui/dom': 1.5.3
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
'@floating-ui/utils@0.2.12': {}
'@floating-ui/utils@0.1.6': {}
'@gerrit0/mini-shiki@3.23.0':
dependencies:
@@ -12686,6 +12763,21 @@ snapshots:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-checkbox@1.3.11(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.7
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-presence': 1.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-use-size': 1.1.4(@types/react@18.0.26)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-collection@1.1.15(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
@@ -12698,6 +12790,12 @@ snapshots:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-compose-refs@1.1.2(@types/react@18.0.26)(react@18.2.0)':
dependencies:
react: 18.2.0
optionalDependencies:
'@types/react': 18.0.26
'@radix-ui/react-compose-refs@1.1.5(@types/react@18.0.26)(react@18.2.0)':
dependencies:
react: 18.2.0
@@ -12752,6 +12850,21 @@ snapshots:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-dropdown-menu@2.1.24(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.7
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-id': 1.1.4(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-menu': 2.1.24(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-focus-guards@1.1.6(@types/react@18.0.26)(react@18.2.0)':
dependencies:
react: 18.2.0
@@ -12773,6 +12886,13 @@ snapshots:
dependencies:
react: 18.2.0
'@radix-ui/react-id@1.1.1(@types/react@18.0.26)(react@18.2.0)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.0.26)(react@18.2.0)
react: 18.2.0
optionalDependencies:
'@types/react': 18.0.26
'@radix-ui/react-id@1.1.4(@types/react@18.0.26)(react@18.2.0)':
dependencies:
'@radix-ui/react-use-layout-effect': 1.1.4(@types/react@18.0.26)(react@18.2.0)
@@ -12780,6 +12900,32 @@ snapshots:
optionalDependencies:
'@types/react': 18.0.26
'@radix-ui/react-menu@2.1.24(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.7
'@radix-ui/react-collection': 1.1.15(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-direction': 1.1.4(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-focus-guards': 1.1.6(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-focus-scope': 1.1.16(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-id': 1.1.4(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-popper': 1.3.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-portal': 1.1.17(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-presence': 1.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-slot': 1.3.3(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-use-callback-ref': 1.1.4(@types/react@18.0.26)(react@18.2.0)
aria-hidden: 1.2.6
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-remove-scroll: 2.7.2(@types/react@18.0.26)(react@18.2.0)
optionalDependencies:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-popover@1.1.23(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.7
@@ -12805,7 +12951,7 @@ snapshots:
'@radix-ui/react-popper@1.3.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@floating-ui/react-dom': 2.1.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@floating-ui/react-dom': 2.0.4(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-arrow': 1.1.15(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
@@ -12849,6 +12995,15 @@ snapshots:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-primitive@2.1.4(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/react-slot': 1.2.4(@types/react@18.0.26)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-progress@1.1.16(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
@@ -12944,6 +13099,13 @@ snapshots:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-slot@1.2.4(@types/react@18.0.26)(react@18.2.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.0.26)(react@18.2.0)
react: 18.2.0
optionalDependencies:
'@types/react': 18.0.26
'@radix-ui/react-slot@1.3.3(@types/react@18.0.26)(react@18.2.0)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
@@ -12951,6 +13113,20 @@ snapshots:
optionalDependencies:
'@types/react': 18.0.26
'@radix-ui/react-switch@1.3.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.7
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-use-size': 1.1.4(@types/react@18.0.26)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-tabs@1.1.21(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.7
@@ -12967,6 +13143,53 @@ snapshots:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-toggle-group@1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.7
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-direction': 1.1.4(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-roving-focus': 1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-toggle': 1.1.18(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-toggle@1.1.18(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.7
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-tooltip@1.2.16(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@radix-ui/primitive': 1.1.7
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-context': 1.2.2(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-dismissable-layer': 1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-id': 1.1.4(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-popper': 1.3.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-portal': 1.1.17(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-presence': 1.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-slot': 1.3.3(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-use-controllable-state': 1.2.6(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-use-layout-effect': 1.1.4(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-visually-hidden': 1.2.11(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
optionalDependencies:
'@types/react': 18.0.26
'@types/react-dom': 18.0.10
'@radix-ui/react-use-callback-ref@1.1.4(@types/react@18.0.26)(react@18.2.0)':
dependencies:
react: 18.2.0
@@ -12995,6 +13218,12 @@ snapshots:
optionalDependencies:
'@types/react': 18.0.26
'@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.0.26)(react@18.2.0)':
dependencies:
react: 18.2.0
optionalDependencies:
'@types/react': 18.0.26
'@radix-ui/react-use-layout-effect@1.1.4(@types/react@18.0.26)(react@18.2.0)':
dependencies:
react: 18.2.0
@@ -13043,14 +13272,14 @@ snapshots:
'@rc-component/context@1.4.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@babel/runtime': 7.29.2
'@babel/runtime': 7.28.2
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
'@rc-component/mini-decimal@1.0.1':
dependencies:
'@babel/runtime': 7.29.2
'@babel/runtime': 7.28.2
'@rc-component/mutate-observer@1.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
@@ -13062,7 +13291,7 @@ snapshots:
'@rc-component/portal@1.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
dependencies:
'@babel/runtime': 7.29.2
'@babel/runtime': 7.28.2
classnames: 2.3.2
rc-util: 5.38.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react: 18.2.0
@@ -13330,15 +13559,16 @@ snapshots:
'@sideway/pinpoint@2.0.0': {}
'@signozhq/design-tokens@2.2.1': {}
'@signozhq/design-tokens@2.1.6': {}
'@signozhq/icons@0.4.0': {}
'@signozhq/ui@0.2.0(@date-fns/tz@1.4.1)(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(date-fns@4.1.0)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)':
'@signozhq/ui@0.1.0(@emotion/is-prop-valid@1.2.0)(@signozhq/icons@0.4.0)(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)':
dependencies:
'@base-ui/react': 1.7.0(@date-fns/tz@1.4.1)(@types/react@18.0.26)(date-fns@4.1.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@chenglou/pretext': 0.0.8
'@radix-ui/react-checkbox': 1.3.11(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-dialog': 1.1.23(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-dropdown-menu': 2.1.24(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-icons': 1.3.2(react@18.2.0)
'@radix-ui/react-popover': 1.1.23(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-progress': 1.1.16(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -13346,7 +13576,11 @@ snapshots:
'@radix-ui/react-select': 2.3.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-slider': 1.4.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-slot': 1.3.3(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-switch': 1.3.7(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-tabs': 1.1.21(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-toggle': 1.1.18(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-toggle-group': 1.1.19(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-tooltip': 1.2.16(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@signozhq/icons': 0.4.0
'@tanstack/react-table': 8.21.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@tanstack/react-virtual': 3.14.9(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -13360,19 +13594,16 @@ snapshots:
next-themes: 0.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
nuqs: 2.9.6(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.6(react@18.2.0))(react@18.2.0)
react: 18.2.0
react-compiler-runtime: 1.0.0(react@18.2.0)
react-day-picker: 9.9.0(react@18.2.0)
react-dom: 18.2.0(react@18.2.0)
react-resizable-panels: 4.12.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
sonner: 2.0.7(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
transitivePeerDependencies:
- '@date-fns/tz'
- '@emotion/is-prop-valid'
- '@remix-run/react'
- '@tanstack/react-router'
- '@types/react'
- '@types/react-dom'
- date-fns
- next
- react-router
- react-router-dom
@@ -14898,10 +15129,10 @@ snapshots:
cmdk@1.1.1(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0):
dependencies:
'@radix-ui/react-compose-refs': 1.1.5(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-dialog': 1.1.23(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-id': 1.1.4(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-primitive': 2.1.10(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@radix-ui/react-id': 1.1.1(@types/react@18.0.26)(react@18.2.0)
'@radix-ui/react-primitive': 2.1.4(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
transitivePeerDependencies:
@@ -19046,10 +19277,6 @@ snapshots:
transitivePeerDependencies:
- react-native
react-compiler-runtime@1.0.0(react@18.2.0):
dependencies:
react: 18.2.0
react-day-picker@9.9.0(react@18.2.0):
dependencies:
'@date-fns/tz': 1.4.1
@@ -19535,8 +19762,6 @@ snapshots:
requires-port@1.0.0: {}
reselect@5.3.0: {}
reserved-words@0.1.2: {}
resize-observer-polyfill@1.5.1: {}
@@ -19642,7 +19867,7 @@ snapshots:
rtl-css-js@1.16.1:
dependencies:
'@babel/runtime': 7.29.2
'@babel/runtime': 7.28.2
run-applescript@7.1.0: {}
@@ -20454,7 +20679,7 @@ snapshots:
unload@2.2.0:
dependencies:
'@babel/runtime': 7.29.2
'@babel/runtime': 7.28.2
detect-node: 2.1.0
unplugin@2.3.11:

View File

@@ -300,17 +300,20 @@ describe('PrivateRoute', () => {
);
});
it.each([
['/logs-save-views', ROUTES.LOGS_EXPLORER],
['/traces-save-views', ROUTES.TRACES_EXPLORER],
[ROUTES.LOGS_SAVE_VIEWS, ROUTES.LOGS_EXPLORER],
[ROUTES.TRACES_SAVE_VIEWS, ROUTES.TRACES_EXPLORER],
[ROUTES.METRICS_EXPLORER_VIEWS, ROUTES.METRICS_EXPLORER_EXPLORER],
[ROUTES.METER_EXPLORER_VIEWS, ROUTES.METER_EXPLORER],
])('should redirect the old views route %s to %s', (from, to) => {
renderPrivateRoute({ initialRoute: from });
it('should redirect /logs-save-views to /logs/saved-views', () => {
renderPrivateRoute({ initialRoute: '/logs-save-views' });
expect(screen.getByTestId('location-display').textContent).toBe(to);
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/logs/saved-views',
);
});
it('should redirect /traces-save-views to /traces/saved-views', () => {
renderPrivateRoute({ initialRoute: '/traces-save-views' });
expect(screen.getByTestId('location-display')).toHaveTextContent(
'/traces/saved-views',
);
});
it('should redirect /settings/access-tokens to /settings/service-accounts', () => {
@@ -1632,6 +1635,11 @@ describe('PrivateRoute', () => {
['LIST_LICENSES', ROUTES.LIST_LICENSES, USER_ROLES.EDITOR as ROLES],
['ONBOARDING', ROUTES.ONBOARDING, USER_ROLES.VIEWER as ROLES],
['APPLICATION', ROUTES.APPLICATION, USER_ROLES.ANONYMOUS as ROLES],
[
'METRICS_EXPLORER_VIEWS',
ROUTES.METRICS_EXPLORER_VIEWS,
USER_ROLES.ANONYMOUS as ROLES,
],
];
it.each(authzRouteRolePairs)(

View File

@@ -513,12 +513,8 @@ export const LIST_LICENSES: AppRoutes = {
export const oldNewRoutesMapping: Record<string, string> = {
'/pipelines': '/logs/pipelines',
'/logs-explorer': '/logs/logs-explorer',
'/logs-save-views': '/logs/logs-explorer',
'/traces-save-views': '/traces-explorer',
'/logs/saved-views': '/logs/logs-explorer',
'/traces/saved-views': '/traces-explorer',
'/metrics-explorer/views': '/metrics-explorer/explorer',
'/meter/explorer/views': '/meter/explorer',
'/logs-save-views': '/logs/saved-views',
'/traces-save-views': '/traces/saved-views',
'/settings/access-tokens': '/settings/service-accounts',
'/settings/api-keys': '/settings/service-accounts',
'/messaging-queues': '/messaging-queues/overview',

View File

@@ -41,16 +41,17 @@ export function ErrorResponseHandler(error: AxiosError): ErrorResponse {
message: data?.error,
};
}
// Avoid logging error when the request was just cancelled for whatever reason
if (isCancel(error)) {
return {
statusCode: 500,
payload: null,
error: 'Something went wrong',
message: null,
};
}
if (request) {
// Avoid logging error when the request was just cancelled for whatever reason
if (isCancel(error)) {
return {
statusCode: 500,
payload: null,
error: 'Something went wrong',
message: null,
};
}
// client never received a response, or request never left
console.error('client never received a response, or request never left');

View File

@@ -1,24 +0,0 @@
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError, CanceledError } from 'axios';
describe('ErrorResponseHandler', () => {
// axios aborts a request whose signal fired before it was sent without ever
// creating one, so the error carries neither `response` nor `request`.
it('treats a request cancelled before it was sent as a cancellation', () => {
const consoleError = jest
.spyOn(console, 'error')
.mockImplementation(() => undefined);
expect(ErrorResponseHandler(new CanceledError() as AxiosError)).toStrictEqual(
{
statusCode: 500,
payload: null,
error: 'Something went wrong',
message: null,
},
);
expect(consoleError).not.toHaveBeenCalled();
consoleError.mockRestore();
});
});

View File

@@ -4,15 +4,23 @@
* * regenerate with 'pnpm generate:api'
* SigNoz
*/
import { useMutation } from 'react-query';
import { useMutation, useQuery } from 'react-query';
import type {
InvalidateOptions,
MutationFunction,
QueryClient,
QueryFunction,
QueryKey,
UseMutationOptions,
UseMutationResult,
UseQueryOptions,
UseQueryResult,
} from 'react-query';
import type {
HandleExportRawDataPOSTParams,
ListPromotedAndIndexedPaths200,
PromotetypesPromotePathDTO,
Querybuildertypesv5QueryRangeRequestDTO,
RenderErrorResponseDTO,
} from '../sigNoz.schemas';
@@ -20,6 +28,26 @@ import type {
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
const withQueryKey = <T extends object, K>(
query: T,
queryKey: K,
): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === 'queryKey') {
continue;
}
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* This endpoints allows complex query exporting raw data for traces and logs
* @summary Export raw data
@@ -121,3 +149,175 @@ export const useHandleExportRawDataPOST = <
> => {
return useMutation(getHandleExportRawDataPOSTMutationOptions(options));
};
/**
* This endpoints promotes and indexes paths
* @summary Promote and index paths
*/
export const listPromotedAndIndexedPaths = (signal?: AbortSignal) => {
return GeneratedAPIInstance<ListPromotedAndIndexedPaths200>({
url: `/api/v1/logs/promote_paths`,
method: 'GET',
signal,
});
};
export const getListPromotedAndIndexedPathsQueryKey = () => {
return [`/api/v1/logs/promote_paths`] as const;
};
export const getListPromotedAndIndexedPathsQueryOptions = <
TData = Awaited<ReturnType<typeof listPromotedAndIndexedPaths>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listPromotedAndIndexedPaths>>,
TError,
TData
>;
}) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getListPromotedAndIndexedPathsQueryKey();
const queryFn: QueryFunction<
Awaited<ReturnType<typeof listPromotedAndIndexedPaths>>
> = ({ signal }) => listPromotedAndIndexedPaths(signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof listPromotedAndIndexedPaths>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ListPromotedAndIndexedPathsQueryResult = NonNullable<
Awaited<ReturnType<typeof listPromotedAndIndexedPaths>>
>;
export type ListPromotedAndIndexedPathsQueryError =
ErrorType<RenderErrorResponseDTO>;
/**
* @summary Promote and index paths
*/
export function useListPromotedAndIndexedPaths<
TData = Awaited<ReturnType<typeof listPromotedAndIndexedPaths>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listPromotedAndIndexedPaths>>,
TError,
TData
>;
}): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getListPromotedAndIndexedPathsQueryOptions(options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
return withQueryKey(query, queryOptions.queryKey);
}
/**
* @summary Promote and index paths
*/
export const invalidateListPromotedAndIndexedPaths = async (
queryClient: QueryClient,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getListPromotedAndIndexedPathsQueryKey() },
options,
);
return queryClient;
};
/**
* This endpoints promotes and indexes paths
* @summary Promote and index paths
*/
export const handlePromoteAndIndexPaths = (
promotetypesPromotePathDTONull?: BodyType<
PromotetypesPromotePathDTO[] | null
> | null,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/logs/promote_paths`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: promotetypesPromotePathDTONull,
signal,
});
};
export const getHandlePromoteAndIndexPathsMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof handlePromoteAndIndexPaths>>,
TError,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof handlePromoteAndIndexPaths>>,
TError,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> },
TContext
> => {
const mutationKey = ['handlePromoteAndIndexPaths'];
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 handlePromoteAndIndexPaths>>,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> }
> = (props) => {
const { data } = props ?? {};
return handlePromoteAndIndexPaths(data);
};
return { mutationFn, ...mutationOptions };
};
export type HandlePromoteAndIndexPathsMutationResult = NonNullable<
Awaited<ReturnType<typeof handlePromoteAndIndexPaths>>
>;
export type HandlePromoteAndIndexPathsMutationBody =
| BodyType<PromotetypesPromotePathDTO[] | null>
| undefined;
export type HandlePromoteAndIndexPathsMutationError =
ErrorType<RenderErrorResponseDTO>;
/**
* @summary Promote and index paths
*/
export const useHandlePromoteAndIndexPaths = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof handlePromoteAndIndexPaths>>,
TError,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof handlePromoteAndIndexPaths>>,
TError,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> },
TContext
> => {
return useMutation(getHandlePromoteAndIndexPathsMutationOptions(options));
};

View File

@@ -1,318 +0,0 @@
/**
* ! Do not edit manually
* * The file has been auto-generated using Orval for SigNoz
* * regenerate with 'pnpm 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 {
IndexMaterializedPaths200,
ListPromotedPaths200,
ListPromotedPathsParams,
PromotetypesPostableIndexMaterializedPathsDTO,
PromotetypesPromotePathDTO,
RenderErrorResponseDTO,
} from '../sigNoz.schemas';
import { GeneratedAPIInstance } from '../../../generatedAPIInstance';
import type { ErrorType, BodyType } from '../../../generatedAPIInstance';
const withQueryKey = <T extends object, K>(
query: T,
queryKey: K,
): T & { queryKey: K } => {
const result = { queryKey } as T & { queryKey: K };
for (const key of Object.keys(query)) {
// The explicit queryKey always wins, matching the previous
// `{ ...query, queryKey }` spread where it was set last.
if (key === 'queryKey') {
continue;
}
Object.defineProperty(result, key, {
enumerable: true,
configurable: true,
get: () => (query as Record<string, unknown>)[key],
});
}
return result;
};
/**
* This endpoint lists the promoted paths of every JSON column, each annotated with its signal and context. The signal, context, promoted and indexes query parameters filter the listing.
* @summary List promoted paths
*/
export const listPromotedPaths = (
params?: ListPromotedPathsParams,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<ListPromotedPaths200>({
url: `/api/v1/promoted_paths`,
method: 'GET',
params,
signal,
});
};
export const getListPromotedPathsQueryKey = (
params?: ListPromotedPathsParams,
) => {
return [`/api/v1/promoted_paths`, ...(params ? [params] : [])] as const;
};
export const getListPromotedPathsQueryOptions = <
TData = Awaited<ReturnType<typeof listPromotedPaths>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
params?: ListPromotedPathsParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listPromotedPaths>>,
TError,
TData
>;
},
) => {
const { query: queryOptions } = options ?? {};
const queryKey =
queryOptions?.queryKey ?? getListPromotedPathsQueryKey(params);
const queryFn: QueryFunction<
Awaited<ReturnType<typeof listPromotedPaths>>
> = ({ signal }) => listPromotedPaths(params, signal);
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
Awaited<ReturnType<typeof listPromotedPaths>>,
TError,
TData
> & { queryKey: QueryKey };
};
export type ListPromotedPathsQueryResult = NonNullable<
Awaited<ReturnType<typeof listPromotedPaths>>
>;
export type ListPromotedPathsQueryError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary List promoted paths
*/
export function useListPromotedPaths<
TData = Awaited<ReturnType<typeof listPromotedPaths>>,
TError = ErrorType<RenderErrorResponseDTO>,
>(
params?: ListPromotedPathsParams,
options?: {
query?: UseQueryOptions<
Awaited<ReturnType<typeof listPromotedPaths>>,
TError,
TData
>;
},
): UseQueryResult<TData, TError> & { queryKey: QueryKey } {
const queryOptions = getListPromotedPathsQueryOptions(params, options);
const query = useQuery(queryOptions) as UseQueryResult<TData, TError> & {
queryKey: QueryKey;
};
return withQueryKey(query, queryOptions.queryKey);
}
/**
* @summary List promoted paths
*/
export const invalidateListPromotedPaths = async (
queryClient: QueryClient,
params?: ListPromotedPathsParams,
options?: InvalidateOptions,
): Promise<QueryClient> => {
await queryClient.invalidateQueries(
{ queryKey: getListPromotedPathsQueryKey(params) },
options,
);
return queryClient;
};
/**
* This endpoint promotes paths of JSON columns to their promoted columns. Each path names its promotion target with its signal and context, e.g. traces/attribute.
* @summary Promote paths
*/
export const promotePaths = (
promotetypesPromotePathDTONull?: BodyType<
PromotetypesPromotePathDTO[] | null
> | null,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<void>({
url: `/api/v1/promoted_paths`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: promotetypesPromotePathDTONull,
signal,
});
};
export const getPromotePathsMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof promotePaths>>,
TError,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof promotePaths>>,
TError,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> },
TContext
> => {
const mutationKey = ['promotePaths'];
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 promotePaths>>,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> }
> = (props) => {
const { data } = props ?? {};
return promotePaths(data);
};
return { mutationFn, ...mutationOptions };
};
export type PromotePathsMutationResult = NonNullable<
Awaited<ReturnType<typeof promotePaths>>
>;
export type PromotePathsMutationBody =
| BodyType<PromotetypesPromotePathDTO[] | null>
| undefined;
export type PromotePathsMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Promote paths
*/
export const usePromotePaths = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof promotePaths>>,
TError,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof promotePaths>>,
TError,
{ data?: BodyType<PromotetypesPromotePathDTO[] | null> },
TContext
> => {
return useMutation(getPromotePathsMutationOptions(options));
};
/**
* This endpoint indexes the JSON sub-columns of materialized paths whose columns are indexed and returns the indexed and skipped paths.
* @summary Index materialized paths
*/
export const indexMaterializedPaths = (
promotetypesPostableIndexMaterializedPathsDTO?: BodyType<PromotetypesPostableIndexMaterializedPathsDTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<IndexMaterializedPaths200>({
url: `/api/v1/promoted_paths/materialized`,
method: 'POST',
headers: { 'Content-Type': 'application/json' },
data: promotetypesPostableIndexMaterializedPathsDTO,
signal,
});
};
export const getIndexMaterializedPathsMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof indexMaterializedPaths>>,
TError,
{ data?: BodyType<PromotetypesPostableIndexMaterializedPathsDTO> },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof indexMaterializedPaths>>,
TError,
{ data?: BodyType<PromotetypesPostableIndexMaterializedPathsDTO> },
TContext
> => {
const mutationKey = ['indexMaterializedPaths'];
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 indexMaterializedPaths>>,
{ data?: BodyType<PromotetypesPostableIndexMaterializedPathsDTO> }
> = (props) => {
const { data } = props ?? {};
return indexMaterializedPaths(data);
};
return { mutationFn, ...mutationOptions };
};
export type IndexMaterializedPathsMutationResult = NonNullable<
Awaited<ReturnType<typeof indexMaterializedPaths>>
>;
export type IndexMaterializedPathsMutationBody =
| BodyType<PromotetypesPostableIndexMaterializedPathsDTO>
| undefined;
export type IndexMaterializedPathsMutationError =
ErrorType<RenderErrorResponseDTO>;
/**
* @summary Index materialized paths
*/
export const useIndexMaterializedPaths = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof indexMaterializedPaths>>,
TError,
{ data?: BodyType<PromotetypesPostableIndexMaterializedPathsDTO> },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof indexMaterializedPaths>>,
TError,
{ data?: BodyType<PromotetypesPostableIndexMaterializedPathsDTO> },
TContext
> => {
return useMutation(getIndexMaterializedPathsMutationOptions(options));
};

View File

@@ -18,8 +18,6 @@ import type {
} from 'react-query';
import type {
CloneRuleByID201,
CloneRuleByIDPathParameters,
CreateRule201,
CreateRuleView201,
DeleteRuleByIDPathParameters,
@@ -965,85 +963,6 @@ export const useUpdateRuleByID = <
> => {
return useMutation(getUpdateRuleByIDMutationOptions(options));
};
/**
* This endpoint creates a new alert rule from the stored definition of an existing one. The clone keeps every modeled field of the source, including its enabled/disabled state, and gets the source's name suffixed with " - Copy" (or a bumped " - Copy (n)" counter when the source is itself a copy). The clone is owned by the caller.
* @summary Clone alert rule
*/
export const cloneRuleByID = (
{ id }: CloneRuleByIDPathParameters,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<CloneRuleByID201>({
url: `/api/v2/rules/${id}/clone`,
method: 'POST',
signal,
});
};
export const getCloneRuleByIDMutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof cloneRuleByID>>,
TError,
{ pathParams: CloneRuleByIDPathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof cloneRuleByID>>,
TError,
{ pathParams: CloneRuleByIDPathParameters },
TContext
> => {
const mutationKey = ['cloneRuleByID'];
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 cloneRuleByID>>,
{ pathParams: CloneRuleByIDPathParameters }
> = (props) => {
const { pathParams } = props ?? {};
return cloneRuleByID(pathParams);
};
return { mutationFn, ...mutationOptions };
};
export type CloneRuleByIDMutationResult = NonNullable<
Awaited<ReturnType<typeof cloneRuleByID>>
>;
export type CloneRuleByIDMutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Clone alert rule
*/
export const useCloneRuleByID = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof cloneRuleByID>>,
TError,
{ pathParams: CloneRuleByIDPathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof cloneRuleByID>>,
TError,
{ pathParams: CloneRuleByIDPathParameters },
TContext
> => {
return useMutation(getCloneRuleByIDMutationOptions(options));
};
/**
* Returns distinct label keys from rule history entries for the selected range.
* @summary Get rule history filter keys

View File

@@ -4,264 +4,6 @@
* * regenerate with 'pnpm generate:api'
* SigNoz
*/
export enum AiobservabilitytypesBlobPartDTOType {
blob = 'blob',
}
export interface AiobservabilitytypesBlobPartDTO {
/**
* @type string
*/
content: string;
/**
* @type string,null
*/
mime_type?: string | null;
/**
* @type string
*/
modality: string;
/**
* @type string
* @enum blob
*/
type: AiobservabilitytypesBlobPartDTOType;
}
export enum AiobservabilitytypesTextPartDTOType {
text = 'text',
}
export interface AiobservabilitytypesTextPartDTO {
/**
* @type string
*/
content: string;
/**
* @type string
* @enum text
*/
type: AiobservabilitytypesTextPartDTOType;
}
export enum AiobservabilitytypesToolCallRequestPartDTOType {
tool_call = 'tool_call',
}
export interface AiobservabilitytypesToolCallRequestPartDTO {
arguments?: unknown;
/**
* @type string,null
*/
id?: string | null;
/**
* @type string
*/
name: string;
/**
* @type string
* @enum tool_call
*/
type: AiobservabilitytypesToolCallRequestPartDTOType;
}
export enum AiobservabilitytypesToolCallResponsePartDTOType {
tool_call_response = 'tool_call_response',
}
export type AiobservabilitytypesToolCallResponsePartDTOResponse =
| unknown
| null;
export interface AiobservabilitytypesToolCallResponsePartDTO {
/**
* @type string,null
*/
id?: string | null;
/**
* @nullable true
*/
response: AiobservabilitytypesToolCallResponsePartDTOResponse;
/**
* @type string
* @enum tool_call_response
*/
type: AiobservabilitytypesToolCallResponsePartDTOType;
}
export interface AiobservabilitytypesGenericServerToolCallDTO {
[key: string]: unknown;
}
export enum AiobservabilitytypesServerToolCallPartDTOType {
server_tool_call = 'server_tool_call',
}
export interface AiobservabilitytypesServerToolCallPartDTO {
/**
* @type string,null
*/
id?: string | null;
/**
* @type string
*/
name: string;
server_tool_call: AiobservabilitytypesGenericServerToolCallDTO;
/**
* @type string
* @enum server_tool_call
*/
type: AiobservabilitytypesServerToolCallPartDTOType;
}
export interface AiobservabilitytypesGenericServerToolCallResponseDTO {
[key: string]: unknown;
}
export enum AiobservabilitytypesServerToolCallResponsePartDTOType {
server_tool_call_response = 'server_tool_call_response',
}
export interface AiobservabilitytypesServerToolCallResponsePartDTO {
/**
* @type string,null
*/
id?: string | null;
server_tool_call_response: AiobservabilitytypesGenericServerToolCallResponseDTO;
/**
* @type string
* @enum server_tool_call_response
*/
type: AiobservabilitytypesServerToolCallResponsePartDTOType;
}
export enum AiobservabilitytypesFilePartDTOType {
file = 'file',
}
export interface AiobservabilitytypesFilePartDTO {
/**
* @type string
*/
file_id: string;
/**
* @type string,null
*/
mime_type?: string | null;
/**
* @type string
*/
modality: string;
/**
* @type string
* @enum file
*/
type: AiobservabilitytypesFilePartDTOType;
}
export enum AiobservabilitytypesUriPartDTOType {
uri = 'uri',
}
export interface AiobservabilitytypesUriPartDTO {
/**
* @type string,null
*/
mime_type?: string | null;
/**
* @type string
*/
modality: string;
/**
* @type string
* @enum uri
*/
type: AiobservabilitytypesUriPartDTOType;
/**
* @type string
*/
uri: string;
}
export enum AiobservabilitytypesReasoningPartDTOType {
reasoning = 'reasoning',
}
export interface AiobservabilitytypesReasoningPartDTO {
/**
* @type string
*/
content: string;
/**
* @type string
* @enum reasoning
*/
type: AiobservabilitytypesReasoningPartDTOType;
}
export enum AiobservabilitytypesCompactionPartDTOType {
compaction = 'compaction',
}
export interface AiobservabilitytypesCompactionPartDTO {
/**
* @type string,null
*/
content?: string | null;
/**
* @type string,null
*/
id?: string | null;
/**
* @type string
* @enum compaction
*/
type: AiobservabilitytypesCompactionPartDTOType;
}
export interface AiobservabilitytypesGenericPartDTO {
[key: string]: unknown;
}
export type AiobservabilitytypesPartDTO =
| AiobservabilitytypesTextPartDTO
| AiobservabilitytypesToolCallRequestPartDTO
| AiobservabilitytypesToolCallResponsePartDTO
| AiobservabilitytypesServerToolCallPartDTO
| AiobservabilitytypesServerToolCallResponsePartDTO
| AiobservabilitytypesBlobPartDTO
| AiobservabilitytypesFilePartDTO
| AiobservabilitytypesUriPartDTO
| AiobservabilitytypesReasoningPartDTO
| AiobservabilitytypesCompactionPartDTO
| AiobservabilitytypesGenericPartDTO;
export type AiobservabilitytypesPartsDTO = AiobservabilitytypesPartDTO[];
export interface AiobservabilitytypesChatMessageDTO {
/**
* @type string,null
*/
name?: string | null;
parts: AiobservabilitytypesPartsDTO;
/**
* @type string
*/
role: string;
}
export type AiobservabilitytypesInputMessagesDTO =
AiobservabilitytypesChatMessageDTO[];
export interface AiobservabilitytypesOutputMessageDTO {
/**
* @type string,null
*/
finish_reason?: string | null;
/**
* @type string,null
*/
name?: string | null;
parts: AiobservabilitytypesPartsDTO;
/**
* @type string
*/
role: string;
}
export type AiobservabilitytypesOutputMessagesDTO =
AiobservabilitytypesOutputMessageDTO[];
export interface AlertmanagertypesChannelDTO {
/**
* @type string
@@ -4469,10 +4211,195 @@ export interface DashboardGridLayoutSpecDTO {
repeatVariable?: string;
}
export enum DashboardtypesAIBuilderQuerySpecDTOSignal {
traces = 'traces',
export enum DashboardtypesAreaFillModeDTO {
solid = 'solid',
gradient = 'gradient',
}
export interface Querybuildertypesv5TraceAggregationDTO {
/**
* @minimum 0
* @maximum 1
* @nullable
*/
export type DashboardtypesFillOpacityDTO = number | null;
export enum DashboardtypesLineInterpolationDTO {
linear = 'linear',
spline = 'spline',
step_after = 'step_after',
step_before = 'step_before',
}
export enum DashboardtypesLineStyleDTO {
solid = 'solid',
dashed = 'dashed',
}
export interface DashboardtypesSpanGapsDTO {
/**
* @type string
* @description The maximum gap size to connect when fillOnlyBelow is true. Gaps larger than this duration are left disconnected.
*/
fillLessThan?: string;
/**
* @type boolean
* @description Controls whether lines connect across null values. When false (default), all gaps are connected. When true, only gaps smaller than fillLessThan are connected.
*/
fillOnlyBelow?: boolean;
}
export interface DashboardtypesAreaChartAppearanceDTO {
fillMode?: DashboardtypesAreaFillModeDTO;
fillOpacity?: DashboardtypesFillOpacityDTO | null;
lineInterpolation?: DashboardtypesLineInterpolationDTO;
lineStyle?: DashboardtypesLineStyleDTO;
/**
* @type boolean
*/
showPoints?: boolean;
spanGaps?: DashboardtypesSpanGapsDTO;
}
export interface DashboardtypesAxesDTO {
/**
* @type boolean
*/
isLogScale?: boolean;
/**
* @type number,null
*/
softMax?: number | null;
/**
* @type number,null
*/
softMin?: number | null;
}
export enum DashboardtypesPrecisionOptionDTO {
NUMBER_0 = '0',
NUMBER_1 = '1',
NUMBER_2 = '2',
NUMBER_3 = '3',
NUMBER_4 = '4',
full = 'full',
}
export interface DashboardtypesPanelFormattingDTO {
decimalPrecision?: DashboardtypesPrecisionOptionDTO;
/**
* @type string
*/
unit?: string;
}
export enum DashboardtypesLegendModeDTO {
list = 'list',
}
export enum DashboardtypesLegendPositionDTO {
bottom = 'bottom',
right = 'right',
}
export type DashboardtypesLegendDTOCustomColorsAnyOf = {
[key: string]: string;
};
/**
* @nullable
*/
export type DashboardtypesLegendDTOCustomColors =
DashboardtypesLegendDTOCustomColorsAnyOf | null;
export interface DashboardtypesLegendDTO {
/**
* @type object,null
*/
customColors?: DashboardtypesLegendDTOCustomColors;
mode?: DashboardtypesLegendModeDTO;
position?: DashboardtypesLegendPositionDTO;
}
export interface DashboardtypesThresholdWithLabelDTO {
/**
* @type string
*/
color: string;
/**
* @type string
*/
label?: string;
/**
* @type string
*/
unit?: string;
/**
* @type number
* @format double
*/
value: number;
}
export enum DashboardtypesStackModeDTO {
none = 'none',
normal = 'normal',
percent = 'percent',
}
export enum DashboardtypesTimePreferenceDTO {
global_time = 'global_time',
last_5_min = 'last_5_min',
last_15_min = 'last_15_min',
last_30_min = 'last_30_min',
last_1_hr = 'last_1_hr',
last_6_hr = 'last_6_hr',
last_1_day = 'last_1_day',
last_3_days = 'last_3_days',
last_1_week = 'last_1_week',
last_1_month = 'last_1_month',
}
export interface DashboardtypesAreaChartVisualizationDTO {
/**
* @type boolean
*/
fillSpans?: boolean;
stack?: DashboardtypesStackModeDTO;
timePreference?: DashboardtypesTimePreferenceDTO;
}
export interface DashboardtypesAreaChartPanelSpecDTO {
axes?: DashboardtypesAxesDTO;
chartAppearance?: DashboardtypesAreaChartAppearanceDTO;
formatting?: DashboardtypesPanelFormattingDTO;
legend?: DashboardtypesLegendDTO;
/**
* @type array,null
*/
thresholds?: DashboardtypesThresholdWithLabelDTO[] | null;
visualization?: DashboardtypesAreaChartVisualizationDTO;
}
export interface DashboardtypesBarChartVisualizationDTO {
/**
* @type boolean
*/
fillSpans?: boolean;
/**
* @type boolean
*/
stackedBarChart?: boolean;
timePreference?: DashboardtypesTimePreferenceDTO;
}
export interface DashboardtypesBarChartPanelSpecDTO {
axes?: DashboardtypesAxesDTO;
formatting?: DashboardtypesPanelFormattingDTO;
legend?: DashboardtypesLegendDTO;
/**
* @type array,null
*/
thresholds?: DashboardtypesThresholdWithLabelDTO[] | null;
visualization?: DashboardtypesBarChartVisualizationDTO;
}
export interface DashboardtypesBasicVisualizationDTO {
timePreference?: DashboardtypesTimePreferenceDTO;
}
export interface Querybuildertypesv5LogAggregationDTO {
/**
* @type string
*/
@@ -4711,274 +4638,13 @@ export interface TelemetrytypesTelemetryFieldKeyDTO {
unit?: string;
}
export enum Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregationDTOSignal {
logs = 'logs',
}
export enum TelemetrytypesSourceDTO {
meter = 'meter',
'' = '',
}
export interface DashboardtypesAIBuilderQuerySpecDTO {
/**
* @type array,null
*/
aggregations?: Querybuildertypesv5TraceAggregationDTO[] | null;
bucketOptions?: Querybuildertypesv5BucketOptionsDTO;
/**
* @type string
*/
cursor?: string;
/**
* @type boolean
*/
disabled?: boolean;
filter?: Querybuildertypesv5FilterDTO;
/**
* @type array,null
*/
functions?: Querybuildertypesv5FunctionDTO[] | null;
/**
* @type array,null
*/
groupBy?: Querybuildertypesv5GroupByKeyDTO[] | null;
having?: Querybuildertypesv5HavingDTO;
/**
* @type string
*/
legend?: string;
/**
* @type integer
*/
limit?: number;
limitBy?: Querybuildertypesv5LimitByDTO;
/**
* @type string
*/
name?: string;
/**
* @type integer
*/
offset?: number;
/**
* @type array,null
*/
order?: Querybuildertypesv5OrderByDTO[] | null;
/**
* @type array,null
*/
secondaryAggregations?: Querybuildertypesv5SecondaryAggregationDTO[] | null;
/**
* @type array,null
*/
selectFields?: TelemetrytypesTelemetryFieldKeyDTO[] | null;
/**
* @enum traces
* @type string
*/
signal: DashboardtypesAIBuilderQuerySpecDTOSignal;
source?: TelemetrytypesSourceDTO;
stepInterval?: Querybuildertypesv5StepDTO;
}
export enum DashboardtypesAreaFillModeDTO {
solid = 'solid',
gradient = 'gradient',
}
/**
* @minimum 0
* @maximum 1
* @nullable
*/
export type DashboardtypesFillOpacityDTO = number | null;
export enum DashboardtypesLineInterpolationDTO {
linear = 'linear',
spline = 'spline',
step_after = 'step_after',
step_before = 'step_before',
}
export enum DashboardtypesLineStyleDTO {
solid = 'solid',
dashed = 'dashed',
}
export interface DashboardtypesSpanGapsDTO {
/**
* @type string
* @description The maximum gap size to connect when fillOnlyBelow is true. Gaps larger than this duration are left disconnected.
*/
fillLessThan?: string;
/**
* @type boolean
* @description Controls whether lines connect across null values. When false (default), all gaps are connected. When true, only gaps smaller than fillLessThan are connected.
*/
fillOnlyBelow?: boolean;
}
export interface DashboardtypesAreaChartAppearanceDTO {
fillMode?: DashboardtypesAreaFillModeDTO;
fillOpacity?: DashboardtypesFillOpacityDTO | null;
lineInterpolation?: DashboardtypesLineInterpolationDTO;
lineStyle?: DashboardtypesLineStyleDTO;
/**
* @type boolean
*/
showPoints?: boolean;
spanGaps?: DashboardtypesSpanGapsDTO;
}
export interface DashboardtypesAxesDTO {
/**
* @type boolean
*/
isLogScale?: boolean;
/**
* @type number,null
*/
softMax?: number | null;
/**
* @type number,null
*/
softMin?: number | null;
}
export enum DashboardtypesPrecisionOptionDTO {
NUMBER_0 = '0',
NUMBER_1 = '1',
NUMBER_2 = '2',
NUMBER_3 = '3',
NUMBER_4 = '4',
full = 'full',
}
export interface DashboardtypesPanelFormattingDTO {
decimalPrecision?: DashboardtypesPrecisionOptionDTO;
/**
* @type string
*/
unit?: string;
}
export enum DashboardtypesLegendModeDTO {
list = 'list',
}
export enum DashboardtypesLegendPositionDTO {
bottom = 'bottom',
right = 'right',
}
export type DashboardtypesLegendDTOCustomColorsAnyOf = {
[key: string]: string;
};
/**
* @nullable
*/
export type DashboardtypesLegendDTOCustomColors =
DashboardtypesLegendDTOCustomColorsAnyOf | null;
export interface DashboardtypesLegendDTO {
/**
* @type object,null
*/
customColors?: DashboardtypesLegendDTOCustomColors;
mode?: DashboardtypesLegendModeDTO;
position?: DashboardtypesLegendPositionDTO;
}
export interface DashboardtypesThresholdWithLabelDTO {
/**
* @type string
*/
color: string;
/**
* @type string
*/
label?: string;
/**
* @type string
*/
unit?: string;
/**
* @type number
* @format double
*/
value: number;
}
export enum DashboardtypesStackModeDTO {
none = 'none',
normal = 'normal',
percent = 'percent',
}
export enum DashboardtypesTimePreferenceDTO {
global_time = 'global_time',
last_5_min = 'last_5_min',
last_15_min = 'last_15_min',
last_30_min = 'last_30_min',
last_1_hr = 'last_1_hr',
last_6_hr = 'last_6_hr',
last_1_day = 'last_1_day',
last_3_days = 'last_3_days',
last_1_week = 'last_1_week',
last_1_month = 'last_1_month',
}
export interface DashboardtypesAreaChartVisualizationDTO {
/**
* @type boolean
*/
fillSpans?: boolean;
stack?: DashboardtypesStackModeDTO;
timePreference?: DashboardtypesTimePreferenceDTO;
}
export interface DashboardtypesAreaChartPanelSpecDTO {
axes?: DashboardtypesAxesDTO;
chartAppearance?: DashboardtypesAreaChartAppearanceDTO;
formatting?: DashboardtypesPanelFormattingDTO;
legend?: DashboardtypesLegendDTO;
/**
* @type array,null
*/
thresholds?: DashboardtypesThresholdWithLabelDTO[] | null;
visualization?: DashboardtypesAreaChartVisualizationDTO;
}
export interface DashboardtypesBarChartVisualizationDTO {
/**
* @type boolean
*/
fillSpans?: boolean;
/**
* @type boolean
*/
stackedBarChart?: boolean;
timePreference?: DashboardtypesTimePreferenceDTO;
}
export interface DashboardtypesBarChartPanelSpecDTO {
axes?: DashboardtypesAxesDTO;
formatting?: DashboardtypesPanelFormattingDTO;
legend?: DashboardtypesLegendDTO;
/**
* @type array,null
*/
thresholds?: DashboardtypesThresholdWithLabelDTO[] | null;
visualization?: DashboardtypesBarChartVisualizationDTO;
}
export interface DashboardtypesBasicVisualizationDTO {
timePreference?: DashboardtypesTimePreferenceDTO;
}
export interface Querybuildertypesv5LogAggregationDTO {
/**
* @type string
*/
alias?: string;
/**
* @type string
*/
expression?: string;
}
export enum Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregationDTOSignal {
logs = 'logs',
}
export interface Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5LogAggregationDTO {
/**
* @type array,null
@@ -5169,6 +4835,17 @@ export interface Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTyp
stepInterval?: Querybuildertypesv5StepDTO;
}
export interface Querybuildertypesv5TraceAggregationDTO {
/**
* @type string
*/
alias?: string;
/**
* @type string
*/
expression?: string;
}
export enum Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregationDTOSignal {
traces = 'traces',
}
@@ -5727,18 +5404,6 @@ export interface DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDa
spec: DashboardtypesBuilderQuerySpecDTO;
}
export enum DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpecDTOKind {
'signoz/AIBuilderQuery' = 'signoz/AIBuilderQuery',
}
export interface DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpecDTO {
/**
* @enum signoz/AIBuilderQuery
* @type string
*/
kind: DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpecDTOKind;
spec: DashboardtypesAIBuilderQuerySpecDTO;
}
export enum DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQueryDTOKind {
'signoz/CompositeQuery' = 'signoz/CompositeQuery',
}
@@ -6030,7 +5695,6 @@ export interface DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQu
export type DashboardtypesQueryPluginDTO =
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpecDTO
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpecDTO
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQueryDTO
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5QueryBuilderFormulaDTO
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5PromQueryDTO
@@ -6709,7 +6373,6 @@ export interface DashboardtypesPostablePublicDashboardDTO {
export enum DashboardtypesQueryPluginKindDTO {
'signoz/BuilderQuery' = 'signoz/BuilderQuery',
'signoz/AIBuilderQuery' = 'signoz/AIBuilderQuery',
'signoz/CompositeQuery' = 'signoz/CompositeQuery',
'signoz/Formula' = 'signoz/Formula',
'signoz/PromQLQuery' = 'signoz/PromQLQuery',
@@ -9856,10 +9519,6 @@ export interface PromotetypesWrappedIndexDTO {
}
export interface PromotetypesPromotePathDTO {
/**
* @type string
*/
context: string;
/**
* @type array
*/
@@ -9867,37 +9526,11 @@ export interface PromotetypesPromotePathDTO {
/**
* @type string
*/
path: string;
path?: string;
/**
* @type boolean
*/
promote?: boolean;
/**
* @type string
*/
signal: string;
}
export interface PromotetypesIndexMaterializedPathsResultDTO {
/**
* @type array,null
*/
indexed: PromotetypesPromotePathDTO[] | null;
/**
* @type array,null
*/
skipped: PromotetypesPromotePathDTO[] | null;
}
export interface PromotetypesPostableIndexMaterializedPathsDTO {
/**
* @type boolean
*/
dryRun?: boolean;
/**
* @type string
*/
signal: string;
}
export interface Querybuildertypesv5AggregationMetaDTO {
@@ -11811,12 +11444,6 @@ export interface SpantypesThreadSpanDTO {
* @type array
*/
events: SpantypesEventDTO[];
formattedInput: AiobservabilitytypesInputMessagesDTO;
formattedOutput: AiobservabilitytypesOutputMessagesDTO;
/**
* @type array
*/
formatterWarnings: string[];
/**
* @type boolean
*/
@@ -11932,12 +11559,6 @@ export interface SpantypesWaterfallSpanDTO {
* @minimum 0
*/
flags?: number;
formattedInput?: AiobservabilitytypesInputMessagesDTO;
formattedOutput?: AiobservabilitytypesOutputMessagesDTO;
/**
* @type array
*/
formatterWarnings?: string[];
/**
* @type boolean
*/
@@ -13213,6 +12834,17 @@ export type ListUnmappedLLMModels200 = {
status: string;
};
export type ListPromotedAndIndexedPaths200 = {
/**
* @type array,null
*/
data: PromotetypesPromotePathDTO[] | null;
/**
* @type string
*/
status: string;
};
export type ListOrgPreferences200 = {
/**
* @type array
@@ -13238,48 +12870,6 @@ export type GetOrgPreference200 = {
export type UpdateOrgPreferencePathParameters = {
name: string;
};
export type ListPromotedPathsParams = {
/**
* @type string
* @description undefined
*/
signal?: string;
/**
* @type string
* @description undefined
*/
context?: string;
/**
* @type boolean,null
* @description undefined
*/
promoted?: boolean | null;
/**
* @type boolean,null
* @description undefined
*/
indexes?: boolean | null;
};
export type ListPromotedPaths200 = {
/**
* @type array,null
*/
data: PromotetypesPromotePathDTO[] | null;
/**
* @type string
*/
status: string;
};
export type IndexMaterializedPaths200 = {
data: PromotetypesIndexMaterializedPathsResultDTO;
/**
* @type string
*/
status: string;
};
export type ListRoles200 = {
/**
* @type array
@@ -14612,17 +14202,6 @@ export type PatchRuleByID200 = {
export type UpdateRuleByIDPathParameters = {
id: string;
};
export type CloneRuleByIDPathParameters = {
id: string;
};
export type CloneRuleByID201 = {
data: RuletypesRuleDTO;
/**
* @type string
*/
status: string;
};
export type GetRuleHistoryFilterKeysPathParameters = {
id: string;
};

View File

@@ -261,7 +261,7 @@ export const invalidateGetTraceSummary = async (
};
/**
* Returns the gen_ai spans of a trace, model calls, tool calls and agent spans, in timestamp order. The messages a span carries are decoded into formattedInput and formattedOutput, with formatterWarnings holding what the converter could not resolve; a span without messages has both empty. Pass nextCursor as after or prevCursor as before to page, or spanId to open the page around a span.
* Returns the spans carrying gen_ai input or output messages in timestamp order. Pass nextCursor as after or prevCursor as before to page, or spanId to open the page around a span.
* @summary Get thread view for a trace
*/
export const getTraceThread = (
@@ -475,7 +475,7 @@ export const useGetFlamegraph = <
return useMutation(getGetFlamegraphMutationOptions(options));
};
/**
* Returns the waterfall view of spans including all spans if total spans are under a limit, a max count otherwise. Aggregations are dropped compared to v3. A span carrying gen_ai input or output messages also has them decoded into formattedInput and formattedOutput, with formatterWarnings holding what the converter could not resolve.
* Returns the waterfall view of spans including all spans if total spans are under a limit, a max count otherwise. Aggregations are dropped compared to v3
* @summary Get waterfall view for a trace
*/
export const getWaterfallV4 = (

View File

@@ -61,11 +61,7 @@ describe('convertV5ResponseToLegacy', () => {
],
values: [
{ timestamp: 1000, value: 10 } as unknown as TimeSeriesValue,
{
timestamp: 2000,
value: 12,
partial: true,
} as unknown as TimeSeriesValue,
{ timestamp: 2000, value: 12 } as unknown as TimeSeriesValue,
],
} as unknown as TimeSeries,
],
@@ -108,7 +104,7 @@ describe('convertV5ResponseToLegacy', () => {
labels: { 'service.name': 'adservice' },
values: [
{ timestamp: 1000, value: '10' },
{ timestamp: 2000, value: '12', partial: true },
{ timestamp: 2000, value: '12' },
],
metaData: expect.objectContaining({
alias: '__result_0',

View File

@@ -125,7 +125,6 @@ function convertTimeSeriesData(
values: series.values.map((value: any) => ({
timestamp: value.timestamp,
value: String(value.value),
...(value.partial ? { partial: true } : {}),
})),
metaData: {
alias,

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 60 60"><linearGradient id="a" x1="15.959" x2="44.307" y1="-13.014" y2="15.333" gradientTransform="matrix(1 0 0 -1 0 61)" gradientUnits="userSpaceOnUse"><stop offset=".097" style="stop-color:#0095d5"/><stop offset=".301" style="stop-color:#238ad9"/><stop offset=".621" style="stop-color:#557bde"/><stop offset=".864" style="stop-color:#7472e2"/><stop offset="1" style="stop-color:#806ee3"/></linearGradient><path d="m0 60 30.1-30.1L60 60z" style="fill:url(#a)"/><linearGradient id="b" x1="4.209" x2="20.673" y1="48.941" y2="65.405" gradientTransform="matrix(1 0 0 -1 0 61)" gradientUnits="userSpaceOnUse"><stop offset=".118" style="stop-color:#0095d5"/><stop offset=".418" style="stop-color:#3c83dc"/><stop offset=".696" style="stop-color:#6d74e1"/><stop offset=".833" style="stop-color:#806ee3"/></linearGradient><path d="M0 0h30.1L0 32.5z" style="fill:url(#b)"/><linearGradient id="c" x1="-10.102" x2="45.731" y1="5.836" y2="61.669" gradientTransform="matrix(1 0 0 -1 0 61)" gradientUnits="userSpaceOnUse"><stop offset=".107" style="stop-color:#c757bc"/><stop offset=".214" style="stop-color:#d0609a"/><stop offset=".425" style="stop-color:#e1725c"/><stop offset=".605" style="stop-color:#ee7e2f"/><stop offset=".743" style="stop-color:#f58613"/><stop offset=".823" style="stop-color:#f88909"/></linearGradient><path d="M30.1 0 0 31.7V60l30.1-30.1L60 0z" style="fill:url(#c)"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,3 +1,3 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="#0E9AA7" d="M8.932 20.806c-.369 0-.738.007-1.109 0-.35-.007-.587-.206-.623-.5a.587.587 0 0 1 .53-.636c.79-.062 1.582-.063 2.372-.003a.548.548 0 0 1 .522.602c-.024.326-.253.526-.616.54zM1.792 8.345c-.392 0-.782.008-1.173.002-.327-.006-.577-.22-.614-.512-.037-.293.146-.544.499-.615.192-.032.388-.045.583-.039a81.515 81.515 0 0 1 1.597 0c.163 0 .325.019.483.056.288.073.445.318.411.617-.034.298-.214.477-.515.487-.424.014-.848.004-1.272.004zm7.588 8.417H4.292a2.464 2.464 0 0 1-.326-.007c-.294-.04-.48-.209-.508-.506-.029-.298.11-.501.391-.606.179-.065.365-.051.549-.051 3.347 0 6.695.005 10.042-.006 1.174-.004 2.187-.439 2.993-1.3.69-.738 1.053-1.63 1.16-2.635.085-.788-.027-1.513-.516-2.156-.544-.718-1.28-1.078-2.163-1.082-3.163-.013-6.328-.005-9.487-.01-.336 0-.673-.027-1.007-.058-.29-.027-.45-.201-.469-.492-.021-.317.141-.545.429-.6a1.55 1.55 0 0 1 .29-.015h10.177c1.71.004 3.187 1.038 3.726 2.654.383 1.147.246 2.304-.182 3.416-.824 2.135-2.762 3.448-5.055 3.454-1.652.005-3.304 0-4.956 0zm2.906-13.568c1.533 0 3.066-.008 4.598 0 2.935.018 5.629 1.892 6.653 4.626.442 1.181.538 2.403.412 3.657-.185 1.842-.735 3.552-1.776 5.084-1.608 2.365-3.873 3.68-6.679 4.118-.95.148-1.905.13-2.86.13-.397 0-.61-.181-.633-.51-.025-.351.196-.621.587-.645.434-.026.87-.004 1.305-.016 2.641-.072 4.928-.982 6.74-2.935 1.269-1.37 1.912-3.039 2.13-4.878.151-1.275.135-2.544-.37-3.752-.773-1.85-2.159-2.983-4.068-3.509-.74-.204-1.5-.243-2.26-.247-2.837-.017-5.675-.007-8.511-.007-.12 0-.24.004-.359-.006a.57.57 0 0 1-.517-.536.557.557 0 0 1 .456-.557c.13-.018.261-.024.392-.019h4.762Z"/>
<path fill="#29F1FB" d="M8.932 20.806c-.369 0-.738.007-1.109 0-.35-.007-.587-.206-.623-.5a.587.587 0 0 1 .53-.636c.79-.062 1.582-.063 2.372-.003a.548.548 0 0 1 .522.602c-.024.326-.253.526-.616.54zM1.792 8.345c-.392 0-.782.008-1.173.002-.327-.006-.577-.22-.614-.512-.037-.293.146-.544.499-.615.192-.032.388-.045.583-.039a81.515 81.515 0 0 1 1.597 0c.163 0 .325.019.483.056.288.073.445.318.411.617-.034.298-.214.477-.515.487-.424.014-.848.004-1.272.004zm7.588 8.417H4.292a2.464 2.464 0 0 1-.326-.007c-.294-.04-.48-.209-.508-.506-.029-.298.11-.501.391-.606.179-.065.365-.051.549-.051 3.347 0 6.695.005 10.042-.006 1.174-.004 2.187-.439 2.993-1.3.69-.738 1.053-1.63 1.16-2.635.085-.788-.027-1.513-.516-2.156-.544-.718-1.28-1.078-2.163-1.082-3.163-.013-6.328-.005-9.487-.01-.336 0-.673-.027-1.007-.058-.29-.027-.45-.201-.469-.492-.021-.317.141-.545.429-.6a1.55 1.55 0 0 1 .29-.015h10.177c1.71.004 3.187 1.038 3.726 2.654.383 1.147.246 2.304-.182 3.416-.824 2.135-2.762 3.448-5.055 3.454-1.652.005-3.304 0-4.956 0zm2.906-13.568c1.533 0 3.066-.008 4.598 0 2.935.018 5.629 1.892 6.653 4.626.442 1.181.538 2.403.412 3.657-.185 1.842-.735 3.552-1.776 5.084-1.608 2.365-3.873 3.68-6.679 4.118-.95.148-1.905.13-2.86.13-.397 0-.61-.181-.633-.51-.025-.351.196-.621.587-.645.434-.026.87-.004 1.305-.016 2.641-.072 4.928-.982 6.74-2.935 1.269-1.37 1.912-3.039 2.13-4.878.151-1.275.135-2.544-.37-3.752-.773-1.85-2.159-2.983-4.068-3.509-.74-.204-1.5-.243-2.26-.247-2.837-.017-5.675-.007-8.511-.007-.12 0-.24.004-.359-.006a.57.57 0 0 1-.517-.536.557.557 0 0 1 .456-.557c.13-.018.261-.024.392-.019h4.762Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -1 +1,3 @@
<svg role="img" viewBox="-2.625 3 28 28" xmlns="http://www.w3.org/2000/svg"><path fill="#7A8291" d="M.36 8.6h16.7v5.6H6.04c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h11.02v5.6h-5.33c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h4.98c.2 0 .35-.15.35-.35V25.4h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34v-5.6h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34V3.35c0-.2-.16-.35-.35-.35H.36c-.2 0-.36.16-.36.35v4.9c0 .2.16.35.36.35Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="#9CA3AF" height="34" viewBox="0 0 131 34">
<path fill="#9CA3AF" d="M.36 8.6h16.7v5.6H6.04c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h11.02v5.6h-5.33c-.2 0-.35.16-.35.35v4.9c0 .2.16.35.35.35h4.98c.2 0 .35-.15.35-.35V25.4h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34v-5.6h5.34c.2 0 .35-.16.35-.35v-4.9c0-.2-.16-.35-.35-.35h-5.34V3.35c0-.2-.16-.35-.35-.35H.36c-.2 0-.36.16-.36.35v4.9c0 .2.16.35.36.35ZM44.41 14.7c-.5-.5-1.1-.9-1.76-1.18a5.62 5.62 0 0 0-4.6.17c-.73.37-1.32.91-1.75 1.62h-.17V8.59H34.1v16.83h2.04v-1.81h.17c.21.36.47.67.77.94.31.25.65.48 1.01.67.37.18.77.31 1.18.39a6.2 6.2 0 0 0 3.39-.24 5.36 5.36 0 0 0 3.02-3.1c.29-.75.44-1.62.44-2.6v-.47c0-.96-.16-1.83-.47-2.58-.3-.75-.7-1.4-1.23-1.9v-.01Zm-5.87.66a3.9 3.9 0 0 1 4.34.84c.36.35.64.8.83 1.3.2.5.3 1.07.3 1.7v.47c0 .64-.1 1.23-.3 1.74a3.75 3.75 0 0 1-2.06 2.15 4.27 4.27 0 0 1-3.12-.03 3.86 3.86 0 0 1-2.09-2.2c-.2-.52-.3-1.11-.3-1.75v-.29c0-.62.1-1.2.3-1.7v-.01c.21-.53.5-.99.84-1.36.36-.37.78-.66 1.26-.86ZM97.04 8.59H95v4.86h-2.94v1.86H95v8.17c0 .56.17 1.03.53 1.4.37.35.84.54 1.4.54h4.18v-1.87h-3.5c-.2 0-.33-.05-.43-.15-.1-.1-.14-.27-.14-.49v-7.6h4.65v-1.86h-4.65V8.59ZM114.61 15a5.48 5.48 0 0 0-1.8-1.33 5.6 5.6 0 0 0-2.57-.56 6.17 6.17 0 0 0-4.26 1.7 5.6 5.6 0 0 0-1.72 4.2v.57c0 .9.15 1.75.44 2.5a5.58 5.58 0 0 0 5.5 3.67c1.55 0 2.8-.35 3.72-1.04a5.35 5.35 0 0 0 1.91-2.73l.03-.07-1.94-.52-.02.07c-.11.33-.27.64-.46.94-.17.27-.4.52-.7.74-.28.22-.63.39-1.04.51-.41.13-.9.19-1.46.19a3.8 3.8 0 0 1-2.84-1.05 4.07 4.07 0 0 1-1.1-2.7h9.68v-1.6c0-.54-.11-1.12-.34-1.75a5.04 5.04 0 0 0-1.03-1.74Zm-8.25 3.21a3.8 3.8 0 0 1 1.22-2.25 4.19 4.19 0 0 1 3.99-.7c.44.16.83.38 1.17.66.34.27.62.62.82 1.02.21.38.34.8.38 1.27h-7.58ZM129.09 14.42a4.47 4.47 0 0 0-3.37-1.3c-.93 0-1.73.2-2.4.59-.64.39-1.15.97-1.52 1.74h-.17v-2h-2.04v11.97h2.04v-6.23c0-1.26.32-2.28.95-3.02a3.31 3.31 0 0 1 2.65-1.14c.94 0 1.7.3 2.24.9.56.6.83 1.52.83 2.74v6.75h2.04v-7.13c0-1.71-.42-3.02-1.25-3.87ZM88.1 15a5.48 5.48 0 0 0-1.78-1.33 5.6 5.6 0 0 0-2.58-.56 6.17 6.17 0 0 0-4.27 1.7 5.59 5.59 0 0 0-1.71 4.2v.56c0 .92.14 1.76.44 2.51a5.6 5.6 0 0 0 5.5 3.67c1.55 0 2.8-.35 3.72-1.04a5.36 5.36 0 0 0 1.91-2.73l.03-.07-1.94-.52-.03.07c-.1.32-.26.64-.45.94-.17.27-.4.52-.7.74-.29.21-.64.39-1.05.51-.4.12-.9.19-1.45.19a3.8 3.8 0 0 1-2.85-1.05 4.07 4.07 0 0 1-1.09-2.7h9.68v-1.61c0-.53-.12-1.12-.34-1.74A5.03 5.03 0 0 0 88.1 15Zm-8.24 3.21a3.83 3.83 0 0 1 1.22-2.25 4.2 4.2 0 0 1 3.99-.7c.44.16.83.38 1.16.66.35.27.62.62.83 1.02.2.38.33.8.37 1.27h-7.57ZM73.65 19.42a6.11 6.11 0 0 0-3.23-1.02 6.63 6.63 0 0 1-2.68-.58c-.47-.3-.7-.7-.7-1.25 0-.27.08-.5.21-.7.14-.2.33-.38.56-.52a4.05 4.05 0 0 1 1.78-.42c.85 0 1.54.21 2.06.63.53.41.83 1 .91 1.73l.01.1 1.95-.47-.01-.07c-.07-.45-.22-.91-.45-1.36a3.46 3.46 0 0 0-.94-1.2 4.6 4.6 0 0 0-1.52-.84 6.05 6.05 0 0 0-2.1-.34c-.58 0-1.13.07-1.65.22-.52.14-1 .36-1.43.65-.41.3-.74.66-.99 1.1a2.9 2.9 0 0 0-.37 1.49v.14c0 1.06.38 1.87 1.14 2.42a6.2 6.2 0 0 0 3.24.97c1.18.08 2.04.26 2.56.54.5.28.75.72.75 1.36 0 .6-.25 1.06-.78 1.4-.52.32-1.22.48-2.1.48a3.68 3.68 0 0 1-2.46-.79 3.13 3.13 0 0 1-1.04-2.14v-.09l-1.93.46h-.02v.07a4.4 4.4 0 0 0 3.1 4c.7.24 1.52.36 2.46.36.7 0 1.35-.09 1.93-.27.6-.16 1.12-.4 1.53-.72A3.38 3.38 0 0 0 74.8 22v-.14c0-1.07-.39-1.9-1.14-2.44ZM60.25 23.4c-.1-.1-.14-.27-.14-.49v-9.46h-2.05v1.85h-.16a3.78 3.78 0 0 0-1.61-1.63 4.62 4.62 0 0 0-2.26-.56c-.77 0-1.5.14-2.19.41a5.27 5.27 0 0 0-3.02 3.12c-.29.75-.44 1.63-.44 2.6v.38c0 .99.15 1.87.44 2.63.3.75.7 1.4 1.2 1.93a5.48 5.48 0 0 0 4.05 1.57c.8 0 1.51-.2 2.2-.58.69-.38 1.24-.97 1.63-1.75h.16v.06c0 .56.18 1.03.53 1.4.37.35.85.54 1.41.54h1.36v-1.87h-.68c-.2 0-.34-.05-.43-.15Zm-4.46.13c-.46.2-.97.3-1.52.3a3.68 3.68 0 0 1-2.75-1.09 4.42 4.42 0 0 1-1.05-3.12v-.38c0-.62.1-1.2.29-1.71a3.65 3.65 0 0 1 5-2.17c.47.2.88.49 1.21.86.35.37.62.83.8 1.36.2.5.3 1.08.3 1.7v.3c0 .63-.1 1.23-.3 1.76-.18.5-.45.96-.78 1.33-.33.37-.73.66-1.2.86Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -1 +0,0 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#9CA3AF" d="M12 22.596c6.628 0 12-4.338 12-9.688 0-3.318-2.057-6.248-5.219-7.986-1.286-.715-2.297-1.357-3.139-1.89C14.058 2.025 13.08 1.404 12 1.404c-1.097 0-2.334.785-3.966 1.821a49.92 49.92 0 0 1-2.816 1.697C2.057 6.66 0 9.59 0 12.908c0 5.35 5.372 9.687 12 9.687v.001ZM10.599 4.715c.334-.759.503-1.58.498-2.409 0-.145.202-.187.23-.029.658 2.783-.902 4.162-2.057 4.624-.124.048-.199-.121-.103-.209a5.763 5.763 0 0 0 1.432-1.977Zm2.058-.102a5.82 5.82 0 0 0-.782-2.306v-.016c-.069-.123.086-.263.185-.172 1.962 2.111 1.307 4.067.556 5.051-.082.103-.23-.003-.189-.126a5.85 5.85 0 0 0 .23-2.431Zm1.776-.561a5.727 5.727 0 0 0-1.612-1.806v-.014c-.112-.085-.024-.274.114-.218 2.595 1.087 2.774 3.18 2.459 4.407a.116.116 0 0 1-.049.071.11.11 0 0 1-.153-.026.122.122 0 0 1-.022-.083 5.891 5.891 0 0 0-.737-2.331Zm-5.087.561c-.617.546-1.282.76-2.063 1-.117 0-.195-.078-.156-.181 1.752-.909 2.376-1.649 2.999-2.778 0 0 .155-.118.188.085 0 .304-.349 1.329-.968 1.874Zm4.945 11.237a2.957 2.957 0 0 1-.937 1.553c-.346.346-.8.565-1.286.62a2.178 2.178 0 0 1-1.327-.62 2.955 2.955 0 0 1-.925-1.553.244.244 0 0 1 .064-.198.234.234 0 0 1 .193-.069h3.965a.226.226 0 0 1 .19.07c.05.053.073.125.063.197Zm-5.458-2.176a1.862 1.862 0 0 1-2.384-.245 1.98 1.98 0 0 1-.233-2.447c.207-.319.503-.566.848-.713a1.84 1.84 0 0 1 1.092-.11c.366.075.703.261.967.531a1.98 1.98 0 0 1 .408 2.114 1.931 1.931 0 0 1-.698.869v.001Zm8.495.005a1.86 1.86 0 0 1-2.381-.253 1.964 1.964 0 0 1-.547-1.366c0-.384.11-.76.32-1.079.207-.319.503-.567.849-.713a1.844 1.844 0 0 1 1.093-.108c.367.076.704.262.968.534a1.98 1.98 0 0 1 .4 2.117 1.932 1.932 0 0 1-.702.868Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#37814A" d="M2.303 0A2.298 2.298 0 0 0 0 2.303v19.394A2.298 2.298 0 0 0 2.303 24h19.394A2.298 2.298 0 0 0 24 21.697V2.303A2.298 2.298 0 0 0 21.697 0zm8.177 3.072c4.098 0 7.028 1.438 7.68 1.764l-1.194 2.55c-2.442-1.057-4.993-1.41-5.672-1.41-1.574 0-2.17.922-2.17 1.763v8.494c0 .869.596 1.791 2.17 1.791.679 0 3.23-.38 5.672-1.41l1.194 2.496c-.435.271-3.637 1.818-7.68 1.818-1.112 0-4.64-.244-4.64-4.64V7.713c0-4.397 3.528-4.64 4.64-4.64z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" id="图层_1" x="0" y="0" version="1.1" viewBox="0 0 519.27 214.14"><style>.st2{fill:#a9cc54}</style><path d="M493.54 171.25c-35.63 0-64.52-28.89-64.52-64.52s28.89-64.52 64.52-64.52c3.85 0 7.62.36 11.29 1L493.54 5.45H104.46v.02C49.24 6.51 4.78 51.59 4.78 107.07c0 55.47 44.45 100.56 99.68 101.59v.02h389.07l11.29-38.44c-3.66.65-7.43 1.01-11.28 1.01" style="fill:#b6de64"/><defs><path id="SVGID_1_" d="M493.54 171.25c-35.63 0-64.52-28.89-64.52-64.52s28.89-64.52 64.52-64.52c3.85 0 7.62.36 11.29 1V5.45H104.46v.02C49.24 6.51 4.78 51.59 4.78 107.07c0 55.47 44.45 100.56 99.68 101.59v.02h389.07l11.29-38.44c-3.66.65-7.43 1.01-11.28 1.01"/></defs><clipPath id="SVGID_2_"><use xlink:href="#SVGID_1_" style="overflow:visible"/></clipPath><g style="clip-path:url(#SVGID_2_)"><path d="M-23.16 55.58H428.5v51.1H-23.16zM-23.16 157.59h530.98v72.35H-23.16z" class="st2"/></g><path d="M493.54 184.23c-43.94 0-79.56-33.62-79.56-77.56s35.62-76.56 79.56-76.56" style="fill:none;stroke:#ddf4a4;stroke-width:49;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10"/></svg>

Before

Width:  |  Height:  |  Size: 529 B

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@@ -1,5 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="lucide/github">
<path id="Vector" d="M15 22V18C15.1391 16.7473 14.7799 15.4901 14 14.5C17 14.5 20 12.5 20 9C20.08 7.75 19.73 6.52 19 5.5C19.28 4.35 19.28 3.15 19 2C19 2 18 2 16 3.5C13.36 3 10.64 3 8 3.5C6 2 5 2 5 2C4.7 3.15 4.7 4.35 5 5.5C4.27187 6.51588 3.91847 7.75279 4 9C4 12.5 7 14.5 10 14.5C9.61 14.99 9.32 15.55 9.15 16.15C8.98 16.75 8.93 17.38 9 18M9 18V22M9 18C4.49 20 4 16 2 16" stroke="#7A8291" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
<path id="Vector" d="M15 22V18C15.1391 16.7473 14.7799 15.4901 14 14.5C17 14.5 20 12.5 20 9C20.08 7.75 19.73 6.52 19 5.5C19.28 4.35 19.28 3.15 19 2C19 2 18 2 16 3.5C13.36 3 10.64 3 8 3.5C6 2 5 2 5 2C4.7 3.15 4.7 4.35 5 5.5C4.27187 6.51588 3.91847 7.75279 4 9C4 12.5 7 14.5 10 14.5C9.61 14.99 9.32 15.55 9.15 16.15C8.98 16.75 8.93 17.38 9 18M9 18V22M9 18C4.49 20 4 16 2 16" stroke="#C0C1C3" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 587 B

After

Width:  |  Height:  |  Size: 587 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#A68400" d="m0 0 11.955 24L24 0zm13.366 4.827h1.393v1.38h-1.393zm-2.77 5.569H9.22V8.993h1.389zm0-2.087H9.22V6.906h1.389zm0-2.086H9.22V4.819h1.389zm2.087 6.263h-1.377V11.08h1.388zm0-2.09h-1.377V8.993h1.388zm0-2.087h-1.377V6.906h1.388zm0-2.086h-1.377V4.819h1.388zm.683.683h1.393v1.389h-1.393zm0 3.475V8.993h1.389v1.388Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="#FFEC6E" d="m0 0 11.955 24L24 0zm13.366 4.827h1.393v1.38h-1.393zm-2.77 5.569H9.22V8.993h1.389zm0-2.087H9.22V6.906h1.389zm0-2.086H9.22V4.819h1.389zm2.087 6.263h-1.377V11.08h1.388zm0-2.09h-1.377V8.993h1.388zm0-2.087h-1.377V6.906h1.388zm0-2.086h-1.377V4.819h1.388zm.683.683h1.393v1.389h-1.393zm0 3.475V8.993h1.389v1.388Z"/></svg>

Before

Width:  |  Height:  |  Size: 398 B

After

Width:  |  Height:  |  Size: 398 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="285" height="285" preserveAspectRatio="xMidYMid" viewBox="-14.5 0 285 285"><path fill="#7C4DD6" d="M230.4 0c13.996 0 25.37 11.232 25.597 25.177l.003.423v233.244c0 13.996-11.235 25.37-25.177 25.597l-.423.003H25.6c-13.996 0-25.37-11.235-25.597-25.176L0 258.844V25.6C0 11.601 11.235.23 25.177.003L25.6 0zm0 14.222H25.6c-6.174 0-11.218 4.948-11.374 11.085l-.004.293v233.244c0 6.174 4.948 11.218 11.085 11.374l.293.004h204.8c6.177 0 11.218-4.947 11.374-11.084l.004-.294V25.6c0-6.272-5.102-11.378-11.378-11.378M64 184.89l32 28.444-32 28.445zM92.444 42.667v80.75c14.201-4.622 34.052-9.64 53.334-9.64 17.582 0 28.106 6.913 33.838 12.712 12.109 12.25 12.397 27.82 12.381 29.897v.058l.003 85.334h-28.444v-84.942c-.14-6.639-3.34-14.614-17.778-14.614-28.632 0-60.714 14.257-61.666 14.683l-.02.009L64 166.016V42.666zm99.556 0c-1.92 16.156-8.47 31.644-21.333 46.222h-28.445c11.179-14.667 18.198-30.101 21.334-46.222Z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="285" height="285" preserveAspectRatio="xMidYMid" viewBox="-14.5 0 285 285"><path fill="#430098" d="M230.4 0c13.996 0 25.37 11.232 25.597 25.177l.003.423v233.244c0 13.996-11.235 25.37-25.177 25.597l-.423.003H25.6c-13.996 0-25.37-11.235-25.597-25.176L0 258.844V25.6C0 11.601 11.235.23 25.177.003L25.6 0zm0 14.222H25.6c-6.174 0-11.218 4.948-11.374 11.085l-.004.293v233.244c0 6.174 4.948 11.218 11.085 11.374l.293.004h204.8c6.177 0 11.218-4.947 11.374-11.084l.004-.294V25.6c0-6.272-5.102-11.378-11.378-11.378M64 184.89l32 28.444-32 28.445zM92.444 42.667v80.75c14.201-4.622 34.052-9.64 53.334-9.64 17.582 0 28.106 6.913 33.838 12.712 12.109 12.25 12.397 27.82 12.381 29.897v.058l.003 85.334h-28.444v-84.942c-.14-6.639-3.34-14.614-17.778-14.614-28.632 0-60.714 14.257-61.666 14.683l-.02.009L64 166.016V42.666zm99.556 0c-1.92 16.156-8.47 31.644-21.333 46.222h-28.445c11.179-14.667 18.198-30.101 21.334-46.222Z"/></svg>

Before

Width:  |  Height:  |  Size: 958 B

After

Width:  |  Height:  |  Size: 958 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 463 B

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -1 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M14 10h-4v4h4v-4zM18 6h-4v4.001h4v-4zM18 14h-4v4h4v-4zM22 2h-4v4h4V2zM22 18h-4v4h4v-4z" fill="#1FD5F9"></path><path d="M6 18V2H2v20h12v-4H6z" fill="#7A8291"></path></svg>
<svg height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>LiveKit</title><path d="M14 10h-4v4h4v-4zM18 6h-4v4.001h4v-4zM18 14h-4v4h4v-4zM22 2h-4v4h4V2zM22 18h-4v4h4v-4z" fill="#1FD5F9"></path><path d="M6 18V2H2v20h12v-4H6z" fill="#fff"></path></svg>

Before

Width:  |  Height:  |  Size: 251 B

After

Width:  |  Height:  |  Size: 316 B

View File

@@ -1 +0,0 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#F25022" d="M1 1h10.5v10.5H1z"/><path fill="#7FBA00" d="M12.5 1H23v10.5H12.5z"/><path fill="#00A4EF" d="M1 12.5h10.5V23H1z"/><path fill="#FFB900" d="M12.5 12.5H23V23H12.5z"/></svg>

Before

Width:  |  Height:  |  Size: 264 B

View File

@@ -1 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#7A8291" d="M22 24H2V0h20zM17 4.8H7v14.4h10z"/></svg>
<svg width='234' height='42' viewBox='0 0 234 42' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M18 30H6V18H18V30Z' fill='#CFCECD'/><path d='M18 12H6V30H18V12ZM24 36H0V6H24V36Z' fill='#656363'/><path d='M48 30H36V18H48V30Z' fill='#CFCECD'/><path d='M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z' fill='#656363'/><path d='M84 24V30H66V24H84Z' fill='#CFCECD'/><path d='M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z' fill='#656363'/><path d='M108 36H96V18H108V36Z' fill='#CFCECD'/><path d='M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z' fill='#656363'/><path d='M144 30H126V18H144V30Z' fill='#CFCECD'/><path d='M144 12H126V30H144V36H120V6H144V12Z' fill='#211E1E'/><path d='M168 30H156V18H168V30Z' fill='#CFCECD'/><path d='M168 12H156V30H168V12ZM174 36H150V6H174V36Z' fill='#211E1E'/><path d='M198 30H186V18H198V30Z' fill='#CFCECD'/><path d='M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z' fill='#211E1E'/><path d='M234 24V30H216V24H234Z' fill='#CFCECD'/><path d='M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z' fill='#211E1E'/></svg>

Before

Width:  |  Height:  |  Size: 137 B

After

Width:  |  Height:  |  Size: 1.0 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@@ -1 +1 @@
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path fill="#7A8291" d="M18.263.007c-3.121-.147-5.744 2.109-6.192 5.082-.018.138-.045.272-.067.405-.696 3.703-3.936 6.507-7.827 6.507-1.388 0-2.691-.356-3.825-.979a.2024.2024 0 0 0-.302.178V24H12v-8.999c0-1.656 1.338-3 2.987-3h2.988c3.382 0 6.103-2.817 5.97-6.244-.12-3.084-2.61-5.603-5.682-5.75"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Render</title><path d="M18.263.007c-3.121-.147-5.744 2.109-6.192 5.082-.018.138-.045.272-.067.405-.696 3.703-3.936 6.507-7.827 6.507a7.9 7.9 0 0 1-3.825-.979.202.202 0 0 0-.302.178V24H12v-8.999c0-1.656 1.338-3 2.987-3h2.988c3.382 0 6.103-2.817 5.97-6.244-.12-3.084-2.61-5.603-5.682-5.75"/></svg>

Before

Width:  |  Height:  |  Size: 376 B

After

Width:  |  Height:  |  Size: 362 B

View File

@@ -1 +1 @@
<svg role="img" viewBox="89.7 190 220 220" xmlns="http://www.w3.org/2000/svg"><linearGradient id="a" x1="128.046" x2="313.226" y1="579.207" y2="472.293" gradientTransform="translate(-20.96 -225.764)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#10e7ff"/><stop offset=".103" style="stop-color:#33d5ff"/><stop offset=".255" style="stop-color:#60bdff"/><stop offset=".408" style="stop-color:#86aaff"/><stop offset=".56" style="stop-color:#a39aff"/><stop offset=".71" style="stop-color:#b790ff"/><stop offset=".858" style="stop-color:#c489ff"/><stop offset="1" style="stop-color:#c887ff"/></linearGradient><path d="m199.7 408.2-92.2-54.1V245.9l92.2-54.1 92.2 54.1v108.2zm-79.4-61.7 79.1 46.5 79.1-46.5v-92.9l-79-46.5-79.3 46.5c.1-.1.1 92.9.1 92.9" style="fill:url(#a)"/><linearGradient id="b" x1="185.354" x2="277.202" y1="542.013" y2="488.984" gradientTransform="translate(-20.96 -225.764)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#10e7ff"/><stop offset=".103" style="stop-color:#33d5ff"/><stop offset=".255" style="stop-color:#60bdff"/><stop offset=".408" style="stop-color:#86aaff"/><stop offset=".56" style="stop-color:#a39aff"/><stop offset=".71" style="stop-color:#b790ff"/><stop offset=".858" style="stop-color:#c489ff"/><stop offset="1" style="stop-color:#c887ff"/></linearGradient><path d="m239.4 270.6-32.3 56.8 10.5 18.5 42.8-75.3zm-36.1 0L171 327.4l10.5 18.5 42.8-75.3zm-64.4 0 42.8 75.3 10.5-18.5-32.3-56.8zm0 0 42.8 75.3 10.5-18.5-32.3-56.8zm64.4 0L171 327.4l10.5 18.5 42.8-75.3z" style="fill:url(#b)"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" version="1.0" viewBox="0 0 800 600"><path d="M364.7 246.1h16.9L363.2 300h-16.9l-18.5-53.9h17.1l10.2 33h.2zM436 300h-39.4v-53.9h39.2v13.6h-23.6v7.1h20.9V279h-20.9v7.4H436zm41.6-54.8c11.3 0 19.6 5.8 22.1 15.1l-14.7 6.4c-.7-4.2-2.9-7.4-7.6-7.4-5.3 0-9.3 4.4-9.3 14s3.8 14.2 9.3 14.2c4.5 0 6.9-3.4 7.6-7.6l14.7 6.4c-2.5 9.3-10.9 15.1-22.1 15.1-16.2 0-26.1-10.7-26.1-28s9.9-28.2 26.1-28.2m50.3 54.8v-40.3h-16.2v-13.6h48.5v13.6H544V300zm97.7-26.9c0 17.2-10 28-26.1 28-16 0-26.1-10.7-26.1-28s10.2-28 26.1-28c16.1.1 26.1 10.8 26.1 28m-35.4 0c0 8.7 3.3 14.2 9.4 14.2s9.4-5.3 9.4-14.2c0-8.7-3.3-14.2-9.4-14.2s-9.4 5.3-9.4 14.2m84.4 26.9c-1.5-1.8-2-3.4-2.5-6.4l-1.1-6c-.7-3.8-2.5-4.9-5.8-4.9h-6.4V300h-16v-53.9h25.6c12.2 0 20.7 4.7 20.7 16.5 0 7.8-5.3 12.3-11.1 13.4v.2c4.7.7 7.4 3.6 8.5 8.5l2 8.5c.7 2.9 1.6 4.7 3.4 6.7.1.1-17.3.1-17.3.1m-8.2-40.7h-7.8V270h7.8c3.8 0 5.8-1.6 5.8-5.4 0-3.5-1.8-5.3-5.8-5.3m-321.5 81.2c3.3.4 5.6 2.4 5.6 6 0 4.4-2.7 7.3-8.7 7.3h-9.3v-26.1h8.9c5.1 0 8.2 2.4 8.2 6.9 0 3.1-2 5.3-4.7 5.9m-9.1-1.1h5.4c3.3 0 5.1-1.5 5.1-4.5s-1.8-4.5-5.1-4.5h-5.4zm0 11.6h5.8c3.6 0 5.4-1.6 5.4-4.5s-2-4.5-5.4-4.5h-5.8zm28-10.7 7.1-12.7h3.4l-8.9 15.8v10.2h-3.3v-10.2l-9.1-15.8h3.6zm26.7-12.7h7.6c7.3 0 12.7 4 12.7 12.9s-5.4 13.1-12.7 13.1h-7.6zm3.2 23.2h4.4c4.7 0 9.3-2.5 9.3-10.3s-4.5-10.2-9.3-10.2h-4.4zm36.2-4.3H419l-2.5 7.3h-3.3l9.4-26.1h3.6l9.4 26.1h-3.3zm-10-2.9h8.9l-4.4-12.5zm24.1 10.1v-23.2h-8.7v-2.9h20.9v2.9h-8.7v23.2zm28.3-7.2h-10.9l-2.5 7.3h-3.3l9.4-26.1h3.6l9.4 26.1h-3.3zm-9.8-2.9h8.9l-4.4-12.5zm20.7-16h7.6c7.3 0 12.7 4 12.7 12.9s-5.4 13.1-12.7 13.1h-7.6zm3.3 23.2h4.4c4.7 0 9.3-2.5 9.3-10.3s-4.5-10.2-9.3-10.2h-4.4zm44.1-10.1c0 8.4-4.5 13.4-11.3 13.4S508 349 508 340.7c0-8.4 4.5-13.4 11.3-13.4s11.3 5 11.3 13.4m-19 0c0 6.4 2.7 10.7 7.8 10.7s7.8-4.4 7.8-10.7c0-6.4-2.7-10.7-7.8-10.7s-7.8 4.3-7.8 10.7m45-.2v13.1H554l-.5-3.8c-.9 2.7-3.8 4.4-7.3 4.4-6.5 0-11.1-4.9-11.1-13.4 0-8.7 4.9-13.6 11.4-13.6 4.7 0 8 2 9.4 6l-2.9 1.6c-.9-3.1-3.4-4.9-6.7-4.9-5.1 0-8 4-8 10.7 0 6.4 2.5 10.7 8 10.7 3.4 0 6.7-1.8 6.7-6.5V343h-6.4v-2.7h9.8z"/><linearGradient id="a" x1="128.046" x2="313.226" y1="579.207" y2="472.293" gradientTransform="translate(-20.96 -225.764)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#10e7ff"/><stop offset=".103" style="stop-color:#33d5ff"/><stop offset=".255" style="stop-color:#60bdff"/><stop offset=".408" style="stop-color:#86aaff"/><stop offset=".56" style="stop-color:#a39aff"/><stop offset=".71" style="stop-color:#b790ff"/><stop offset=".858" style="stop-color:#c489ff"/><stop offset="1" style="stop-color:#c887ff"/></linearGradient><path d="m199.7 408.2-92.2-54.1V245.9l92.2-54.1 92.2 54.1v108.2zm-79.4-61.7 79.1 46.5 79.1-46.5v-92.9l-79-46.5-79.3 46.5c.1-.1.1 92.9.1 92.9" style="fill:url(#a)"/><linearGradient id="b" x1="185.354" x2="277.202" y1="542.013" y2="488.984" gradientTransform="translate(-20.96 -225.764)" gradientUnits="userSpaceOnUse"><stop offset="0" style="stop-color:#10e7ff"/><stop offset=".103" style="stop-color:#33d5ff"/><stop offset=".255" style="stop-color:#60bdff"/><stop offset=".408" style="stop-color:#86aaff"/><stop offset=".56" style="stop-color:#a39aff"/><stop offset=".71" style="stop-color:#b790ff"/><stop offset=".858" style="stop-color:#c489ff"/><stop offset="1" style="stop-color:#c887ff"/></linearGradient><path d="m239.4 270.6-32.3 56.8 10.5 18.5 42.8-75.3zm-36.1 0L171 327.4l10.5 18.5 42.8-75.3zm-64.4 0 42.8 75.3 10.5-18.5-32.3-56.8zm0 0 42.8 75.3 10.5-18.5-32.3-56.8zm64.4 0L171 327.4l10.5 18.5 42.8-75.3z" style="fill:url(#b)"/></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -17,10 +17,7 @@ function ModalFixture(): JSX.Element {
return (
<>
<Button
variant="solid"
color="primary"
size="md"
testId="open-add-credit-card"
data-testid="open-add-credit-card"
onClick={(): void => setOpen(true)}
>
Contact support

View File

@@ -8,10 +8,6 @@
:global(.ant-breadcrumb-separator) {
color: var(--muted-foreground);
}
[data-slot='button'] {
padding: 0px;
}
}
.divider {

View File

@@ -1,5 +1,9 @@
.itemLast {
color: var(--button-secondary-label);
font-size: var(--button-font-size, var(--periscope-font-size-base));
line-height: var(--button-size-line-height, 16px);
.item {
--button-padding: 0;
--button-font-size: var(--periscope-font-size-base);
}
.itemLast {
color: var(--muted-foreground);
font-size: var(--periscope-font-size-base);
}

View File

@@ -26,9 +26,9 @@ function BreadcrumbItem({
return (
<Button
size="md"
variant="link"
variant="ghost"
color="secondary"
className={styles.item}
onClick={(e: React.MouseEvent): void => {
if (!('route' in props) || !props.route) {
return;

View File

@@ -34,23 +34,21 @@ function ErrorEmptyState({
</div>
<div className={styles.actions}>
<Button
size="md"
variant="solid"
color="secondary"
prefix={<LifeBuoy size={14} />}
onClick={onContactSupport}
testId="error-contact-support-button"
data-testid="error-contact-support-button"
>
Contact Support
</Button>
{onRefresh && (
<Button
size="md"
variant="outlined"
color="secondary"
prefix={<RefreshCw size={14} />}
onClick={onRefresh}
testId="error-refresh-button"
data-testid="error-refresh-button"
>
Refresh
</Button>

View File

@@ -1,32 +1,20 @@
.labelColumn {
// Badges fill their wrapper; the wrapper, as the flex item, owns the cap.
--badge-max-width: 100%;
position: relative;
display: flex;
gap: 4px;
align-items: center;
overflow: hidden;
max-width: 100%;
width: 100%;
> * {
flex-shrink: 0;
max-width: 180px;
}
}
// Kept mounted so its width can still be measured.
.overflowed {
position: absolute;
visibility: hidden;
pointer-events: none;
}
.labelBadge {
cursor: default;
font-size: 12px;
// The only visible label truncates instead of pushing `+N` out of view.
.labelColumn > .shrinkable {
flex-shrink: 1;
min-width: 0;
--badge-display: inline;
max-width: 180px;
text-overflow: ellipsis;
}
.overflowTrigger {
@@ -34,6 +22,11 @@
cursor: pointer;
}
.overflowBadge {
cursor: pointer;
font-size: 12px;
}
.labelPopover {
display: flex;
flex-direction: column;

View File

@@ -1,14 +1,13 @@
import { TooltipProvider } from '@signozhq/ui/tooltip';
import { act, render, screen } from '@testing-library/react';
import LabelColumn from './LabelColumn';
const BADGE_WIDTH = 60;
let resizeCallbacks: ResizeObserverCallback[] = [];
let resizeCallback: ResizeObserverCallback | null = null;
class MockResizeObserver {
constructor(callback: ResizeObserverCallback) {
resizeCallbacks.push(callback);
resizeCallback = callback;
}
observe = jest.fn();
@@ -16,23 +15,15 @@ class MockResizeObserver {
disconnect = jest.fn();
}
// jsdom has no layout: every badge measures BADGE_WIDTH, the column `containerWidth`.
function mockLayout(containerWidth: number): void {
jest
.spyOn(HTMLElement.prototype, 'clientWidth', 'get')
.mockImplementation(function getClientWidth(this: HTMLElement) {
return this.dataset.testid === 'label-column' ? containerWidth : 0;
function triggerResize(width: number): void {
if (resizeCallback) {
act(() => {
resizeCallback?.(
[{ contentRect: { width } } as ResizeObserverEntry],
{} as ResizeObserver,
);
});
jest
.spyOn(HTMLElement.prototype, 'getBoundingClientRect')
.mockReturnValue({ width: BADGE_WIDTH } as DOMRect);
}
function triggerResize(containerWidth: number): void {
mockLayout(containerWidth);
act(() => {
resizeCallbacks.forEach((callback) => callback([], {} as ResizeObserver));
});
}
}
beforeAll(() => {
@@ -40,14 +31,13 @@ beforeAll(() => {
});
afterEach(() => {
resizeCallbacks = [];
jest.restoreAllMocks();
resizeCallback = null;
});
function renderWithProviders(
ui: React.ReactElement,
): ReturnType<typeof render> {
return render(ui);
return render(<TooltipProvider>{ui}</TooltipProvider>);
}
describe('LabelColumn', () => {
@@ -64,10 +54,13 @@ describe('LabelColumn', () => {
it('should truncate labels and show +N badge when container is narrow', () => {
const labels = ['env', 'service', 'region', 'team', 'owner', 'version'];
// 3 badges (180px) + overflow reserve (40px)
mockLayout(220);
renderWithProviders(<LabelColumn labels={labels} />);
// Simulate narrow container that fits ~3 badges
// Badge widths: env=37, service=65, region=58, team=44, owner=51, version=65
// 220px available = 3 badges (160px) + gaps (8px) + overflow (44px)
triggerResize(220);
// First 3 visible
expect(screen.getByTestId('label-tag-env')).toBeInTheDocument();
expect(screen.getByTestId('label-tag-service')).toBeInTheDocument();
@@ -100,37 +93,19 @@ describe('LabelColumn', () => {
expect(screen.getByTestId('label-tag-service')).toHaveTextContent('service');
});
it('should update the overflow count when the container resizes', () => {
it('should show overflow badge with remaining count when container is narrow', () => {
const labels = ['env', 'service', 'region', 'team', 'owner', 'version'];
mockLayout(220);
renderWithProviders(<LabelColumn labels={labels} />);
expect(screen.getByTestId('label-overflow-badge')).toHaveTextContent('+3');
// 2 badges (120px) + overflow reserve (40px)
triggerResize(160);
expect(screen.getByTestId('label-overflow-badge')).toHaveTextContent('+4');
triggerResize(1000);
expect(screen.queryByTestId('label-overflow-badge')).not.toBeInTheDocument();
});
it('should skip the overflow reserve when every label fits', () => {
const labels = ['env', 'service', 'region', 'team', 'owner', 'version'];
mockLayout(labels.length * BADGE_WIDTH);
renderWithProviders(<LabelColumn labels={labels} />);
expect(screen.queryByTestId('label-overflow-badge')).not.toBeInTheDocument();
});
// Simulate narrow container to trigger overflow (shows 3 labels)
// 220px fits first 3 badges before overflow
triggerResize(220);
it('should keep one label visible when even that one does not fit', () => {
const labels = ['env', 'service'];
mockLayout(50);
renderWithProviders(<LabelColumn labels={labels} />);
expect(screen.getByTestId('label-overflow-badge')).toHaveTextContent('+1');
// Overflow badge shows +3 (remaining labels)
const overflowBadge = screen.getByTestId('label-overflow-badge');
expect(overflowBadge).toBeInTheDocument();
expect(overflowBadge).toHaveTextContent('+3');
});
it('should render empty when no labels provided', () => {
@@ -151,9 +126,11 @@ describe('LabelColumn', () => {
it('should show all labels when container is wide enough', () => {
const labels = ['env', 'service', 'region', 'team', 'owner', 'version'];
mockLayout(1000);
renderWithProviders(<LabelColumn labels={labels} />);
// Simulate wide container
triggerResize(1000);
// All labels visible
labels.forEach((label) => {
expect(screen.getByTestId(`label-tag-${label}`)).toBeInTheDocument();

View File

@@ -1,19 +1,35 @@
import { Copy } from '@signozhq/icons';
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
import { Badge } from '@signozhq/ui/badge';
import { toast } from '@signozhq/ui/sonner';
import { Tooltip } from '@signozhq/ui/tooltip';
import cx from 'classnames';
import { useLayoutEffect, useRef, useState } from 'react';
import {
TooltipContent,
TooltipRoot,
TooltipTrigger,
} from '@signozhq/ui/tooltip';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import LabelTag from './LabelTag';
import styles from './LabelColumn.module.scss';
import { getVisibleCount } from './utils';
import { BADGE_GAP, estimateBadgeWidth, OVERFLOW_BADGE_WIDTH } from './utils';
export interface LabelColumnProps {
labels: string[];
color?: BadgeColorType;
color?:
| 'primary'
| 'secondary'
| 'success'
| 'error'
| 'warning'
| 'robin'
| 'forest'
| 'amber'
| 'sienna'
| 'cherry'
| 'sakura'
| 'aqua'
| 'vanilla';
value?: { [key: string]: string };
}
@@ -23,37 +39,60 @@ function LabelColumn({
color = 'primary',
}: LabelColumnProps): JSX.Element {
const containerRef = useRef<HTMLDivElement>(null);
const [visibleCount, setVisibleCount] = useState(labels.length);
const [maxVisibleCount, setMaxVisibleCount] = useState(labels.length);
const [, copyToClipboard] = useCopyToClipboard();
useLayoutEffect(() => {
const calculateMaxVisible = useCallback(
(width: number): number => {
if (width <= 0) {
return 1;
}
const availableWidth = width - OVERFLOW_BADGE_WIDTH - BADGE_GAP;
let usedWidth = 0;
let count = 0;
for (const label of labels) {
const badgeWidth = estimateBadgeWidth(label, value?.[label]) + BADGE_GAP;
if (usedWidth + badgeWidth > availableWidth && count > 0) {
break;
}
usedWidth += badgeWidth;
count++;
}
return Math.max(1, count);
},
[labels, value],
);
useEffect(() => {
const container = containerRef.current;
if (!container) {
return;
}
const measure = (): void => {
const available = container.clientWidth;
if (available <= 0) {
return;
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry && entry.contentRect.width > 0) {
setMaxVisibleCount(calculateMaxVisible(entry.contentRect.width));
}
});
const widths = Array.from(container.children)
.slice(0, labels.length)
.map((item) => item.getBoundingClientRect().width);
const gap = parseFloat(getComputedStyle(container).columnGap) || 0;
setVisibleCount(getVisibleCount(widths, available, gap));
};
const observer = new ResizeObserver(measure);
observer.observe(container);
measure();
if (container.clientWidth > 0) {
setMaxVisibleCount(calculateMaxVisible(container.clientWidth));
}
return (): void => observer.disconnect();
}, [labels, value]);
}, [calculateMaxVisible]);
const remainingLabels = labels.slice(visibleCount);
const needsOverflow = labels.length > maxVisibleCount;
const visibleLabels = needsOverflow
? labels.slice(0, maxVisibleCount)
: labels;
const remainingLabels = needsOverflow ? labels.slice(maxVisibleCount) : [];
return (
<div
@@ -61,23 +100,24 @@ function LabelColumn({
className={styles.labelColumn}
data-testid="label-column"
>
{labels.map((label, index) => (
<LabelTag
key={label}
label={label}
color={color}
value={value?.[label]}
className={cx({
[styles.overflowed]: index >= visibleCount,
[styles.shrinkable]: index === 0 && visibleCount === 1,
})}
/>
{visibleLabels.map((label) => (
<LabelTag key={label} label={label} color={color} value={value?.[label]} />
))}
{remainingLabels.length > 0 && (
<Tooltip
side="bottom"
align="end"
title={
<TooltipRoot>
<TooltipTrigger asChild>
<span>
<Badge
color={color}
className={styles.overflowBadge}
variant="outline"
data-testid="label-overflow-badge"
>
+{remainingLabels.length}
</Badge>
</span>
</TooltipTrigger>
<TooltipContent side="bottom" align="end">
<div className={styles.tooltipContent}>
<span>
{remainingLabels
@@ -100,14 +140,8 @@ function LabelColumn({
<Copy size={12} />
</button>
</div>
}
>
<span>
<Badge color={color} variant="outlined" testId="label-overflow-badge">
+{remainingLabels.length}
</Badge>
</span>
</Tooltip>
</TooltipContent>
</TooltipRoot>
)}
</div>
);

View File

@@ -1,3 +1,11 @@
.labelBadge {
cursor: default;
font-size: 12px;
max-width: 180px;
text-overflow: ellipsis;
}
.labelValue {
text-overflow: ellipsis;
overflow: hidden;

View File

@@ -1,24 +1,35 @@
import { Copy } from '@signozhq/icons';
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
import { Badge } from '@signozhq/ui/badge';
import { toast } from '@signozhq/ui/sonner';
import { Tooltip } from '@signozhq/ui/tooltip';
import {
TooltipContent,
TooltipRoot,
TooltipTrigger,
} from '@signozhq/ui/tooltip';
import { useCopyToClipboard } from 'react-use';
import styles from './LabelTag.module.scss';
export interface LabelTagProps {
label: string;
color?: BadgeColorType;
color?:
| 'primary'
| 'secondary'
| 'success'
| 'error'
| 'warning'
| 'robin'
| 'forest'
| 'amber'
| 'sienna'
| 'cherry'
| 'sakura'
| 'aqua'
| 'vanilla';
value?: string;
className?: string;
}
function LabelTag({
label,
value,
color,
className,
}: LabelTagProps): JSX.Element {
function LabelTag({ label, value, color }: LabelTagProps): JSX.Element {
const [, copyToClipboard] = useCopyToClipboard();
const displayText = value ? `${label}: ${value}` : label;
const searchFormat = value ? `${label} ${value}` : label;
@@ -30,8 +41,20 @@ function LabelTag({
};
return (
<Tooltip
title={
<TooltipRoot>
<TooltipTrigger asChild>
<span>
<Badge
color={color}
className={styles.labelBadge}
variant="outline"
data-testid={`label-tag-${label}`}
>
<span className={styles.labelValue}>{displayText}</span>
</Badge>
</span>
</TooltipTrigger>
<TooltipContent>
<div className={styles.tooltipContent}>
<span>{displayText}</span>
<button
@@ -43,19 +66,8 @@ function LabelTag({
<Copy size={12} />
</button>
</div>
}
>
<span className={className}>
<Badge
textTransform="none"
color={color ?? 'secondary'}
variant="outlined"
testId={`label-tag-${label}`}
>
<span className={styles.labelValue}>{displayText}</span>
</Badge>
</span>
</Tooltip>
</TooltipContent>
</TooltipRoot>
);
}

View File

@@ -1,59 +0,0 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { expect, screen, userEvent, waitFor, within } from 'storybook/test';
import { withCanvas } from '@/storybook/decorators/withCanvas';
import LabelColumn from '../LabelColumn';
const meta = {
title: 'Components/Label Column',
component: LabelColumn,
tags: ['play'],
decorators: [withCanvas({ maxWidth: 180 })],
args: {
labels: ['severity', 'team', 'service', 'environment'],
value: {
severity: 'critical',
team: 'payments',
service: 'checkout',
environment: 'production',
},
},
} satisfies Meta<typeof LabelColumn>;
export default meta;
type Story = StoryObj<typeof meta>;
/** Labels that fit the column; the rest collapse into a `+N` chip. */
export const Default: Story = {};
/** Copying one label from its tooltip. */
export const LabelCopiedToast: Story = {
play: async ({ canvasElement }): Promise<void> => {
await userEvent.hover(
await within(canvasElement).findByTestId('label-tag-severity'),
);
await userEvent.click(
await screen.findByRole('button', { name: 'Copy to clipboard' }),
);
await waitFor(() =>
expect(screen.getByText(/copied! use in search/i)).toBeVisible(),
);
},
};
/** Copying the labels that overflow the column from the `+N` chip's tooltip. */
export const OverflowLabelsCopiedToast: Story = {
play: async ({ canvasElement }): Promise<void> => {
await userEvent.hover(
await within(canvasElement).findByTestId('label-overflow-badge'),
);
await userEvent.click(
await screen.findByRole('button', { name: 'Copy to clipboard' }),
);
await waitFor(() =>
expect(screen.getByText(/copied! use in search/i)).toBeVisible(),
);
},
};

View File

@@ -1,33 +1,14 @@
export const BADGE_GAP = 4;
export const OVERFLOW_BADGE_WIDTH = 40;
/**
* How many leading labels fit in `available` px, reserving room for the `+N` badge
* whenever some are left out. At least one label is always shown.
*/
export function getVisibleCount(
widths: number[],
available: number,
gap: number,
): number {
const total = widths.reduce(
(sum, width, index) => sum + width + (index > 0 ? gap : 0),
0,
export const BADGE_MAX_WIDTH = 180;
export const BADGE_PADDING = 16;
export const CHAR_WIDTH = 7;
export function estimateBadgeWidth(label: string, value?: string): number {
const displayText = value ? `${label}: ${value}` : label;
return Math.min(
displayText.length * CHAR_WIDTH + BADGE_PADDING,
BADGE_MAX_WIDTH,
);
if (total <= available) {
return widths.length;
}
const limit = available - OVERFLOW_BADGE_WIDTH - gap;
let used = 0;
let count = 0;
for (const width of widths) {
used += width + (count > 0 ? gap : 0);
if (used > limit) {
break;
}
count++;
}
return Math.max(1, count);
}

View File

@@ -30,23 +30,21 @@ function NoResultsEmptyState({
<div className={styles.actions}>
{onClear && (
<Button
size="md"
variant="outlined"
color="secondary"
onClick={onClear}
testId="no-results-clear-button"
data-testid="no-results-clear-button"
>
{clearButtonText}
</Button>
)}
{onRefresh && (
<Button
size="md"
variant="outlined"
color="secondary"
prefix={<RefreshCw size={14} />}
onClick={onRefresh}
testId="no-results-refresh-button"
data-testid="no-results-refresh-button"
>
Refresh
</Button>

View File

@@ -1,4 +1,4 @@
import type { BadgeColorType } from '@signozhq/ui/badge';
import type { BadgeColor } from '@signozhq/ui/badge';
export const STATE_ORDER = ['firing', 'pending', 'inactive', 'disabled'];
export const SEVERITY_ORDER = ['critical', 'error', 'warning', 'info'];
@@ -24,9 +24,9 @@ export const SEVERITY_COLORS: Record<string, string> = {
info: 'var(--bg-robin-500)',
};
export const SEVERITY_BADGE_COLORS: Record<string, BadgeColorType> = {
critical: 'danger',
error: 'danger',
export const SEVERITY_BADGE_COLORS: Record<string, BadgeColor> = {
critical: 'error',
error: 'error',
warning: 'warning',
info: 'primary',
};

View File

@@ -33,3 +33,36 @@
color: var(--l1-foreground);
white-space: nowrap;
}
.auth-header-help-button {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
height: 32px;
padding: 10px 16px;
background: var(--l2-background);
color: var(--l2-foreground);
border: none;
border-radius: 2px;
cursor: pointer;
transition: opacity 0.2s ease;
span {
font-family: var(--font-family-inter, Inter, sans-serif);
font-size: 11px;
font-weight: 500;
line-height: 1;
color: var(--l2-foreground);
text-align: center;
}
svg {
flex-shrink: 0;
color: var(--l2-foreground);
}
&:hover {
opacity: 0.8;
}
}

View File

@@ -22,11 +22,11 @@ function AuthHeader(): JSX.Element {
<span className="auth-header-logo-text">SigNoz</span>
</div>
<Button
size="md"
className="auth-header-help-button"
prefix={<LifeBuoy size={12} />}
onClick={handleGetHelp}
variant="solid"
color="secondary"
color="none"
>
Get Help
</Button>

View File

@@ -1,7 +1,7 @@
import React, { Dispatch, SetStateAction, useState } from 'react';
import { Check, Plus, X } from '@signozhq/icons';
import { Button, Flex } from 'antd';
import { Pill } from '@signozhq/ui/pill';
import { Badge } from '@signozhq/ui/badge';
import Input from 'components/Input';
import './Badges.styles.scss';
@@ -47,13 +47,18 @@ function Badges({ tags, setTags }: AddTagsProps): JSX.Element {
return (
<div className="tags-container">
{tags.map<React.ReactNode>((tag) => (
<Pill.Closeable
textTransform="none"
<Badge
key={tag}
onClose={() => handleClose(tag)}
color="vanilla"
style={{ userSelect: 'none' }}
closable
onClose={(e): void => {
e.preventDefault();
handleClose(tag);
}}
>
{tag}
</Pill.Closeable>
</Badge>
))}
{inputVisible && (

View File

@@ -39,3 +39,9 @@
}
}
}
.cloud-service-data-collected-table-tooltip {
max-width: 280px;
white-space: normal;
word-break: break-word;
}

View File

@@ -4,7 +4,7 @@ import {
CloudintegrationtypesCollectedMetricDTO,
} from 'api/generated/services/sigNoz.schemas';
import { BarChart, Info, ScrollText } from '@signozhq/icons';
import { Tooltip } from '@signozhq/ui/tooltip';
import { TooltipProvider, TooltipSimple } from '@signozhq/ui/tooltip';
import './CloudServiceDataCollected.styles.scss';
@@ -88,15 +88,23 @@ function CloudServiceDataCollected({
<BarChart size={14} />
Metrics
{metricsInfoTooltip && (
<Tooltip title={metricsInfoTooltip} side="top">
<span
className="cloud-service-data-collected-table-heading-info"
aria-label="About the metrics listed below"
data-testid="data-collected-metrics-info"
<TooltipProvider>
<TooltipSimple
title={metricsInfoTooltip}
side="top"
tooltipContentProps={{
className: 'cloud-service-data-collected-table-tooltip',
}}
>
<Info size={12} />
</span>
</Tooltip>
<span
className="cloud-service-data-collected-table-heading-info"
aria-label="About the metrics listed below"
data-testid="data-collected-metrics-info"
>
<Info size={12} />
</span>
</TooltipSimple>
</TooltipProvider>
)}
</div>
<Table

View File

@@ -2,13 +2,6 @@
position: relative;
}
.copyButton {
position: absolute;
right: 8px;
top: 8px;
z-index: 1;
}
.codeBlockSyntaxHighlighter {
background-color: var(--l2-background) !important;
border-radius: 4px !important;

View File

@@ -2,7 +2,6 @@ import { useMemo, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import { Check, Copy } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { Tooltip } from '@signozhq/ui/tooltip';
import SyntaxHighlighter, {
a11yDark,
} from 'components/MarkdownRenderer/syntaxHighlighter';
@@ -53,20 +52,16 @@ function CodeBlock({
data-testid="code-block-container"
>
{showCopyButton ? (
<div className={styles.copyButton}>
<Tooltip title={isCopied ? 'Copied' : 'Copy'}>
<Button
variant="ghost"
color="secondary"
size="sm"
icon
onClick={handleCopy}
aria-label="Copy code"
>
{isCopied ? <Check size={14} /> : <Copy size={14} />}
</Button>
</Tooltip>
</div>
<Button
variant="ghost"
color="secondary"
size="sm"
onClick={handleCopy}
prefix={isCopied ? <Check size={14} /> : <Copy size={14} />}
aria-label="Copy code"
title={isCopied ? 'Copied' : 'Copy'}
style={{ position: 'absolute', right: 8, top: 8, zIndex: 1 }}
/>
) : null}
<SyntaxHighlighter
style={a11yDark}

View File

@@ -134,27 +134,26 @@ function CreateServiceAccountModal(): JSX.Element {
<DialogFooter className="create-sa-modal__footer">
<Button
size="md"
type="button"
variant="solid"
color="secondary"
onClick={handleClose}
testId="create-sa-cancel-btn"
prefix={<X size={12} />}
data-testid="create-sa-cancel-btn"
>
<X size={12} />
Cancel
</Button>
<AuthZButton
size="md"
checks={[SACreatePermission]}
withPortal={false}
type="submit"
form="create-sa-form"
variant="solid"
color="primary"
loading={isSubmitting}
disabled={!isValid}
testId="create-sa-submit-btn"
data-testid="create-sa-submit-btn"
>
Create Service Account
</AuthZButton>

View File

@@ -5,7 +5,6 @@ import {
} from 'lib/authz/utils/authz-test-utils';
import { rest, server } from 'mocks-server/server';
import { NuqsTestingAdapter } from 'nuqs/adapters/testing';
import { submitImplicitly } from 'tests/submitImplicitly';
import { render, screen, userEvent, waitFor } from 'tests/test-utils';
import CreateServiceAccountModal from '../CreateServiceAccountModal';
@@ -61,10 +60,7 @@ describe('CreateServiceAccountModal', () => {
await screen.findByTestId('create-sa-name-input');
await waitFor(() =>
expect(screen.getByTestId('create-sa-submit-btn')).toHaveAttribute(
'aria-disabled',
'true',
),
expect(screen.getByTestId('create-sa-submit-btn')).toBeDisabled(),
);
});
@@ -76,14 +72,10 @@ describe('CreateServiceAccountModal', () => {
const submitBtn = await screen.findByTestId('create-sa-submit-btn');
await user.type(nameInput, 'test');
await waitFor(() =>
expect(submitBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(submitBtn).not.toBeDisabled());
await user.clear(nameInput);
await waitFor(() =>
expect(submitBtn).toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(submitBtn).toBeDisabled());
});
it('successful submit shows toast.success and closes modal', async () => {
@@ -94,9 +86,7 @@ describe('CreateServiceAccountModal', () => {
await user.type(nameInput, 'Deploy Bot');
const submitBtn = screen.getByTestId('create-sa-submit-btn');
await waitFor(() =>
expect(submitBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(submitBtn).not.toBeDisabled());
await user.click(submitBtn);
await waitFor(() => {
@@ -112,40 +102,6 @@ describe('CreateServiceAccountModal', () => {
});
});
it('pressing Enter again while creating sends one request', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
let requests = 0;
server.use(
rest.post(SERVICE_ACCOUNTS_ENDPOINT, (_, res, ctx) => {
requests += 1;
return res(
ctx.delay(100),
ctx.status(201),
ctx.json({ status: 'success', data: {} }),
);
}),
);
renderModal();
const nameInput = await screen.findByTestId<HTMLInputElement>(
'create-sa-name-input',
);
await user.type(nameInput, 'Deploy Bot');
const submitBtn = screen.getByTestId('create-sa-submit-btn');
await waitFor(() =>
expect(submitBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
submitImplicitly(nameInput);
await waitFor(() => expect(submitBtn).toHaveAttribute('aria-busy', 'true'));
submitImplicitly(nameInput);
await waitFor(() => {
expect(mockToast.success).toHaveBeenCalled();
});
expect(requests).toBe(1);
});
it('shows toast.error on API error and keeps modal open', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
@@ -164,9 +120,7 @@ describe('CreateServiceAccountModal', () => {
await user.type(nameInput, 'Dupe Bot');
const submitBtn = screen.getByTestId('create-sa-submit-btn');
await waitFor(() =>
expect(submitBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(submitBtn).not.toBeDisabled());
await user.click(submitBtn);
await waitFor(() => {
@@ -226,10 +180,7 @@ describe('CreateServiceAccountModal', () => {
).resolves.toBeInTheDocument();
// The footer lives outside the guard: submit is gated, Cancel still works.
expect(screen.getByTestId('create-sa-submit-btn')).toHaveAttribute(
'aria-disabled',
'true',
);
expect(screen.getByTestId('create-sa-submit-btn')).toBeDisabled();
await user.click(screen.getByTestId('create-sa-cancel-btn'));

View File

@@ -4,6 +4,30 @@
align-items: center;
gap: 4px;
.zoom-out-btn {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
color: var(--secondary-foreground);
background-color: var(--secondary-background);
border: 1px solid var(--secondary-border);
border-radius: 2px;
box-shadow: none;
padding: 10px;
height: 33px;
&:hover:not(:disabled) {
color: var(--primary-foreground);
background: var(--primary-background);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
.timeSelection-input {
&:hover {
border-color: var(--l1-border) !important;

View File

@@ -72,8 +72,6 @@ interface CustomTimePickerProps {
maxTime: number;
/** When true, zoom-out button is hidden (e.g. in drawer/modal time selection) */
isModalTimeSelection?: boolean;
/** Lands on the trigger input. Not spread — the component takes no rest props. */
'data-testid'?: string;
}
function CustomTimePicker({
@@ -97,7 +95,6 @@ function CustomTimePicker({
minTime,
maxTime,
isModalTimeSelection = false,
'data-testid': dataTestId,
}: CustomTimePickerProps): JSX.Element {
const [selectedTimePlaceholderValue, setSelectedTimePlaceholderValue] =
useState('Select / Enter Time Range');
@@ -599,7 +596,6 @@ function CustomTimePicker({
>
<Input
ref={inputRef}
data-testid={dataTestId}
autoComplete="off"
className={cx(
'timeSelection-input',
@@ -654,20 +650,20 @@ function CustomTimePicker({
</Popover>
</Tooltip>
{!showLiveLogs && !isModalTimeSelection && (
<Tooltip title={zoomOutDisabled ? undefined : 'Zoom out'}>
<Tooltip
title={
zoomOutDisabled ? 'Zoom out time range is limited to 1 month' : 'Zoom out'
}
>
<Button
disabledTooltip="Zoom out time range is limited to 1 month"
size="md"
className="zoom-out-btn"
onClick={handleZoomOut}
disabled={zoomOutDisabled}
testId="zoom-out-btn"
icon
aria-label="Zoom out"
data-testid="zoom-out-btn"
prefix={<ZoomOut size={14} />}
variant="solid"
color="secondary"
>
<ZoomOut size={14} />
</Button>
color="none"
/>
</Tooltip>
)}
</div>
@@ -686,5 +682,4 @@ CustomTimePicker.defaultProps = {
onExitLiveLogs: noop,
showLiveLogs: false,
showRecentlyUsed: true,
'data-testid': undefined,
};

View File

@@ -180,7 +180,6 @@ function CustomTimePickerPopoverContent({
type="text"
className="time-btns"
key={option.label + option.value}
data-testid={`time-chip-${option.value}`}
onClick={(): void => {
handleExitLiveLogs();
onSelectHandler(option.label, option.value);
@@ -260,7 +259,6 @@ function CustomTimePickerPopoverContent({
<Button
type="text"
key={option.label + option.value}
data-testid={`time-option-${option.value}`}
onClick={(e: React.MouseEvent<HTMLButtonElement>): void => {
e.stopPropagation();
e.preventDefault();

View File

@@ -164,6 +164,6 @@ describe('CustomTimePicker - zoom out button', () => {
);
const zoomOutBtn = screen.getByTestId('zoom-out-btn');
expect(zoomOutBtn).toHaveAttribute('aria-disabled', 'true');
expect(zoomOutBtn).toBeDisabled();
});
});

View File

@@ -27,14 +27,12 @@ function DetailsHeader({
const closeButton = (
<Button
variant="ghost"
size="sm"
icon
size="icon"
color="secondary"
onClick={onClose}
aria-label="Close"
>
<X size={14} />
</Button>
prefix={<X size={14} />}
></Button>
);
return (

View File

@@ -42,5 +42,12 @@
height: 1px;
background: var(--l1-border);
}
.export-button {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
}
}

View File

@@ -308,16 +308,13 @@ describe.each([
fireEvent.click(screen.getByText('Export'));
expect(screen.getByTestId(testId)).toHaveAttribute('aria-disabled', 'true');
expect(screen.getByTestId(testId)).toBeDisabled();
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
resolveDownload!();
await waitFor(() => {
expect(screen.getByTestId(testId)).not.toHaveAttribute(
'aria-disabled',
'true',
);
expect(screen.getByTestId(testId)).not.toBeDisabled();
});
});
});

View File

@@ -1,7 +1,7 @@
import { useCallback, useMemo, useState } from 'react';
import { Popover, Tooltip } from 'antd';
import { Button } from '@signozhq/ui/button';
import { RadioGroup } from '@signozhq/ui/radio-group';
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
import { Typography } from '@signozhq/ui/typography';
import { TelemetryFieldKey } from 'api/v5/v5';
import { PANEL_TYPES } from 'constants/queryBuilder';
@@ -69,15 +69,10 @@ export default function DownloadOptionsMenu({
>
<div className="export-format">
<Typography.Text className="title">FORMAT</Typography.Text>
<RadioGroup
color="primary"
value={exportFormat}
onChange={setExportFormat}
items={[
{ value: DownloadFormats.CSV, label: 'csv' },
{ value: DownloadFormats.JSONL, label: 'jsonl' },
]}
/>
<RadioGroup value={exportFormat} onChange={setExportFormat}>
<RadioGroupItem value={DownloadFormats.CSV}>csv</RadioGroupItem>
<RadioGroupItem value={DownloadFormats.JSONL}>jsonl</RadioGroupItem>
</RadioGroup>
</div>
<div className="horizontal-line" />
@@ -85,15 +80,19 @@ export default function DownloadOptionsMenu({
<div className="row-limit">
<Typography.Text className="title">Number of Rows</Typography.Text>
<RadioGroup
color="primary"
value={String(rowLimit)}
onChange={(value): void => setRowLimit(Number(value))}
items={[
{ value: String(DownloadRowCounts.TEN_K), label: '10k' },
{ value: String(DownloadRowCounts.THIRTY_K), label: '30k' },
{ value: String(DownloadRowCounts.FIFTY_K), label: '50k' },
]}
/>
>
<RadioGroupItem value={String(DownloadRowCounts.TEN_K)}>
10k
</RadioGroupItem>
<RadioGroupItem value={String(DownloadRowCounts.THIRTY_K)}>
30k
</RadioGroupItem>
<RadioGroupItem value={String(DownloadRowCounts.FIFTY_K)}>
50k
</RadioGroupItem>
</RadioGroup>
</div>
{dataSource !== DataSource.TRACES && (
@@ -102,15 +101,12 @@ export default function DownloadOptionsMenu({
<div className="columns-scope">
<Typography.Text className="title">Columns</Typography.Text>
<RadioGroup
color="primary"
value={columnsScope}
onChange={setColumnsScope}
items={[
{ value: DownloadColumnsScopes.ALL, label: 'All' },
{ value: DownloadColumnsScopes.SELECTED, label: 'Selected' },
]}
/>
<RadioGroup value={columnsScope} onChange={setColumnsScope}>
<RadioGroupItem value={DownloadColumnsScopes.ALL}>All</RadioGroupItem>
<RadioGroupItem value={DownloadColumnsScopes.SELECTED}>
Selected
</RadioGroupItem>
</RadioGroup>
</div>
</>
)}
@@ -119,9 +115,9 @@ export default function DownloadOptionsMenu({
variant="solid"
color="primary"
prefix={<Download size={16} />}
size="md"
width="100%"
onClick={handleExport}
className="export-button"
disabled={isDownloading}
loading={isDownloading}
>
Export
@@ -152,14 +148,13 @@ export default function DownloadOptionsMenu({
<Button
variant="ghost"
color="secondary"
size="sm"
icon
size="icon"
prefix={<Download size={14} />}
aria-label="Download"
testId={`periscope-btn-download-${dataSource}`}
data-testid={`periscope-btn-download-${dataSource}`}
disabled={isDownloading}
loading={isDownloading}
>
<Download size={14} />
</Button>
/>
</Tooltip>
</Popover>
);

View File

@@ -38,23 +38,18 @@ function DeleteMemberDialog({
const footer = (
<>
<Button
size="md"
variant="solid"
color="secondary"
onClick={onClose}
prefix={<X size={12} />}
>
<Button variant="solid" color="secondary" onClick={onClose}>
<X size={12} />
Cancel
</Button>
<Button
size="md"
variant="solid"
color="danger"
color="destructive"
disabled={isDeleting}
onClick={onConfirm}
loading={isDeleting}
prefix={<Trash2 size={12} />}
>
<Trash2 size={12} />
{isDeleting ? 'Processing...' : title}
</Button>
</>

View File

@@ -90,6 +90,7 @@
font-weight: var(--uppercase-small-500-font-weight);
line-height: 100%;
letter-spacing: 0.44px;
text-transform: uppercase;
}
}
@@ -112,7 +113,6 @@
&__footer-left {
display: flex;
align-items: center;
gap: var(--spacing-4);
}
&__footer-right {
@@ -128,6 +128,10 @@
flex-shrink: 0;
}
&__tooltip-wrapper {
display: inline-flex;
}
&__footer-btn {
display: inline-flex;
align-items: center;
@@ -196,7 +200,6 @@
background: var(--l2-background);
border: 1px solid var(--l1-border);
border-radius: 2px;
padding-right: var(--spacing-4);
}
&__link-text-wrap {
@@ -217,4 +220,8 @@
line-height: var(--line-height-18);
letter-spacing: -0.07px;
}
&__copy-btn {
border-left: 1px solid var(--l1-border);
}
}

View File

@@ -519,12 +519,7 @@ function EditMemberDrawer({
localRoles.map((roleId) => {
const role = availableRoles.find((r) => r.id === roleId);
return (
<Badge
textTransform="none"
variant="solid"
key={roleId}
color="secondary"
>
<Badge key={roleId} color="vanilla">
{role?.name ?? roleId}
</Badge>
);
@@ -564,15 +559,15 @@ function EditMemberDrawer({
<div className="edit-member-drawer__meta-item">
<span className="edit-member-drawer__meta-label">Status</span>
{member?.status === MemberStatus.Active ? (
<Badge color="success" variant="outlined">
<Badge color="forest" variant="outline">
ACTIVE
</Badge>
) : member?.status === MemberStatus.Deleted ? (
<Badge color="danger" variant="outlined">
<Badge color="cherry" variant="outline">
DELETED
</Badge>
) : (
<Badge color="warning" variant="outlined">
<Badge color="amber" variant="outline">
INVITED
</Badge>
)}
@@ -580,16 +575,12 @@ function EditMemberDrawer({
<div className="edit-member-drawer__meta-item">
<span className="edit-member-drawer__meta-label">{joinedOnLabel}</span>
<Badge textTransform="none" variant="solid" color="secondary">
{formatTimestamp(member?.joinedOn)}
</Badge>
<Badge color="vanilla">{formatTimestamp(member?.joinedOn)}</Badge>
</div>
{!isInvited && (
<div className="edit-member-drawer__meta-item">
<span className="edit-member-drawer__meta-label">Last Modified</span>
<Badge textTransform="none" variant="solid" color="secondary">
{formatTimestamp(member?.updatedAt)}
</Badge>
<Badge color="vanilla">{formatTimestamp(member?.updatedAt)}</Badge>
</div>
)}
</div>
@@ -620,59 +611,55 @@ function EditMemberDrawer({
{!isDeleted && (
<>
<div className="edit-member-drawer__footer-left">
<Button
disabledTooltip={getDeleteTooltip(isRootUser, isSelf)}
size="md"
onClick={(): void => setShowDeleteConfirm(true)}
disabled={isRootUser || isSelf}
variant="link"
color="danger"
prefix={<Trash2 size={12} />}
>
{isInvited ? 'Revoke Invite' : 'Delete Member'}
</Button>
<Tooltip title={getDeleteTooltip(isRootUser, isSelf)}>
<span className="edit-member-drawer__tooltip-wrapper">
<Button
onClick={(): void => setShowDeleteConfirm(true)}
disabled={isRootUser || isSelf}
variant="link"
color="destructive"
>
<Trash2 size={12} />
{isInvited ? 'Revoke Invite' : 'Delete Member'}
</Button>
</span>
</Tooltip>
<div className="edit-member-drawer__footer-divider" />
<Button
disabledTooltip={ROOT_USER_TOOLTIP}
size="md"
onClick={handleGenerateResetLink}
disabled={isRootUser}
loading={isGeneratingLink || isLoadingTokenStatus}
variant="link"
color="warning"
prefix={<RefreshCw size={12} />}
>
{isGeneratingLink
? 'Generating...'
: isInvited
? getInviteButtonLabel(
isLoadingTokenStatus,
existingToken,
isTokenExpired,
tokenNotFound,
)
: 'Generate Password Reset Link'}
</Button>
<Tooltip title={isRootUser ? ROOT_USER_TOOLTIP : undefined}>
<span className="edit-member-drawer__tooltip-wrapper">
<Button
onClick={handleGenerateResetLink}
disabled={isGeneratingLink || isRootUser || isLoadingTokenStatus}
variant="link"
color="warning"
>
<RefreshCw size={12} />
{isGeneratingLink
? 'Generating...'
: isInvited
? getInviteButtonLabel(
isLoadingTokenStatus,
existingToken,
isTokenExpired,
tokenNotFound,
)
: 'Generate Password Reset Link'}
</Button>
</span>
</Tooltip>
</div>
<div className="edit-member-drawer__footer-right">
<Button
size="md"
variant="outlined"
color="secondary"
onClick={handleClose}
prefix={<X size={14} />}
>
<Button variant="outlined" color="secondary" onClick={handleClose}>
<X size={14} />
Cancel
</Button>
<Button
disabledTooltip={isRootUser ? ROOT_USER_TOOLTIP : 'No changes to save'}
size="md"
variant="solid"
color="primary"
disabled={!isDirty || isRootUser}
disabled={!isDirty || isSaving || isRootUser}
onClick={handleSave}
loading={isSaving}
>

View File

@@ -45,11 +45,11 @@ function ResetLinkDialog({
<span className="reset-link-dialog__link-text">{resetLink}</span>
</div>
<Button
size="md"
variant="link"
color="secondary"
onClick={onCopy}
prefix={hasCopied ? <Check size={12} /> : <Copy size={12} />}
className="reset-link-dialog__copy-btn"
>
{hasCopied ? 'Copied!' : 'Copy'}
</Button>

View File

@@ -251,7 +251,7 @@ describe('EditMemberDrawer', () => {
expect(screen.getByText('ACTIVE')).toBeInTheDocument();
expect(
screen.getByRole('button', { name: /save member details/i }),
).toHaveAttribute('aria-disabled', 'true');
).toBeDisabled();
});
it('enables Save after editing name and calls updateUser on confirm', async () => {
@@ -271,9 +271,7 @@ describe('EditMemberDrawer', () => {
await user.type(nameInput, 'Alice Updated');
const saveBtn = screen.getByRole('button', { name: /save member details/i });
await waitFor(() =>
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(saveBtn).not.toBeDisabled());
await user.click(saveBtn);
@@ -297,9 +295,7 @@ describe('EditMemberDrawer', () => {
await user.type(nameInput, 'Alice Updated');
const saveBtn = screen.getByRole('button', { name: /save member details/i });
await waitFor(() =>
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(saveBtn).not.toBeDisabled());
await user.click(saveBtn);
await waitFor(() => {
@@ -327,9 +323,7 @@ describe('EditMemberDrawer', () => {
await user.click(await screen.findByTitle('signoz-editor'));
const saveBtn = screen.getByRole('button', { name: /save member details/i });
await waitFor(() =>
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(saveBtn).not.toBeDisabled());
await user.click(saveBtn);
await waitFor(() => {
@@ -355,9 +349,7 @@ describe('EditMemberDrawer', () => {
await user.click(removeBtn);
const saveBtn = screen.getByRole('button', { name: /save member details/i });
await waitFor(() =>
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(saveBtn).not.toBeDisabled());
await user.click(saveBtn);
await waitFor(() => {
@@ -513,9 +505,7 @@ describe('EditMemberDrawer', () => {
await user.type(nameInput, 'Bob Updated');
const saveBtn = screen.getByRole('button', { name: /save member details/i });
await waitFor(() =>
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(saveBtn).not.toBeDisabled());
await user.click(saveBtn);
await waitFor(() => {
@@ -551,9 +541,7 @@ describe('EditMemberDrawer', () => {
await user.type(nameInput, 'Alice Updated');
const saveBtn = screen.getByRole('button', { name: /save member details/i });
await waitFor(() =>
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
);
await waitFor(() => expect(saveBtn).not.toBeDisabled());
await user.click(saveBtn);
await waitFor(() => {
@@ -631,7 +619,7 @@ describe('EditMemberDrawer', () => {
renderDrawer({ member: selfMember });
expect(
screen.getByRole('button', { name: /delete member/i }),
).toHaveAttribute('aria-disabled', 'true');
).toBeDisabled();
});
it('does not open delete confirm dialog when Delete is clicked while disabled (isSelf)', async () => {
@@ -654,7 +642,7 @@ describe('EditMemberDrawer', () => {
renderDrawer({ member: selfMember });
expect(
screen.getByRole('button', { name: /generate password reset link/i }),
).not.toHaveAttribute('aria-disabled', 'true');
).not.toBeDisabled();
});
});
@@ -676,21 +664,21 @@ describe('EditMemberDrawer', () => {
renderDrawer();
expect(
screen.getByRole('button', { name: /delete member/i }),
).toHaveAttribute('aria-disabled', 'true');
).toBeDisabled();
});
it('disables Reset Link button for root user', () => {
renderDrawer();
expect(
screen.getByRole('button', { name: /generate password reset link/i }),
).toHaveAttribute('aria-disabled', 'true');
).toBeDisabled();
});
it('disables Save button for root user', () => {
renderDrawer();
expect(
screen.getByRole('button', { name: /save member details/i }),
).toHaveAttribute('aria-disabled', 'true');
).toBeDisabled();
});
it('does not open delete confirm dialog when Delete is clicked while disabled (root)', async () => {

View File

@@ -53,7 +53,7 @@ function ErrorModal({
onClick={(): void => setVisible(true)}
onKeyDown={undefined}
>
<Badge variant="solid" color="danger">
<Badge color="error">
<CircleAlert size={14} color={Color.BG_CHERRY_500} /> error
</Badge>
</span>

View File

@@ -1,7 +1,7 @@
import { useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import { Button, Col, Popover, Row, Select, Space } from 'antd';
import { Dropdown, type DropdownItemType } from '@signozhq/ui/dropdown';
import { DropdownMenuSimple, type MenuProps } from '@signozhq/ui/dropdown-menu';
import { Typography } from '@signozhq/ui/typography';
import axios from 'axios';
import TextToolTip from 'components/TextToolTip';
@@ -137,15 +137,16 @@ function ExplorerCard({
);
};
const moreOptionItems: DropdownItemType[] = [
{
type: 'item',
value: 'delete',
label: <Typography.Text strong>Delete</Typography.Text>,
onClick: onDeleteHandler,
prefix: <Trash2 size="md" />,
},
];
const moreOptionMenu: MenuProps = {
items: [
{
key: 'delete',
label: <Typography.Text strong>Delete</Typography.Text>,
onClick: onDeleteHandler,
icon: <Trash2 size="md" />,
},
],
};
const saveButtonType = isQueryUpdated ? 'default' : 'primary';
const saveButtonIcon = isQueryUpdated ? null : <Save size="md" />;
@@ -229,14 +230,9 @@ function ExplorerCard({
</Popover>
<Share2 onClick={onCopyUrlHandler} size="md" />
{viewKey && (
<Dropdown
items={moreOptionItems}
nativeButton
align="end"
side="bottom"
>
<DropdownMenuSimple menu={moreOptionMenu}>
<Button type="text" size="small" icon={<Ellipsis size="md" />} />
</Dropdown>
</DropdownMenuSimple>
)}
</Space>
</OffSetCol>

View File

@@ -26,4 +26,8 @@
font-size: var(--periscope-font-size-base);
}
}
.export-button {
width: 100%;
}
}

View File

@@ -1,8 +1,8 @@
import { Download } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { Popover, PopoverContent, PopoverTrigger } from '@signozhq/ui/popover';
import { RadioGroup } from '@signozhq/ui/radio-group';
import { Tooltip } from '@signozhq/ui/tooltip';
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Typography } from '@signozhq/ui/typography';
import {
ClientExportData,
@@ -51,41 +51,36 @@ export default function ExportMenu({
return (
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
<Tooltip title="Download">
<TooltipSimple title="Download">
<PopoverTrigger asChild>
<Button
variant="ghost"
color="secondary"
size="sm"
icon
size="icon"
aria-label="Download"
testId={`export-menu-${dataSource}`}
data-testid={`export-menu-${dataSource}`}
disabled={isExporting}
loading={isExporting}
>
<Download size={14} />
</Button>
</PopoverTrigger>
</Tooltip>
</TooltipSimple>
<PopoverContent align="end" className="export-menu-popover">
<div className="export-format">
<Typography.Text className="title">FORMAT</Typography.Text>
<RadioGroup
color="primary"
value={exportFormat}
onChange={setExportFormat}
items={[
{ value: ExportFormat.Csv, label: 'csv' },
{ value: ExportFormat.Jsonl, label: 'jsonl' },
]}
/>
<RadioGroup value={exportFormat} onChange={setExportFormat}>
<RadioGroupItem value={ExportFormat.Csv}>csv</RadioGroupItem>
<RadioGroupItem value={ExportFormat.Jsonl}>jsonl</RadioGroupItem>
</RadioGroup>
</div>
<Button
size="md"
variant="solid"
color="primary"
width="100%"
className="export-button"
onClick={handleExport}
disabled={isExporting}
loading={isExporting}
prefix={<Download size={16} />}
>

View File

@@ -80,6 +80,6 @@ describe('ExportMenu', () => {
mockIsExporting = true;
renderMenu();
expect(screen.getByTestId(TEST_ID)).toHaveAttribute('aria-disabled', 'true');
expect(screen.getByTestId(TEST_ID)).toBeDisabled();
});
});

View File

@@ -57,8 +57,9 @@ function SortableField({
</div>
{!isRequired && (
<Button
variant="solid"
color="danger"
className={cx(styles.removeBtn, 'periscope-btn')}
variant="outlined"
color="destructive"
size="sm"
onClick={(): void => onRemove(field)}
>

View File

@@ -98,15 +98,11 @@
user-select: none;
font-size: 13px;
> [data-slot='button'] {
opacity: 0;
transition: opacity 0.15s ease-in-out;
}
&:hover {
background-color: var(--l2-background);
> [data-slot='button'] {
.removeBtn,
.addBtn {
opacity: 1;
}
}
@@ -141,6 +137,14 @@
height: 32px;
}
.removeBtn,
.addBtn {
padding: 4px 10px;
opacity: 0;
transition: opacity 0.15s ease-in-out;
flex-shrink: 0;
}
.footer {
display: flex;
gap: 12px;

View File

@@ -173,7 +173,6 @@ function FieldsSelectorContent({
{hasUnsavedChanges && (
<div className={styles.footer}>
<Button
size="md"
variant="outlined"
color="secondary"
onClick={handleDiscard}
@@ -182,7 +181,6 @@ function FieldsSelectorContent({
Discard
</Button>
<Button
size="md"
variant="solid"
color="primary"
onClick={handleSave}

View File

@@ -138,6 +138,7 @@ function OtherFields({
<span className={styles.fieldKey}>{attr.name}</span>
{!isAtLimit && (
<Button
className={cx(styles.addBtn, 'periscope-btn')}
variant="outlined"
color="secondary"
size="sm"

View File

@@ -1,5 +1,5 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { expect, screen, userEvent, waitFor } from 'storybook/test';
import { screen, userEvent } from 'storybook/test';
import { DataSource } from 'types/common/queryBuilder';
import FieldsSelector from '../FieldsSelector';
@@ -56,21 +56,6 @@ export const UnsavedChanges: Story = {
},
};
/** Saving: the toast raised once the edited columns are applied. */
export const SavedToast: Story = {
play: async (): Promise<void> => {
const [addField] = await screen.findAllByRole('button', { name: 'Add' });
await userEvent.click(addField);
await userEvent.click(
await screen.findByRole('button', { name: 'Save changes' }),
);
await waitFor(() =>
expect(screen.getByText('Saved successfully')).toBeVisible(),
);
},
};
/** Empty: the suggestion request succeeds with no columns to add. */
export const NoResults: Story = {
parameters: {

View File

@@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { toast } from '@signozhq/ui/sonner';
import { Button, Input } from 'antd';
import { ToggleGroup } from '@signozhq/ui/toggle-group';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import { handleContactSupport } from 'container/Integrations/utils';
@@ -102,12 +102,10 @@ function FeedbackModal({ onClose }: { onClose: () => void }): JSX.Element {
return (
<div className="feedback-modal-container">
<div className="feedback-modal-header">
<ToggleGroup
variant="outlined"
color="secondary"
size="sm"
<ToggleGroupSimple
type="single"
value={activeTab}
className="feedback-modal-tabs"
onChange={setActiveTab}
items={items}
/>

View File

@@ -1,8 +1,7 @@
.header-right-section-container {
display: flex;
align-items: center;
gap: var(--spacing-8);
padding-right: var(--spacing-8);
gap: 8px;
}
.header-ai-assistant-btn-container {
@@ -122,26 +121,78 @@
}
.feedback-modal-container {
.feedback-modal-tab-label {
.feedback-modal-tabs {
width: 100%;
display: flex;
align-items: center;
gap: 8px;
.tab-icon {
width: 6px;
height: 6px;
.ant-radio-button-wrapper {
flex: 1;
margin: 0px !important;
border: 1px solid var(--l1-border);
&:before {
display: none;
}
.ant-radio-button-checked {
background-color: var(--l3-background);
}
}
.feedback-tab {
background-color: var(--danger-background);
.feedback-modal-tab-label {
display: flex;
align-items: center;
gap: 8px;
.tab-icon {
width: 6px;
height: 6px;
}
.feedback-tab {
background-color: var(--danger-background);
}
.bug-tab {
background-color: var(--warning-background);
}
.feature-tab {
background-color: var(--primary-background);
}
}
.bug-tab {
background-color: var(--warning-background);
}
.ant-tabs-nav-list {
.ant-tabs-tab {
padding: 6px 16px;
.feature-tab {
background-color: var(--primary-background);
border-radius: 2px;
background: var(--l2-background);
box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.1);
border: 1px solid var(--l1-border);
margin: 0 !important;
.ant-tabs-tab-btn {
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 166.667% */
letter-spacing: -0.06px;
}
&-active {
background: var(--l3-background);
color: var(--l1-foreground);
border-bottom: none !important;
.ant-tabs-tab-btn {
color: var(--l1-foreground);
}
}
}
}
}

View File

@@ -2,7 +2,7 @@ import { useCallback, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { Dot } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { Tooltip } from '@signozhq/ui/tooltip';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import Noz from 'components/Noz/Noz';
import { NOZ_TOOLTIP_TITLE } from 'components/Noz/Noz.constants';
import { Popover } from 'antd';
@@ -113,26 +113,24 @@ function HeaderRightSection({
</span>
) : null}
<span className="noz-wave">
<Tooltip title={NOZ_TOOLTIP_TITLE}>
<Button
size="md"
variant="ghost"
color="secondary"
onClick={handleOpenAIAssistant}
aria-label={
showHeaderPendingBadge
? pendingUserInputCount === 1
? 'Open Noz, 1 action needs your response'
: `Open Noz, ${pendingUserInputCount} actions need your response`
: 'Open Noz'
}
prefix={<Noz size={20} />}
>
<Typography.Text>Noz</Typography.Text>
</Button>
</Tooltip>
</span>
<TooltipSimple title={NOZ_TOOLTIP_TITLE}>
<Button
variant="solid"
color="secondary"
className="noz-wave"
onClick={handleOpenAIAssistant}
aria-label={
showHeaderPendingBadge
? pendingUserInputCount === 1
? 'Open Noz, 1 action needs your response'
: `Open Noz, ${pendingUserInputCount} actions need your response`
: 'Open Noz'
}
prefix={<Noz size={20} />}
>
<Typography.Text>Noz</Typography.Text>
</Button>
</TooltipSimple>
</div>
)}
@@ -149,15 +147,13 @@ function HeaderRightSection({
onOpenChange={handleOpenFeedbackModalChange}
>
<Button
color="primary"
variant="link"
size="md"
icon
variant="ghost"
size="icon"
className="share-feedback-btn"
aria-label="Feedback"
prefix={<SquarePen size={14} />}
onClick={handleOpenFeedbackModal}
>
<SquarePen size={14} />
</Button>
/>
</Popover>
)}
@@ -174,19 +170,16 @@ function HeaderRightSection({
onOpenChange={handleOpenAnnouncementsModalChange}
>
<Button
color="primary"
variant="link"
size="md"
icon
variant="ghost"
size="icon"
aria-label="Announcements"
prefix={<Inbox size={14} />}
onClick={(): void => {
logEvent('Announcements: Clicked', {
page: location.pathname,
});
}}
>
<Inbox size={14} />
</Button>
/>
</Popover>
)}
@@ -203,15 +196,12 @@ function HeaderRightSection({
onOpenChange={handleOpenShareURLModalChange}
>
<Button
color="primary"
variant="link"
size="md"
icon
variant="ghost"
size="icon"
aria-label="Share"
prefix={<Globe size={14} />}
onClick={handleOpenShareURLModal}
>
<Globe size={14} />
</Button>
/>
</Popover>
)}
</div>

View File

@@ -149,9 +149,6 @@ function ShareURLModal({ extraOption }: ShareURLModalProps): JSX.Element {
<Info size={14} color={Color.BG_AMBER_600} />
)}
<Switch
color="primary"
textPlacement="right"
disabledTooltip="Please select / enter valid relative time to toggle."
value={enableAbsoluteTime}
disabled={!isValidateRelativeTime}
onChange={(): void => {
@@ -176,8 +173,6 @@ function ShareURLModal({ extraOption }: ShareURLModalProps): JSX.Element {
</Typography.Text>
<div className="absolute-relative-time-toggler">
<Switch
color="primary"
textPlacement="right"
value={enableExtraOption}
onChange={(): void => setEnableExtraOption((prev) => !prev)}
/>

View File

@@ -69,23 +69,23 @@ describe('FeedbackModal', () => {
const user = userEvent.setup();
render(<FeedbackModal onClose={mockOnClose} />);
// Initially, feedback button should be active
const feedbackButton = screen.getByRole('button', { name: 'Feedback' });
expect(feedbackButton).toHaveAttribute('aria-pressed', 'true');
// Initially, feedback radio should be active
const feedbackRadio = screen.getByRole('radio', { name: 'Feedback' });
expect(feedbackRadio).toBeChecked();
const bugTab = screen.getByText('Report a bug');
await user.click(bugTab);
// Bug button should now be active
const bugButton = screen.getByRole('button', { name: 'Report a bug' });
expect(bugButton).toHaveAttribute('aria-pressed', 'true');
// Bug radio should now be active
const bugRadio = screen.getByRole('radio', { name: 'Report a bug' });
expect(bugRadio).toBeChecked();
const featureTab = screen.getByText('Feature request');
await user.click(featureTab);
// Feature button should now be active
const featureButton = screen.getByRole('button', { name: 'Feature request' });
expect(featureButton).toHaveAttribute('aria-pressed', 'true');
// Feature radio should now be active
const featureRadio = screen.getByRole('radio', { name: 'Feature request' });
expect(featureRadio).toBeChecked();
});
it('should update feedback text when typing in textarea', async () => {
@@ -133,9 +133,9 @@ describe('FeedbackModal', () => {
const bugTab = screen.getByText('Report a bug');
await user.click(bugTab);
// Verify bug report button is now active
const bugButton = screen.getByRole('button', { name: 'Report a bug' });
expect(bugButton).toHaveAttribute('aria-pressed', 'true');
// Verify bug report radio is now active
const bugRadio = screen.getByRole('radio', { name: 'Report a bug' });
expect(bugRadio).toBeChecked();
const textarea = screen.getByPlaceholderText('Write your feedback here...');
const submitButton = screen.getByRole('button', { name: /submit/i });
@@ -166,9 +166,9 @@ describe('FeedbackModal', () => {
const featureTab = screen.getByText('Feature request');
await user.click(featureTab);
// Verify feature request button is now active
const featureButton = screen.getByRole('button', { name: 'Feature request' });
expect(featureButton).toHaveAttribute('aria-pressed', 'true');
// Verify feature request radio is now active
const featureRadio = screen.getByRole('radio', { name: 'Feature request' });
expect(featureRadio).toBeChecked();
const textarea = screen.getByPlaceholderText('Write your feedback here...');
const submitButton = screen.getByRole('button', { name: /submit/i });
@@ -262,8 +262,8 @@ describe('FeedbackModal', () => {
);
expect(newTextArea).toHaveValue(''); // Should be empty
// Verify active button is reset to default (Feedback button)
const feedbackButton = screen.getByRole('button', { name: 'Feedback' });
expect(feedbackButton).toHaveAttribute('aria-pressed', 'true');
// Verify active radio is reset to default (Feedback radio)
const feedbackRadio = screen.getByRole('radio', { name: 'Feedback' });
expect(feedbackRadio).toBeChecked();
});
});

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