Compare commits
123 Commits
test/explo
...
feat/alert
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a99dea282 | ||
|
|
aef60bad5d | ||
|
|
f540a72e03 | ||
|
|
c3625cfa17 | ||
|
|
2377130302 | ||
|
|
7d77577d75 | ||
|
|
f1bf33630f | ||
|
|
c1cc58ac50 | ||
|
|
c2dd94f190 | ||
|
|
60bc6ac96f | ||
|
|
8c4db94ef7 | ||
|
|
1dbc9ed47c | ||
|
|
65ad9b5f38 | ||
|
|
de1912de43 | ||
|
|
81ff25b2f8 | ||
|
|
923d758f7c | ||
|
|
eeb57ceff2 | ||
|
|
d28cd7cf2e | ||
|
|
268c67d34b | ||
|
|
206349b75c | ||
|
|
001b7bd755 | ||
|
|
25ad34a133 | ||
|
|
11ae922ff7 | ||
|
|
3e087fe7bd | ||
|
|
9ec39f2494 | ||
|
|
ef67a47cf3 | ||
|
|
441f7e2927 | ||
|
|
4aff26f696 | ||
|
|
47a773245b | ||
|
|
375b3f8305 | ||
|
|
0565363083 | ||
|
|
c2f6ffb423 | ||
|
|
b6208538bf | ||
|
|
db708385ad | ||
|
|
83b1b90f29 | ||
|
|
aaec7c89c5 | ||
|
|
57c193ec8f | ||
|
|
47cf0847b1 | ||
|
|
da556abac1 | ||
|
|
cc8440248b | ||
|
|
e84ddfb61e | ||
|
|
dc9fda2916 | ||
|
|
670677bae2 | ||
|
|
e4d66c5413 | ||
|
|
1371cb0beb | ||
|
|
685a02e72d | ||
|
|
140498cb11 | ||
|
|
ba01bc0418 | ||
|
|
b5fcd935a7 | ||
|
|
fa105e640c | ||
|
|
014e1fc86a | ||
|
|
6d3fbb9903 | ||
|
|
758443f42a | ||
|
|
847eb52883 | ||
|
|
17574db3dc | ||
|
|
34482d8cc1 | ||
|
|
a541d2687c | ||
|
|
6ab70762a7 | ||
|
|
8989e0a3a1 | ||
|
|
0d150554c7 | ||
|
|
bee75c8e34 | ||
|
|
eb54542ca1 | ||
|
|
c92fe4969a | ||
|
|
97a2fefe61 | ||
|
|
c059d3b8f6 | ||
|
|
37d342eb90 | ||
|
|
d60da7b8a7 | ||
|
|
8d5111bc7d | ||
|
|
e2578c15fb | ||
|
|
6c5016bdd9 | ||
|
|
3b13ae9681 | ||
|
|
d55e4a7243 | ||
|
|
6ff779d5ab | ||
|
|
a446a832ae | ||
|
|
c89c19ce9e | ||
|
|
e543b3ef32 | ||
|
|
3f7531f444 | ||
|
|
abfa9bee1e | ||
|
|
2f497108df | ||
|
|
1fa1e292e9 | ||
|
|
a328988a15 | ||
|
|
7e53bd607d | ||
|
|
4d82eb8436 | ||
|
|
95c11d0550 | ||
|
|
b2810ec117 | ||
|
|
7556dd2868 | ||
|
|
339a10217e | ||
|
|
cd0eb62734 | ||
|
|
a667007991 | ||
|
|
208fc1e8ec | ||
|
|
54eb67392a | ||
|
|
0e0d97c16f | ||
|
|
659f9c7ecf | ||
|
|
ce8442c17a | ||
|
|
7ab21678b5 | ||
|
|
951bfb66cd | ||
|
|
113685fbdb | ||
|
|
b9c306dd26 | ||
|
|
7e71d4507c | ||
|
|
e13cb08097 | ||
|
|
1e7aaa9f14 | ||
|
|
09ab9f4081 | ||
|
|
f945b5f513 | ||
|
|
8b0ab0ff26 | ||
|
|
f2679a6866 | ||
|
|
253a117849 | ||
|
|
c9538be38f | ||
|
|
be51c37317 | ||
|
|
66796d1767 | ||
|
|
8e19b17855 | ||
|
|
8885497e14 | ||
|
|
626e047485 | ||
|
|
cc3d86c3ec | ||
|
|
0100083284 | ||
|
|
648e945471 | ||
|
|
7323d5ae0b | ||
|
|
578b9172ba | ||
|
|
2110006d17 | ||
|
|
01dda19d17 | ||
|
|
b519574b89 | ||
|
|
629ecabce7 | ||
|
|
131e302c55 | ||
|
|
239c92ba67 |
@@ -17,3 +17,4 @@ 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
@@ -1,3 +1,4 @@
|
||||
*.css linguist-detectable=false
|
||||
*.stories.mocks.tsx linguist-generated=true
|
||||
**/__story_mockdata__/** linguist-generated=true
|
||||
tests/e2e/drivers/** linguist-generated=true
|
||||
|
||||
47
.github/workflows/e2eci.yaml
vendored
@@ -3,16 +3,44 @@ 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'))) && contains(github.event.pull_request.labels.*.name, 'safe-to-e2e')
|
||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))) && needs.changes.outputs.e2e == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
@@ -41,9 +69,10 @@ 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'))) && contains(github.event.pull_request.labels.*.name, 'safe-to-e2e')
|
||||
(github.event_name == 'pull_request_target' && contains(github.event.pull_request.labels.*.name, 'safe-to-test'))) && needs.changes.outputs.e2e == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
@@ -152,3 +181,17 @@ 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
|
||||
|
||||
@@ -1,5 +1,219 @@
|
||||
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:
|
||||
@@ -3367,6 +3581,69 @@ 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:
|
||||
@@ -4420,6 +4697,7 @@ components:
|
||||
DashboardtypesQueryPlugin:
|
||||
discriminator:
|
||||
mapping:
|
||||
signoz/AIBuilderQuery: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpec'
|
||||
signoz/BuilderQuery: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec'
|
||||
signoz/ClickHouseSQL: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5ClickHouseQuery'
|
||||
signoz/CompositeQuery: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQuery'
|
||||
@@ -4429,6 +4707,7 @@ components:
|
||||
propertyName: kind
|
||||
oneOf:
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpec'
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpec'
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQuery'
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5QueryBuilderFormula'
|
||||
- $ref: '#/components/schemas/DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5PromQuery'
|
||||
@@ -4438,12 +4717,25 @@ 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:
|
||||
@@ -10225,6 +10517,14 @@ 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:
|
||||
@@ -10267,6 +10567,9 @@ components:
|
||||
- attributes
|
||||
- events
|
||||
- references
|
||||
- formattedInput
|
||||
- formattedOutput
|
||||
- formatterWarnings
|
||||
type: object
|
||||
SpantypesTraceAISummary:
|
||||
properties:
|
||||
@@ -10347,6 +10650,14 @@ 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:
|
||||
@@ -12658,9 +12969,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- planned-maintenance:list
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- planned-maintenance:list
|
||||
summary: List downtime schedules
|
||||
tags:
|
||||
- downtimeschedules
|
||||
@@ -12715,9 +13026,13 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- EDITOR
|
||||
- planned-maintenance:create
|
||||
- planned-maintenance:attach
|
||||
- rule:attach
|
||||
- tokenizer:
|
||||
- EDITOR
|
||||
- planned-maintenance:create
|
||||
- planned-maintenance:attach
|
||||
- rule:attach
|
||||
summary: Create downtime schedule
|
||||
tags:
|
||||
- downtimeschedules
|
||||
@@ -12762,9 +13077,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- EDITOR
|
||||
- planned-maintenance:delete
|
||||
- tokenizer:
|
||||
- EDITOR
|
||||
- planned-maintenance:delete
|
||||
summary: Delete downtime schedule
|
||||
tags:
|
||||
- downtimeschedules
|
||||
@@ -12820,9 +13135,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- planned-maintenance:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- planned-maintenance:read
|
||||
summary: Get downtime schedule by ID
|
||||
tags:
|
||||
- downtimeschedules
|
||||
@@ -12877,9 +13192,17 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- EDITOR
|
||||
- planned-maintenance:update
|
||||
- planned-maintenance:attach
|
||||
- planned-maintenance:detach
|
||||
- rule:attach
|
||||
- rule:detach
|
||||
- tokenizer:
|
||||
- EDITOR
|
||||
- planned-maintenance:update
|
||||
- planned-maintenance:attach
|
||||
- planned-maintenance:detach
|
||||
- rule:attach
|
||||
- rule:detach
|
||||
summary: Update downtime schedule
|
||||
tags:
|
||||
- downtimeschedules
|
||||
@@ -14101,9 +14424,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- route-policy:list
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- route-policy:list
|
||||
summary: List route policies
|
||||
tags:
|
||||
- routepolicies
|
||||
@@ -14158,9 +14481,13 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- route-policy:create
|
||||
- route-policy:attach
|
||||
- notification-channel:attach
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- route-policy:create
|
||||
- route-policy:attach
|
||||
- notification-channel:attach
|
||||
summary: Create route policy
|
||||
tags:
|
||||
- routepolicies
|
||||
@@ -14205,9 +14532,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- route-policy:delete
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- route-policy:delete
|
||||
summary: Delete route policy
|
||||
tags:
|
||||
- routepolicies
|
||||
@@ -14263,9 +14590,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- route-policy:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- route-policy:read
|
||||
summary: Get route policy by ID
|
||||
tags:
|
||||
- routepolicies
|
||||
@@ -14332,9 +14659,17 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- route-policy:update
|
||||
- route-policy:attach
|
||||
- route-policy:detach
|
||||
- notification-channel:attach
|
||||
- notification-channel:detach
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- route-policy:update
|
||||
- route-policy:attach
|
||||
- route-policy:detach
|
||||
- notification-channel:attach
|
||||
- notification-channel:detach
|
||||
summary: Update route policy
|
||||
tags:
|
||||
- routepolicies
|
||||
@@ -15753,10 +16088,8 @@ paths:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- api_key: []
|
||||
- tokenizer: []
|
||||
summary: Get stats
|
||||
tags:
|
||||
- stats
|
||||
@@ -16134,9 +16467,12 @@ paths:
|
||||
/api/v1/traces/{traceID}/thread:
|
||||
get:
|
||||
deprecated: false
|
||||
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.
|
||||
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.
|
||||
operationId: GetTraceThread
|
||||
parameters:
|
||||
- description: Page size, at most 100. 0 means 20.
|
||||
@@ -18602,9 +18938,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: Run Infra Monitoring Setup Checks
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -18676,9 +19012,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List Clusters for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -18760,9 +19096,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List DaemonSets for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -18838,9 +19174,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List Deployments for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -18906,9 +19242,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List Hosts for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -18988,9 +19324,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List Jobs for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -19069,9 +19405,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List Kubernetes Containers for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -19140,9 +19476,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List Namespaces for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -19215,9 +19551,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List Nodes for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -19291,9 +19627,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List Pods for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -19362,9 +19698,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List Volumes for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -19440,9 +19776,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- metrics:read
|
||||
summary: List StatefulSets for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
@@ -21731,9 +22067,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
summary: List rule saved views
|
||||
tags:
|
||||
- rules
|
||||
@@ -21789,9 +22125,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
summary: Create rule saved view
|
||||
tags:
|
||||
- rules
|
||||
@@ -21843,9 +22179,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
summary: Delete rule saved view
|
||||
tags:
|
||||
- rules
|
||||
@@ -21912,9 +22248,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
summary: Update rule saved view
|
||||
tags:
|
||||
- rules
|
||||
@@ -21963,9 +22299,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
summary: List alert rules
|
||||
tags:
|
||||
- rules
|
||||
@@ -22804,9 +23140,13 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- EDITOR
|
||||
- rule:create
|
||||
- rule:attach
|
||||
- notification-channel:attach
|
||||
- tokenizer:
|
||||
- EDITOR
|
||||
- rule:create
|
||||
- rule:attach
|
||||
- notification-channel:attach
|
||||
summary: Create alert rule
|
||||
tags:
|
||||
- rules
|
||||
@@ -22851,9 +23191,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- EDITOR
|
||||
- rule:delete
|
||||
- tokenizer:
|
||||
- EDITOR
|
||||
- rule:delete
|
||||
summary: Delete alert rule
|
||||
tags:
|
||||
- rules
|
||||
@@ -22909,9 +23249,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
summary: Get alert rule by ID
|
||||
tags:
|
||||
- rules
|
||||
@@ -23762,9 +24102,17 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- EDITOR
|
||||
- rule:update
|
||||
- rule:attach
|
||||
- rule:detach
|
||||
- notification-channel:attach
|
||||
- notification-channel:detach
|
||||
- tokenizer:
|
||||
- EDITOR
|
||||
- rule:update
|
||||
- rule:attach
|
||||
- rule:detach
|
||||
- notification-channel:attach
|
||||
- notification-channel:detach
|
||||
summary: Patch alert rule
|
||||
tags:
|
||||
- rules
|
||||
@@ -24603,9 +24951,17 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- EDITOR
|
||||
- rule:update
|
||||
- rule:attach
|
||||
- rule:detach
|
||||
- notification-channel:attach
|
||||
- notification-channel:detach
|
||||
- tokenizer:
|
||||
- EDITOR
|
||||
- rule:update
|
||||
- rule:attach
|
||||
- rule:detach
|
||||
- notification-channel:attach
|
||||
- notification-channel:detach
|
||||
summary: Update alert rule
|
||||
tags:
|
||||
- rules
|
||||
@@ -24705,9 +25061,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
summary: Get rule history filter keys
|
||||
tags:
|
||||
- rules
|
||||
@@ -24815,9 +25171,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
summary: Get rule history filter values
|
||||
tags:
|
||||
- rules
|
||||
@@ -24890,9 +25246,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
summary: Get rule overall status timeline
|
||||
tags:
|
||||
- rules
|
||||
@@ -24962,9 +25318,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
summary: Get rule history stats
|
||||
tags:
|
||||
- rules
|
||||
@@ -25054,9 +25410,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
summary: Get rule history timeline
|
||||
tags:
|
||||
- rules
|
||||
@@ -25129,9 +25485,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:read
|
||||
summary: Get top contributors to rule firing
|
||||
tags:
|
||||
- rules
|
||||
@@ -25971,9 +26327,13 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- EDITOR
|
||||
- rule:create
|
||||
- rule:attach
|
||||
- notification-channel:attach
|
||||
- tokenizer:
|
||||
- EDITOR
|
||||
- rule:create
|
||||
- rule:attach
|
||||
- notification-channel:attach
|
||||
summary: Test alert rule
|
||||
tags:
|
||||
- rules
|
||||
@@ -27839,9 +28199,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
- rule:list
|
||||
summary: List alert rules (v3)
|
||||
tags:
|
||||
- rules
|
||||
@@ -28280,7 +28640,9 @@ 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
|
||||
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.
|
||||
operationId: GetWaterfallV4
|
||||
parameters:
|
||||
- in: path
|
||||
|
||||
@@ -20,6 +20,7 @@ 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/`
|
||||
|
||||
62
docs/contributing/go/spanmapper.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# 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).
|
||||
@@ -86,6 +86,9 @@ 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
|
||||
@@ -112,6 +115,24 @@ 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>/`.
|
||||
|
||||
@@ -25,6 +25,7 @@ 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:
|
||||
|
||||
@@ -41,6 +41,12 @@ 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
|
||||
|
||||
@@ -136,6 +142,8 @@ 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
|
||||
|
||||
|
||||
@@ -41,6 +41,20 @@ 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 {}
|
||||
|
||||
@@ -49,12 +49,13 @@
|
||||
"@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.1.6",
|
||||
"@signozhq/design-tokens": "2.2.1",
|
||||
"@signozhq/icons": "0.4.0",
|
||||
"@signozhq/ui": "0.1.0",
|
||||
"@signozhq/ui": "0.2.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
@@ -62,6 +62,9 @@ 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)
|
||||
@@ -72,14 +75,14 @@ importers:
|
||||
specifier: 5.3.0
|
||||
version: 5.3.0
|
||||
'@signozhq/design-tokens':
|
||||
specifier: 2.1.6
|
||||
version: 2.1.6
|
||||
specifier: 2.2.1
|
||||
version: 2.2.1
|
||||
'@signozhq/icons':
|
||||
specifier: 0.4.0
|
||||
version: 0.4.0
|
||||
'@signozhq/ui':
|
||||
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)
|
||||
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)
|
||||
'@tanstack/react-table':
|
||||
specifier: 8.21.3
|
||||
version: 8.21.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
|
||||
@@ -1284,6 +1287,33 @@ 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==}
|
||||
|
||||
@@ -1726,20 +1756,20 @@ packages:
|
||||
resolution: {integrity: sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==}
|
||||
engines: {node: ^20.19.0 || ^22.13.0 || >=24}
|
||||
|
||||
'@floating-ui/core@1.5.2':
|
||||
resolution: {integrity: sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==}
|
||||
'@floating-ui/core@1.8.0':
|
||||
resolution: {integrity: sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==}
|
||||
|
||||
'@floating-ui/dom@1.5.3':
|
||||
resolution: {integrity: sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==}
|
||||
'@floating-ui/dom@1.8.0':
|
||||
resolution: {integrity: sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==}
|
||||
|
||||
'@floating-ui/react-dom@2.0.4':
|
||||
resolution: {integrity: sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==}
|
||||
'@floating-ui/react-dom@2.1.9':
|
||||
resolution: {integrity: sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
'@floating-ui/utils@0.1.6':
|
||||
resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==}
|
||||
'@floating-ui/utils@0.2.12':
|
||||
resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==}
|
||||
|
||||
'@gerrit0/mini-shiki@3.23.0':
|
||||
resolution: {integrity: sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==}
|
||||
@@ -2920,19 +2950,6 @@ 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:
|
||||
@@ -2946,15 +2963,6 @@ 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:
|
||||
@@ -3008,19 +3016,6 @@ 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:
|
||||
@@ -3048,15 +3043,6 @@ 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:
|
||||
@@ -3066,19 +3052,6 @@ 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:
|
||||
@@ -3144,19 +3117,6 @@ 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:
|
||||
@@ -3222,15 +3182,6 @@ 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:
|
||||
@@ -3240,19 +3191,6 @@ 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:
|
||||
@@ -3266,45 +3204,6 @@ 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:
|
||||
@@ -3341,15 +3240,6 @@ 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:
|
||||
@@ -3721,14 +3611,14 @@ packages:
|
||||
'@sideway/pinpoint@2.0.0':
|
||||
resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==}
|
||||
|
||||
'@signozhq/design-tokens@2.1.6':
|
||||
resolution: {integrity: sha512-n0cH4TRS1Xj7BR38HGiwIBDekIOAnDgl2JyYmAyUvws0L1nlE0txSHO5NiOBJ22Dm8uMva0v5/rOHxpKsGQbsw==}
|
||||
'@signozhq/design-tokens@2.2.1':
|
||||
resolution: {integrity: sha512-ulf6lhWUP6qxqsyemQySUMfOIKRvefvZ5rBRv+wlhOpPfYPk3uatPmPkKEhAjISpX8f9g1Gg8wgRZxUCysf20Q==}
|
||||
|
||||
'@signozhq/icons@0.4.0':
|
||||
resolution: {integrity: sha512-QctNvNPlQlsefFdIVmSy/P/7a1IcQ3IryKP0KPMqtoqew3q89FGUmFa5IfEJ7Z3CHhtG1zaXwBKqP72QkLyA3g==}
|
||||
|
||||
'@signozhq/ui@0.1.0':
|
||||
resolution: {integrity: sha512-FjqmQhz+WtpGl29gPGZey5YwNe4p++TCyX4jaGUhAgvlBGRmxmAKW0lFRrSOYTPbgFL5UaDCW0wR62aiGHZ+FA==}
|
||||
'@signozhq/ui@0.2.0':
|
||||
resolution: {integrity: sha512-j20ldiOkR150ON0BCDhlq6fXGoPN++7w6e4t2HtM2XMM4buJO7aHzPzNKvDRZ2UUf/LGPULPAW8w6sg6gUVHyw==}
|
||||
peerDependencies:
|
||||
'@signozhq/icons': 0.3.0
|
||||
react: ^18.2.0
|
||||
@@ -8520,6 +8410,11 @@ 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'}
|
||||
@@ -8907,6 +8802,9 @@ 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==}
|
||||
|
||||
@@ -11154,6 +11052,31 @@ 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': {}
|
||||
@@ -11598,22 +11521,22 @@ snapshots:
|
||||
'@eslint/core': 1.2.1
|
||||
levn: 0.4.1
|
||||
|
||||
'@floating-ui/core@1.5.2':
|
||||
'@floating-ui/core@1.8.0':
|
||||
dependencies:
|
||||
'@floating-ui/utils': 0.1.6
|
||||
'@floating-ui/utils': 0.2.12
|
||||
|
||||
'@floating-ui/dom@1.5.3':
|
||||
'@floating-ui/dom@1.8.0':
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.5.2
|
||||
'@floating-ui/utils': 0.1.6
|
||||
'@floating-ui/core': 1.8.0
|
||||
'@floating-ui/utils': 0.2.12
|
||||
|
||||
'@floating-ui/react-dom@2.0.4(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)':
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.5.3
|
||||
'@floating-ui/dom': 1.8.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
|
||||
'@floating-ui/utils@0.1.6': {}
|
||||
'@floating-ui/utils@0.2.12': {}
|
||||
|
||||
'@gerrit0/mini-shiki@3.23.0':
|
||||
dependencies:
|
||||
@@ -12763,21 +12686,6 @@ 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)
|
||||
@@ -12790,12 +12698,6 @@ 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
|
||||
@@ -12850,21 +12752,6 @@ 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
|
||||
@@ -12886,13 +12773,6 @@ 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)
|
||||
@@ -12900,32 +12780,6 @@ 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
|
||||
@@ -12951,7 +12805,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.0.4(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)
|
||||
'@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)
|
||||
@@ -12995,15 +12849,6 @@ 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)
|
||||
@@ -13099,13 +12944,6 @@ 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)
|
||||
@@ -13113,20 +12951,6 @@ 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
|
||||
@@ -13143,53 +12967,6 @@ 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
|
||||
@@ -13218,12 +12995,6 @@ 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
|
||||
@@ -13272,14 +13043,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.28.2
|
||||
'@babel/runtime': 7.29.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.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
|
||||
'@rc-component/mutate-observer@1.1.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0)':
|
||||
dependencies:
|
||||
@@ -13291,7 +13062,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.28.2
|
||||
'@babel/runtime': 7.29.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
|
||||
@@ -13559,16 +13330,15 @@ snapshots:
|
||||
|
||||
'@sideway/pinpoint@2.0.0': {}
|
||||
|
||||
'@signozhq/design-tokens@2.1.6': {}
|
||||
'@signozhq/design-tokens@2.2.1': {}
|
||||
|
||||
'@signozhq/icons@0.4.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)':
|
||||
'@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)':
|
||||
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)
|
||||
@@ -13576,11 +13346,7 @@ 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)
|
||||
@@ -13594,16 +13360,19 @@ 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
|
||||
@@ -15129,10 +14898,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.2(@types/react@18.0.26)(react@18.2.0)
|
||||
'@radix-ui/react-compose-refs': 1.1.5(@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.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)
|
||||
'@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)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
transitivePeerDependencies:
|
||||
@@ -19277,6 +19046,10 @@ 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
|
||||
@@ -19762,6 +19535,8 @@ snapshots:
|
||||
|
||||
requires-port@1.0.0: {}
|
||||
|
||||
reselect@5.3.0: {}
|
||||
|
||||
reserved-words@0.1.2: {}
|
||||
|
||||
resize-observer-polyfill@1.5.1: {}
|
||||
@@ -19867,7 +19642,7 @@ snapshots:
|
||||
|
||||
rtl-css-js@1.16.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
|
||||
run-applescript@7.1.0: {}
|
||||
|
||||
@@ -20679,7 +20454,7 @@ snapshots:
|
||||
|
||||
unload@2.2.0:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.2
|
||||
'@babel/runtime': 7.29.2
|
||||
detect-node: 2.1.0
|
||||
|
||||
unplugin@2.3.11:
|
||||
|
||||
@@ -41,17 +41,16 @@ 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');
|
||||
|
||||
|
||||
22
frontend/src/api/__tests__/ErrorResponseHandler.test.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
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)).toEqual({
|
||||
statusCode: 500,
|
||||
payload: null,
|
||||
error: 'Something went wrong',
|
||||
message: null,
|
||||
});
|
||||
expect(consoleError).not.toHaveBeenCalled();
|
||||
|
||||
consoleError.mockRestore();
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,264 @@
|
||||
* * 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
|
||||
@@ -4211,195 +4469,10 @@ export interface DashboardGridLayoutSpecDTO {
|
||||
repeatVariable?: string;
|
||||
}
|
||||
|
||||
export enum DashboardtypesAreaFillModeDTO {
|
||||
solid = 'solid',
|
||||
gradient = 'gradient',
|
||||
export enum DashboardtypesAIBuilderQuerySpecDTOSignal {
|
||||
traces = 'traces',
|
||||
}
|
||||
/**
|
||||
* @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 {
|
||||
export interface Querybuildertypesv5TraceAggregationDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
@@ -4638,13 +4711,274 @@ 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
|
||||
@@ -4835,17 +5169,6 @@ export interface Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTyp
|
||||
stepInterval?: Querybuildertypesv5StepDTO;
|
||||
}
|
||||
|
||||
export interface Querybuildertypesv5TraceAggregationDTO {
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
alias?: string;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
expression?: string;
|
||||
}
|
||||
|
||||
export enum Querybuildertypesv5QueryBuilderQueryGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5TraceAggregationDTOSignal {
|
||||
traces = 'traces',
|
||||
}
|
||||
@@ -5404,6 +5727,18 @@ 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',
|
||||
}
|
||||
@@ -5695,6 +6030,7 @@ export interface DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQu
|
||||
|
||||
export type DashboardtypesQueryPluginDTO =
|
||||
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesBuilderQuerySpecDTO
|
||||
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesDashboardtypesAIBuilderQuerySpecDTO
|
||||
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5CompositeQueryDTO
|
||||
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5QueryBuilderFormulaDTO
|
||||
| DashboardtypesQueryPluginVariantGithubComSigNozSignozPkgTypesQuerybuildertypesQuerybuildertypesv5PromQueryDTO
|
||||
@@ -6373,6 +6709,7 @@ 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',
|
||||
@@ -11452,6 +11789,12 @@ export interface SpantypesThreadSpanDTO {
|
||||
* @type array
|
||||
*/
|
||||
events: SpantypesEventDTO[];
|
||||
formattedInput: AiobservabilitytypesInputMessagesDTO;
|
||||
formattedOutput: AiobservabilitytypesOutputMessagesDTO;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
formatterWarnings: string[];
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
@@ -11567,6 +11910,12 @@ export interface SpantypesWaterfallSpanDTO {
|
||||
* @minimum 0
|
||||
*/
|
||||
flags?: number;
|
||||
formattedInput?: AiobservabilitytypesInputMessagesDTO;
|
||||
formattedOutput?: AiobservabilitytypesOutputMessagesDTO;
|
||||
/**
|
||||
* @type array
|
||||
*/
|
||||
formatterWarnings?: string[];
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
|
||||
@@ -261,7 +261,7 @@ export const invalidateGetTraceSummary = async (
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
* @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
|
||||
* 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.
|
||||
* @summary Get waterfall view for a trace
|
||||
*/
|
||||
export const getWaterfallV4 = (
|
||||
|
||||
@@ -61,7 +61,11 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
],
|
||||
values: [
|
||||
{ timestamp: 1000, value: 10 } as unknown as TimeSeriesValue,
|
||||
{ timestamp: 2000, value: 12 } as unknown as TimeSeriesValue,
|
||||
{
|
||||
timestamp: 2000,
|
||||
value: 12,
|
||||
partial: true,
|
||||
} as unknown as TimeSeriesValue,
|
||||
],
|
||||
} as unknown as TimeSeries,
|
||||
],
|
||||
@@ -104,7 +108,7 @@ describe('convertV5ResponseToLegacy', () => {
|
||||
labels: { 'service.name': 'adservice' },
|
||||
values: [
|
||||
{ timestamp: 1000, value: '10' },
|
||||
{ timestamp: 2000, value: '12' },
|
||||
{ timestamp: 2000, value: '12', partial: true },
|
||||
],
|
||||
metaData: expect.objectContaining({
|
||||
alias: '__result_0',
|
||||
|
||||
@@ -125,6 +125,7 @@ function convertTimeSeriesData(
|
||||
values: series.values.map((value: any) => ({
|
||||
timestamp: value.timestamp,
|
||||
value: String(value.value),
|
||||
...(value.partial ? { partial: true } : {}),
|
||||
})),
|
||||
metaData: {
|
||||
alias,
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<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"/>
|
||||
<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"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
@@ -1,3 +1 @@
|
||||
<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>
|
||||
<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>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 451 B |
@@ -1 +1 @@
|
||||
<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>
|
||||
<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>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 529 B |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
@@ -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="#C0C1C3" 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="#7A8291" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 587 B After Width: | Height: | Size: 587 B |
@@ -1 +1 @@
|
||||
<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>
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 398 B After Width: | Height: | Size: 398 B |
@@ -1 +1 @@
|
||||
<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>
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 958 B After Width: | Height: | Size: 958 B |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 463 B |
@@ -1 +1 @@
|
||||
<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>
|
||||
<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>
|
||||
|
||||
|
Before Width: | Height: | Size: 316 B After Width: | Height: | Size: 251 B |
@@ -1 +1 @@
|
||||
<svg width='234' height='42' viewBox='0 0 234 42' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M18 30H6V18H18V30Z' fill='#CFCECD'/><path d='M18 12H6V30H18V12ZM24 36H0V6H24V36Z' fill='#656363'/><path d='M48 30H36V18H48V30Z' fill='#CFCECD'/><path d='M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z' fill='#656363'/><path d='M84 24V30H66V24H84Z' fill='#CFCECD'/><path d='M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z' fill='#656363'/><path d='M108 36H96V18H108V36Z' fill='#CFCECD'/><path d='M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z' fill='#656363'/><path d='M144 30H126V18H144V30Z' fill='#CFCECD'/><path d='M144 12H126V30H144V36H120V6H144V12Z' fill='#211E1E'/><path d='M168 30H156V18H168V30Z' fill='#CFCECD'/><path d='M168 12H156V30H168V12ZM174 36H150V6H174V36Z' fill='#211E1E'/><path d='M198 30H186V18H198V30Z' fill='#CFCECD'/><path d='M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z' fill='#211E1E'/><path d='M234 24V30H216V24H234Z' fill='#CFCECD'/><path d='M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z' fill='#211E1E'/></svg>
|
||||
<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>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 137 B |
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
@@ -1 +1 @@
|
||||
<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>
|
||||
<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>
|
||||
|
||||
|
Before Width: | Height: | Size: 362 B After Width: | Height: | Size: 376 B |
@@ -1 +1 @@
|
||||
<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>
|
||||
<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>
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -17,7 +17,10 @@ function ModalFixture(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
data-testid="open-add-credit-card"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
size="md"
|
||||
testId="open-add-credit-card"
|
||||
onClick={(): void => setOpen(true)}
|
||||
>
|
||||
Contact support
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
:global(.ant-breadcrumb-separator) {
|
||||
color: var(--muted-foreground);
|
||||
}
|
||||
|
||||
[data-slot='button'] {
|
||||
padding: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
.item {
|
||||
--button-padding: 0;
|
||||
--button-font-size: var(--periscope-font-size-base);
|
||||
}
|
||||
|
||||
.itemLast {
|
||||
color: var(--muted-foreground);
|
||||
font-size: var(--periscope-font-size-base);
|
||||
color: var(--button-secondary-label);
|
||||
font-size: var(--button-font-size, var(--periscope-font-size-base));
|
||||
line-height: var(--button-size-line-height, 16px);
|
||||
}
|
||||
|
||||
@@ -26,9 +26,9 @@ function BreadcrumbItem({
|
||||
|
||||
return (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="md"
|
||||
variant="link"
|
||||
color="secondary"
|
||||
className={styles.item}
|
||||
onClick={(e: React.MouseEvent): void => {
|
||||
if (!('route' in props) || !props.route) {
|
||||
return;
|
||||
|
||||
@@ -34,21 +34,23 @@ function ErrorEmptyState({
|
||||
</div>
|
||||
<div className={styles.actions}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
prefix={<LifeBuoy size={14} />}
|
||||
onClick={onContactSupport}
|
||||
data-testid="error-contact-support-button"
|
||||
testId="error-contact-support-button"
|
||||
>
|
||||
Contact Support
|
||||
</Button>
|
||||
{onRefresh && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<RefreshCw size={14} />}
|
||||
onClick={onRefresh}
|
||||
data-testid="error-refresh-button"
|
||||
testId="error-refresh-button"
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
|
||||
@@ -1,20 +1,32 @@
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
.labelBadge {
|
||||
cursor: default;
|
||||
font-size: 12px;
|
||||
// Kept mounted so its width can still be measured.
|
||||
.overflowed {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
--badge-display: inline;
|
||||
|
||||
max-width: 180px;
|
||||
text-overflow: ellipsis;
|
||||
// The only visible label truncates instead of pushing `+N` out of view.
|
||||
.labelColumn > .shrinkable {
|
||||
flex-shrink: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.overflowTrigger {
|
||||
@@ -22,11 +34,6 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.overflowBadge {
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.labelPopover {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { TooltipProvider } from '@signozhq/ui/tooltip';
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
|
||||
import LabelColumn from './LabelColumn';
|
||||
|
||||
let resizeCallback: ResizeObserverCallback | null = null;
|
||||
const BADGE_WIDTH = 60;
|
||||
|
||||
let resizeCallbacks: ResizeObserverCallback[] = [];
|
||||
|
||||
class MockResizeObserver {
|
||||
constructor(callback: ResizeObserverCallback) {
|
||||
resizeCallback = callback;
|
||||
resizeCallbacks.push(callback);
|
||||
}
|
||||
|
||||
observe = jest.fn();
|
||||
@@ -15,15 +16,23 @@ class MockResizeObserver {
|
||||
disconnect = jest.fn();
|
||||
}
|
||||
|
||||
function triggerResize(width: number): void {
|
||||
if (resizeCallback) {
|
||||
act(() => {
|
||||
resizeCallback?.(
|
||||
[{ contentRect: { width } } as ResizeObserverEntry],
|
||||
{} as ResizeObserver,
|
||||
);
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
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(() => {
|
||||
@@ -31,13 +40,14 @@ beforeAll(() => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resizeCallback = null;
|
||||
resizeCallbacks = [];
|
||||
jest.restoreAllMocks();
|
||||
});
|
||||
|
||||
function renderWithProviders(
|
||||
ui: React.ReactElement,
|
||||
): ReturnType<typeof render> {
|
||||
return render(<TooltipProvider>{ui}</TooltipProvider>);
|
||||
return render(ui);
|
||||
}
|
||||
|
||||
describe('LabelColumn', () => {
|
||||
@@ -54,13 +64,10 @@ 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();
|
||||
@@ -93,19 +100,37 @@ describe('LabelColumn', () => {
|
||||
expect(screen.getByTestId('label-tag-service')).toHaveTextContent('service');
|
||||
});
|
||||
|
||||
it('should show overflow badge with remaining count when container is narrow', () => {
|
||||
it('should update the overflow count when the container resizes', () => {
|
||||
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} />);
|
||||
|
||||
// Simulate narrow container to trigger overflow (shows 3 labels)
|
||||
// 220px fits first 3 badges before overflow
|
||||
triggerResize(220);
|
||||
expect(screen.queryByTestId('label-overflow-badge')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Overflow badge shows +3 (remaining labels)
|
||||
const overflowBadge = screen.getByTestId('label-overflow-badge');
|
||||
expect(overflowBadge).toBeInTheDocument();
|
||||
expect(overflowBadge).toHaveTextContent('+3');
|
||||
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');
|
||||
});
|
||||
|
||||
it('should render empty when no labels provided', () => {
|
||||
@@ -126,11 +151,9 @@ 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();
|
||||
|
||||
@@ -1,35 +1,19 @@
|
||||
import { Copy } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import {
|
||||
TooltipContent,
|
||||
TooltipRoot,
|
||||
TooltipTrigger,
|
||||
} from '@signozhq/ui/tooltip';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import cx from 'classnames';
|
||||
import { useLayoutEffect, useRef, useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
import LabelTag from './LabelTag';
|
||||
|
||||
import styles from './LabelColumn.module.scss';
|
||||
import { BADGE_GAP, estimateBadgeWidth, OVERFLOW_BADGE_WIDTH } from './utils';
|
||||
import { getVisibleCount } from './utils';
|
||||
|
||||
export interface LabelColumnProps {
|
||||
labels: string[];
|
||||
color?:
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'success'
|
||||
| 'error'
|
||||
| 'warning'
|
||||
| 'robin'
|
||||
| 'forest'
|
||||
| 'amber'
|
||||
| 'sienna'
|
||||
| 'cherry'
|
||||
| 'sakura'
|
||||
| 'aqua'
|
||||
| 'vanilla';
|
||||
color?: BadgeColorType;
|
||||
value?: { [key: string]: string };
|
||||
}
|
||||
|
||||
@@ -39,60 +23,37 @@ function LabelColumn({
|
||||
color = 'primary',
|
||||
}: LabelColumnProps): JSX.Element {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [maxVisibleCount, setMaxVisibleCount] = useState(labels.length);
|
||||
const [visibleCount, setVisibleCount] = useState(labels.length);
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
|
||||
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(() => {
|
||||
useLayoutEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
const observer = new ResizeObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (entry && entry.contentRect.width > 0) {
|
||||
setMaxVisibleCount(calculateMaxVisible(entry.contentRect.width));
|
||||
const measure = (): void => {
|
||||
const available = container.clientWidth;
|
||||
if (available <= 0) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
if (container.clientWidth > 0) {
|
||||
setMaxVisibleCount(calculateMaxVisible(container.clientWidth));
|
||||
}
|
||||
measure();
|
||||
|
||||
return (): void => observer.disconnect();
|
||||
}, [calculateMaxVisible]);
|
||||
}, [labels, value]);
|
||||
|
||||
const needsOverflow = labels.length > maxVisibleCount;
|
||||
const visibleLabels = needsOverflow
|
||||
? labels.slice(0, maxVisibleCount)
|
||||
: labels;
|
||||
const remainingLabels = needsOverflow ? labels.slice(maxVisibleCount) : [];
|
||||
const remainingLabels = labels.slice(visibleCount);
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -100,24 +61,23 @@ function LabelColumn({
|
||||
className={styles.labelColumn}
|
||||
data-testid="label-column"
|
||||
>
|
||||
{visibleLabels.map((label) => (
|
||||
<LabelTag key={label} label={label} color={color} value={value?.[label]} />
|
||||
{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,
|
||||
})}
|
||||
/>
|
||||
))}
|
||||
{remainingLabels.length > 0 && (
|
||||
<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">
|
||||
<Tooltip
|
||||
side="bottom"
|
||||
align="end"
|
||||
title={
|
||||
<div className={styles.tooltipContent}>
|
||||
<span>
|
||||
{remainingLabels
|
||||
@@ -140,8 +100,14 @@ function LabelColumn({
|
||||
<Copy size={12} />
|
||||
</button>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</TooltipRoot>
|
||||
}
|
||||
>
|
||||
<span>
|
||||
<Badge color={color} variant="outlined" testId="label-overflow-badge">
|
||||
+{remainingLabels.length}
|
||||
</Badge>
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,3 @@
|
||||
.labelBadge {
|
||||
cursor: default;
|
||||
font-size: 12px;
|
||||
|
||||
max-width: 180px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.labelValue {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -1,35 +1,24 @@
|
||||
import { Copy } from '@signozhq/icons';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
import {
|
||||
TooltipContent,
|
||||
TooltipRoot,
|
||||
TooltipTrigger,
|
||||
} from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
import styles from './LabelTag.module.scss';
|
||||
|
||||
export interface LabelTagProps {
|
||||
label: string;
|
||||
color?:
|
||||
| 'primary'
|
||||
| 'secondary'
|
||||
| 'success'
|
||||
| 'error'
|
||||
| 'warning'
|
||||
| 'robin'
|
||||
| 'forest'
|
||||
| 'amber'
|
||||
| 'sienna'
|
||||
| 'cherry'
|
||||
| 'sakura'
|
||||
| 'aqua'
|
||||
| 'vanilla';
|
||||
color?: BadgeColorType;
|
||||
value?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function LabelTag({ label, value, color }: LabelTagProps): JSX.Element {
|
||||
function LabelTag({
|
||||
label,
|
||||
value,
|
||||
color,
|
||||
className,
|
||||
}: LabelTagProps): JSX.Element {
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
const displayText = value ? `${label}: ${value}` : label;
|
||||
const searchFormat = value ? `${label} ${value}` : label;
|
||||
@@ -41,20 +30,8 @@ function LabelTag({ label, value, color }: LabelTagProps): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<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>
|
||||
<Tooltip
|
||||
title={
|
||||
<div className={styles.tooltipContent}>
|
||||
<span>{displayText}</span>
|
||||
<button
|
||||
@@ -66,8 +43,19 @@ function LabelTag({ label, value, color }: LabelTagProps): JSX.Element {
|
||||
<Copy size={12} />
|
||||
</button>
|
||||
</div>
|
||||
</TooltipContent>
|
||||
</TooltipRoot>
|
||||
}
|
||||
>
|
||||
<span className={className}>
|
||||
<Badge
|
||||
textTransform="none"
|
||||
color={color ?? 'secondary'}
|
||||
variant="outlined"
|
||||
testId={`label-tag-${label}`}
|
||||
>
|
||||
<span className={styles.labelValue}>{displayText}</span>
|
||||
</Badge>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,33 @@
|
||||
export const BADGE_GAP = 4;
|
||||
export const OVERFLOW_BADGE_WIDTH = 40;
|
||||
|
||||
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,
|
||||
/**
|
||||
* 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,
|
||||
);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -30,21 +30,23 @@ function NoResultsEmptyState({
|
||||
<div className={styles.actions}>
|
||||
{onClear && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={onClear}
|
||||
data-testid="no-results-clear-button"
|
||||
testId="no-results-clear-button"
|
||||
>
|
||||
{clearButtonText}
|
||||
</Button>
|
||||
)}
|
||||
{onRefresh && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<RefreshCw size={14} />}
|
||||
onClick={onRefresh}
|
||||
data-testid="no-results-refresh-button"
|
||||
testId="no-results-refresh-button"
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BadgeColor } from '@signozhq/ui/badge';
|
||||
import type { BadgeColorType } 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, BadgeColor> = {
|
||||
critical: 'error',
|
||||
error: 'error',
|
||||
export const SEVERITY_BADGE_COLORS: Record<string, BadgeColorType> = {
|
||||
critical: 'danger',
|
||||
error: 'danger',
|
||||
warning: 'warning',
|
||||
info: 'primary',
|
||||
};
|
||||
|
||||
@@ -33,36 +33,3 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,11 @@ function AuthHeader(): JSX.Element {
|
||||
<span className="auth-header-logo-text">SigNoz</span>
|
||||
</div>
|
||||
<Button
|
||||
className="auth-header-help-button"
|
||||
size="md"
|
||||
prefix={<LifeBuoy size={12} />}
|
||||
onClick={handleGetHelp}
|
||||
variant="solid"
|
||||
color="none"
|
||||
color="secondary"
|
||||
>
|
||||
Get Help
|
||||
</Button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Dispatch, SetStateAction, useState } from 'react';
|
||||
import { Check, Plus, X } from '@signozhq/icons';
|
||||
import { Button, Flex } from 'antd';
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Pill } from '@signozhq/ui/pill';
|
||||
import Input from 'components/Input';
|
||||
|
||||
import './Badges.styles.scss';
|
||||
@@ -47,18 +47,13 @@ function Badges({ tags, setTags }: AddTagsProps): JSX.Element {
|
||||
return (
|
||||
<div className="tags-container">
|
||||
{tags.map<React.ReactNode>((tag) => (
|
||||
<Badge
|
||||
<Pill.Closeable
|
||||
textTransform="none"
|
||||
key={tag}
|
||||
color="vanilla"
|
||||
style={{ userSelect: 'none' }}
|
||||
closable
|
||||
onClose={(e): void => {
|
||||
e.preventDefault();
|
||||
handleClose(tag);
|
||||
}}
|
||||
onClose={() => handleClose(tag)}
|
||||
>
|
||||
{tag}
|
||||
</Badge>
|
||||
</Pill.Closeable>
|
||||
))}
|
||||
|
||||
{inputVisible && (
|
||||
|
||||
@@ -39,9 +39,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cloud-service-data-collected-table-tooltip {
|
||||
max-width: 280px;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
CloudintegrationtypesCollectedMetricDTO,
|
||||
} from 'api/generated/services/sigNoz.schemas';
|
||||
import { BarChart, Info, ScrollText } from '@signozhq/icons';
|
||||
import { TooltipProvider, TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
|
||||
import './CloudServiceDataCollected.styles.scss';
|
||||
|
||||
@@ -88,23 +88,15 @@ function CloudServiceDataCollected({
|
||||
<BarChart size={14} />
|
||||
Metrics
|
||||
{metricsInfoTooltip && (
|
||||
<TooltipProvider>
|
||||
<TooltipSimple
|
||||
title={metricsInfoTooltip}
|
||||
side="top"
|
||||
tooltipContentProps={{
|
||||
className: 'cloud-service-data-collected-table-tooltip',
|
||||
}}
|
||||
<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"
|
||||
>
|
||||
<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>
|
||||
<Info size={12} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<Table
|
||||
|
||||
@@ -2,6 +2,13 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.copyButton {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 8px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.codeBlockSyntaxHighlighter {
|
||||
background-color: var(--l2-background) !important;
|
||||
border-radius: 4px !important;
|
||||
|
||||
@@ -2,6 +2,7 @@ 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';
|
||||
@@ -52,16 +53,20 @@ function CodeBlock({
|
||||
data-testid="code-block-container"
|
||||
>
|
||||
{showCopyButton ? (
|
||||
<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 }}
|
||||
/>
|
||||
<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>
|
||||
) : null}
|
||||
<SyntaxHighlighter
|
||||
style={a11yDark}
|
||||
|
||||
@@ -134,26 +134,27 @@ function CreateServiceAccountModal(): JSX.Element {
|
||||
|
||||
<DialogFooter className="create-sa-modal__footer">
|
||||
<Button
|
||||
size="md"
|
||||
type="button"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
data-testid="create-sa-cancel-btn"
|
||||
testId="create-sa-cancel-btn"
|
||||
prefix={<X size={12} />}
|
||||
>
|
||||
<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}
|
||||
data-testid="create-sa-submit-btn"
|
||||
testId="create-sa-submit-btn"
|
||||
>
|
||||
Create Service Account
|
||||
</AuthZButton>
|
||||
|
||||
@@ -5,6 +5,7 @@ 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';
|
||||
@@ -60,7 +61,10 @@ describe('CreateServiceAccountModal', () => {
|
||||
await screen.findByTestId('create-sa-name-input');
|
||||
|
||||
await waitFor(() =>
|
||||
expect(screen.getByTestId('create-sa-submit-btn')).toBeDisabled(),
|
||||
expect(screen.getByTestId('create-sa-submit-btn')).toHaveAttribute(
|
||||
'aria-disabled',
|
||||
'true',
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -72,10 +76,14 @@ describe('CreateServiceAccountModal', () => {
|
||||
const submitBtn = await screen.findByTestId('create-sa-submit-btn');
|
||||
|
||||
await user.type(nameInput, 'test');
|
||||
await waitFor(() => expect(submitBtn).not.toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(submitBtn).not.toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
|
||||
await user.clear(nameInput);
|
||||
await waitFor(() => expect(submitBtn).toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(submitBtn).toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
});
|
||||
|
||||
it('successful submit shows toast.success and closes modal', async () => {
|
||||
@@ -86,7 +94,9 @@ describe('CreateServiceAccountModal', () => {
|
||||
await user.type(nameInput, 'Deploy Bot');
|
||||
|
||||
const submitBtn = screen.getByTestId('create-sa-submit-btn');
|
||||
await waitFor(() => expect(submitBtn).not.toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(submitBtn).not.toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
await user.click(submitBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -102,6 +112,40 @@ 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 });
|
||||
|
||||
@@ -120,7 +164,9 @@ describe('CreateServiceAccountModal', () => {
|
||||
await user.type(nameInput, 'Dupe Bot');
|
||||
|
||||
const submitBtn = screen.getByTestId('create-sa-submit-btn');
|
||||
await waitFor(() => expect(submitBtn).not.toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(submitBtn).not.toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
await user.click(submitBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -180,7 +226,10 @@ describe('CreateServiceAccountModal', () => {
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
// The footer lives outside the guard: submit is gated, Cancel still works.
|
||||
expect(screen.getByTestId('create-sa-submit-btn')).toBeDisabled();
|
||||
expect(screen.getByTestId('create-sa-submit-btn')).toHaveAttribute(
|
||||
'aria-disabled',
|
||||
'true',
|
||||
);
|
||||
|
||||
await user.click(screen.getByTestId('create-sa-cancel-btn'));
|
||||
|
||||
|
||||
@@ -4,30 +4,6 @@
|
||||
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;
|
||||
|
||||
@@ -72,6 +72,8 @@ 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({
|
||||
@@ -95,6 +97,7 @@ function CustomTimePicker({
|
||||
minTime,
|
||||
maxTime,
|
||||
isModalTimeSelection = false,
|
||||
'data-testid': dataTestId,
|
||||
}: CustomTimePickerProps): JSX.Element {
|
||||
const [selectedTimePlaceholderValue, setSelectedTimePlaceholderValue] =
|
||||
useState('Select / Enter Time Range');
|
||||
@@ -596,6 +599,7 @@ function CustomTimePicker({
|
||||
>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
data-testid={dataTestId}
|
||||
autoComplete="off"
|
||||
className={cx(
|
||||
'timeSelection-input',
|
||||
@@ -650,20 +654,20 @@ function CustomTimePicker({
|
||||
</Popover>
|
||||
</Tooltip>
|
||||
{!showLiveLogs && !isModalTimeSelection && (
|
||||
<Tooltip
|
||||
title={
|
||||
zoomOutDisabled ? 'Zoom out time range is limited to 1 month' : 'Zoom out'
|
||||
}
|
||||
>
|
||||
<Tooltip title={zoomOutDisabled ? undefined : 'Zoom out'}>
|
||||
<Button
|
||||
className="zoom-out-btn"
|
||||
disabledTooltip="Zoom out time range is limited to 1 month"
|
||||
size="md"
|
||||
onClick={handleZoomOut}
|
||||
disabled={zoomOutDisabled}
|
||||
data-testid="zoom-out-btn"
|
||||
prefix={<ZoomOut size={14} />}
|
||||
testId="zoom-out-btn"
|
||||
icon
|
||||
aria-label="Zoom out"
|
||||
variant="solid"
|
||||
color="none"
|
||||
/>
|
||||
color="secondary"
|
||||
>
|
||||
<ZoomOut size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
@@ -682,4 +686,5 @@ CustomTimePicker.defaultProps = {
|
||||
onExitLiveLogs: noop,
|
||||
showLiveLogs: false,
|
||||
showRecentlyUsed: true,
|
||||
'data-testid': undefined,
|
||||
};
|
||||
|
||||
@@ -180,6 +180,7 @@ 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);
|
||||
@@ -259,6 +260,7 @@ 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();
|
||||
|
||||
@@ -164,6 +164,6 @@ describe('CustomTimePicker - zoom out button', () => {
|
||||
);
|
||||
|
||||
const zoomOutBtn = screen.getByTestId('zoom-out-btn');
|
||||
expect(zoomOutBtn).toBeDisabled();
|
||||
expect(zoomOutBtn).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -27,12 +27,14 @@ function DetailsHeader({
|
||||
const closeButton = (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
color="secondary"
|
||||
onClick={onClose}
|
||||
aria-label="Close"
|
||||
prefix={<X size={14} />}
|
||||
></Button>
|
||||
>
|
||||
<X size={14} />
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -42,12 +42,5 @@
|
||||
height: 1px;
|
||||
background: var(--l1-border);
|
||||
}
|
||||
|
||||
.export-button {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -308,13 +308,16 @@ describe.each([
|
||||
|
||||
fireEvent.click(screen.getByText('Export'));
|
||||
|
||||
expect(screen.getByTestId(testId)).toBeDisabled();
|
||||
expect(screen.getByTestId(testId)).toHaveAttribute('aria-disabled', 'true');
|
||||
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
||||
|
||||
resolveDownload!();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId(testId)).not.toBeDisabled();
|
||||
expect(screen.getByTestId(testId)).not.toHaveAttribute(
|
||||
'aria-disabled',
|
||||
'true',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { Popover, Tooltip } from 'antd';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
|
||||
import { RadioGroup } from '@signozhq/ui/radio-group';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { TelemetryFieldKey } from 'api/v5/v5';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
@@ -69,10 +69,15 @@ export default function DownloadOptionsMenu({
|
||||
>
|
||||
<div className="export-format">
|
||||
<Typography.Text className="title">FORMAT</Typography.Text>
|
||||
<RadioGroup value={exportFormat} onChange={setExportFormat}>
|
||||
<RadioGroupItem value={DownloadFormats.CSV}>csv</RadioGroupItem>
|
||||
<RadioGroupItem value={DownloadFormats.JSONL}>jsonl</RadioGroupItem>
|
||||
</RadioGroup>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={exportFormat}
|
||||
onChange={setExportFormat}
|
||||
items={[
|
||||
{ value: DownloadFormats.CSV, label: 'csv' },
|
||||
{ value: DownloadFormats.JSONL, label: 'jsonl' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="horizontal-line" />
|
||||
@@ -80,19 +85,15 @@ 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))}
|
||||
>
|
||||
<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>
|
||||
items={[
|
||||
{ value: String(DownloadRowCounts.TEN_K), label: '10k' },
|
||||
{ value: String(DownloadRowCounts.THIRTY_K), label: '30k' },
|
||||
{ value: String(DownloadRowCounts.FIFTY_K), label: '50k' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{dataSource !== DataSource.TRACES && (
|
||||
@@ -101,12 +102,15 @@ export default function DownloadOptionsMenu({
|
||||
|
||||
<div className="columns-scope">
|
||||
<Typography.Text className="title">Columns</Typography.Text>
|
||||
<RadioGroup value={columnsScope} onChange={setColumnsScope}>
|
||||
<RadioGroupItem value={DownloadColumnsScopes.ALL}>All</RadioGroupItem>
|
||||
<RadioGroupItem value={DownloadColumnsScopes.SELECTED}>
|
||||
Selected
|
||||
</RadioGroupItem>
|
||||
</RadioGroup>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={columnsScope}
|
||||
onChange={setColumnsScope}
|
||||
items={[
|
||||
{ value: DownloadColumnsScopes.ALL, label: 'All' },
|
||||
{ value: DownloadColumnsScopes.SELECTED, label: 'Selected' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
@@ -115,9 +119,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
|
||||
@@ -148,13 +152,14 @@ export default function DownloadOptionsMenu({
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
prefix={<Download size={14} />}
|
||||
size="sm"
|
||||
icon
|
||||
aria-label="Download"
|
||||
data-testid={`periscope-btn-download-${dataSource}`}
|
||||
disabled={isDownloading}
|
||||
testId={`periscope-btn-download-${dataSource}`}
|
||||
loading={isDownloading}
|
||||
/>
|
||||
>
|
||||
<Download size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
@@ -38,18 +38,23 @@ function DeleteMemberDialog({
|
||||
|
||||
const footer = (
|
||||
<>
|
||||
<Button variant="solid" color="secondary" onClick={onClose}>
|
||||
<X size={12} />
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="secondary"
|
||||
onClick={onClose}
|
||||
prefix={<X size={12} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="destructive"
|
||||
disabled={isDeleting}
|
||||
color="danger"
|
||||
onClick={onConfirm}
|
||||
loading={isDeleting}
|
||||
prefix={<Trash2 size={12} />}
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
{isDeleting ? 'Processing...' : title}
|
||||
</Button>
|
||||
</>
|
||||
|
||||
@@ -90,7 +90,6 @@
|
||||
font-weight: var(--uppercase-small-500-font-weight);
|
||||
line-height: 100%;
|
||||
letter-spacing: 0.44px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +112,7 @@
|
||||
&__footer-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--spacing-4);
|
||||
}
|
||||
|
||||
&__footer-right {
|
||||
@@ -128,10 +128,6 @@
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__tooltip-wrapper {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
&__footer-btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -200,6 +196,7 @@
|
||||
background: var(--l2-background);
|
||||
border: 1px solid var(--l1-border);
|
||||
border-radius: 2px;
|
||||
padding-right: var(--spacing-4);
|
||||
}
|
||||
|
||||
&__link-text-wrap {
|
||||
@@ -220,8 +217,4 @@
|
||||
line-height: var(--line-height-18);
|
||||
letter-spacing: -0.07px;
|
||||
}
|
||||
|
||||
&__copy-btn {
|
||||
border-left: 1px solid var(--l1-border);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +519,12 @@ function EditMemberDrawer({
|
||||
localRoles.map((roleId) => {
|
||||
const role = availableRoles.find((r) => r.id === roleId);
|
||||
return (
|
||||
<Badge key={roleId} color="vanilla">
|
||||
<Badge
|
||||
textTransform="none"
|
||||
variant="solid"
|
||||
key={roleId}
|
||||
color="secondary"
|
||||
>
|
||||
{role?.name ?? roleId}
|
||||
</Badge>
|
||||
);
|
||||
@@ -559,15 +564,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="forest" variant="outline">
|
||||
<Badge color="success" variant="outlined">
|
||||
ACTIVE
|
||||
</Badge>
|
||||
) : member?.status === MemberStatus.Deleted ? (
|
||||
<Badge color="cherry" variant="outline">
|
||||
<Badge color="danger" variant="outlined">
|
||||
DELETED
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge color="amber" variant="outline">
|
||||
<Badge color="warning" variant="outlined">
|
||||
INVITED
|
||||
</Badge>
|
||||
)}
|
||||
@@ -575,12 +580,16 @@ function EditMemberDrawer({
|
||||
|
||||
<div className="edit-member-drawer__meta-item">
|
||||
<span className="edit-member-drawer__meta-label">{joinedOnLabel}</span>
|
||||
<Badge color="vanilla">{formatTimestamp(member?.joinedOn)}</Badge>
|
||||
<Badge textTransform="none" variant="solid" color="secondary">
|
||||
{formatTimestamp(member?.joinedOn)}
|
||||
</Badge>
|
||||
</div>
|
||||
{!isInvited && (
|
||||
<div className="edit-member-drawer__meta-item">
|
||||
<span className="edit-member-drawer__meta-label">Last Modified</span>
|
||||
<Badge color="vanilla">{formatTimestamp(member?.updatedAt)}</Badge>
|
||||
<Badge textTransform="none" variant="solid" color="secondary">
|
||||
{formatTimestamp(member?.updatedAt)}
|
||||
</Badge>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -611,55 +620,59 @@ function EditMemberDrawer({
|
||||
{!isDeleted && (
|
||||
<>
|
||||
<div className="edit-member-drawer__footer-left">
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<div className="edit-member-drawer__footer-divider" />
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<div className="edit-member-drawer__footer-right">
|
||||
<Button variant="outlined" color="secondary" onClick={handleClose}>
|
||||
<X size={14} />
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={handleClose}
|
||||
prefix={<X size={14} />}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
disabledTooltip={isRootUser ? ROOT_USER_TOOLTIP : 'No changes to save'}
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
disabled={!isDirty || isSaving || isRootUser}
|
||||
disabled={!isDirty || isRootUser}
|
||||
onClick={handleSave}
|
||||
loading={isSaving}
|
||||
>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -251,7 +251,7 @@ describe('EditMemberDrawer', () => {
|
||||
expect(screen.getByText('ACTIVE')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('button', { name: /save member details/i }),
|
||||
).toBeDisabled();
|
||||
).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
|
||||
it('enables Save after editing name and calls updateUser on confirm', async () => {
|
||||
@@ -271,7 +271,9 @@ describe('EditMemberDrawer', () => {
|
||||
await user.type(nameInput, 'Alice Updated');
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /save member details/i });
|
||||
await waitFor(() => expect(saveBtn).not.toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
|
||||
await user.click(saveBtn);
|
||||
|
||||
@@ -295,7 +297,9 @@ describe('EditMemberDrawer', () => {
|
||||
await user.type(nameInput, 'Alice Updated');
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /save member details/i });
|
||||
await waitFor(() => expect(saveBtn).not.toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
await user.click(saveBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -323,7 +327,9 @@ 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.toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
await user.click(saveBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -349,7 +355,9 @@ describe('EditMemberDrawer', () => {
|
||||
await user.click(removeBtn);
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /save member details/i });
|
||||
await waitFor(() => expect(saveBtn).not.toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
await user.click(saveBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -505,7 +513,9 @@ describe('EditMemberDrawer', () => {
|
||||
await user.type(nameInput, 'Bob Updated');
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /save member details/i });
|
||||
await waitFor(() => expect(saveBtn).not.toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
await user.click(saveBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -541,7 +551,9 @@ describe('EditMemberDrawer', () => {
|
||||
await user.type(nameInput, 'Alice Updated');
|
||||
|
||||
const saveBtn = screen.getByRole('button', { name: /save member details/i });
|
||||
await waitFor(() => expect(saveBtn).not.toBeDisabled());
|
||||
await waitFor(() =>
|
||||
expect(saveBtn).not.toHaveAttribute('aria-disabled', 'true'),
|
||||
);
|
||||
await user.click(saveBtn);
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -619,7 +631,7 @@ describe('EditMemberDrawer', () => {
|
||||
renderDrawer({ member: selfMember });
|
||||
expect(
|
||||
screen.getByRole('button', { name: /delete member/i }),
|
||||
).toBeDisabled();
|
||||
).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
|
||||
it('does not open delete confirm dialog when Delete is clicked while disabled (isSelf)', async () => {
|
||||
@@ -642,7 +654,7 @@ describe('EditMemberDrawer', () => {
|
||||
renderDrawer({ member: selfMember });
|
||||
expect(
|
||||
screen.getByRole('button', { name: /generate password reset link/i }),
|
||||
).not.toBeDisabled();
|
||||
).not.toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -664,21 +676,21 @@ describe('EditMemberDrawer', () => {
|
||||
renderDrawer();
|
||||
expect(
|
||||
screen.getByRole('button', { name: /delete member/i }),
|
||||
).toBeDisabled();
|
||||
).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
|
||||
it('disables Reset Link button for root user', () => {
|
||||
renderDrawer();
|
||||
expect(
|
||||
screen.getByRole('button', { name: /generate password reset link/i }),
|
||||
).toBeDisabled();
|
||||
).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
|
||||
it('disables Save button for root user', () => {
|
||||
renderDrawer();
|
||||
expect(
|
||||
screen.getByRole('button', { name: /save member details/i }),
|
||||
).toBeDisabled();
|
||||
).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
|
||||
it('does not open delete confirm dialog when Delete is clicked while disabled (root)', async () => {
|
||||
|
||||
@@ -53,7 +53,7 @@ function ErrorModal({
|
||||
onClick={(): void => setVisible(true)}
|
||||
onKeyDown={undefined}
|
||||
>
|
||||
<Badge color="error">
|
||||
<Badge variant="solid" color="danger">
|
||||
<CircleAlert size={14} color={Color.BG_CHERRY_500} /> error
|
||||
</Badge>
|
||||
</span>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { Button, Col, Popover, Row, Select, Space } from 'antd';
|
||||
import { DropdownMenuSimple, type MenuProps } from '@signozhq/ui/dropdown-menu';
|
||||
import { Dropdown, type DropdownItemType } from '@signozhq/ui/dropdown';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import axios from 'axios';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
@@ -137,16 +137,15 @@ function ExplorerCard({
|
||||
);
|
||||
};
|
||||
|
||||
const moreOptionMenu: MenuProps = {
|
||||
items: [
|
||||
{
|
||||
key: 'delete',
|
||||
label: <Typography.Text strong>Delete</Typography.Text>,
|
||||
onClick: onDeleteHandler,
|
||||
icon: <Trash2 size="md" />,
|
||||
},
|
||||
],
|
||||
};
|
||||
const moreOptionItems: DropdownItemType[] = [
|
||||
{
|
||||
type: 'item',
|
||||
value: 'delete',
|
||||
label: <Typography.Text strong>Delete</Typography.Text>,
|
||||
onClick: onDeleteHandler,
|
||||
prefix: <Trash2 size="md" />,
|
||||
},
|
||||
];
|
||||
|
||||
const saveButtonType = isQueryUpdated ? 'default' : 'primary';
|
||||
const saveButtonIcon = isQueryUpdated ? null : <Save size="md" />;
|
||||
@@ -230,9 +229,14 @@ function ExplorerCard({
|
||||
</Popover>
|
||||
<Share2 onClick={onCopyUrlHandler} size="md" />
|
||||
{viewKey && (
|
||||
<DropdownMenuSimple menu={moreOptionMenu}>
|
||||
<Dropdown
|
||||
items={moreOptionItems}
|
||||
nativeButton
|
||||
align="end"
|
||||
side="bottom"
|
||||
>
|
||||
<Button type="text" size="small" icon={<Ellipsis size="md" />} />
|
||||
</DropdownMenuSimple>
|
||||
</Dropdown>
|
||||
)}
|
||||
</Space>
|
||||
</OffSetCol>
|
||||
|
||||
@@ -26,8 +26,4 @@
|
||||
font-size: var(--periscope-font-size-base);
|
||||
}
|
||||
}
|
||||
|
||||
.export-button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, RadioGroupItem } from '@signozhq/ui/radio-group';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { RadioGroup } from '@signozhq/ui/radio-group';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import {
|
||||
ClientExportData,
|
||||
@@ -51,36 +51,41 @@ export default function ExportMenu({
|
||||
|
||||
return (
|
||||
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen}>
|
||||
<TooltipSimple title="Download">
|
||||
<Tooltip title="Download">
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
aria-label="Download"
|
||||
data-testid={`export-menu-${dataSource}`}
|
||||
disabled={isExporting}
|
||||
testId={`export-menu-${dataSource}`}
|
||||
loading={isExporting}
|
||||
>
|
||||
<Download size={14} />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
<PopoverContent align="end" className="export-menu-popover">
|
||||
<div className="export-format">
|
||||
<Typography.Text className="title">FORMAT</Typography.Text>
|
||||
<RadioGroup value={exportFormat} onChange={setExportFormat}>
|
||||
<RadioGroupItem value={ExportFormat.Csv}>csv</RadioGroupItem>
|
||||
<RadioGroupItem value={ExportFormat.Jsonl}>jsonl</RadioGroupItem>
|
||||
</RadioGroup>
|
||||
<RadioGroup
|
||||
color="primary"
|
||||
value={exportFormat}
|
||||
onChange={setExportFormat}
|
||||
items={[
|
||||
{ value: ExportFormat.Csv, label: 'csv' },
|
||||
{ value: ExportFormat.Jsonl, label: 'jsonl' },
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
className="export-button"
|
||||
width="100%"
|
||||
onClick={handleExport}
|
||||
disabled={isExporting}
|
||||
loading={isExporting}
|
||||
prefix={<Download size={16} />}
|
||||
>
|
||||
|
||||
@@ -80,6 +80,6 @@ describe('ExportMenu', () => {
|
||||
mockIsExporting = true;
|
||||
renderMenu();
|
||||
|
||||
expect(screen.getByTestId(TEST_ID)).toBeDisabled();
|
||||
expect(screen.getByTestId(TEST_ID)).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,9 +57,8 @@ function SortableField({
|
||||
</div>
|
||||
{!isRequired && (
|
||||
<Button
|
||||
className={cx(styles.removeBtn, 'periscope-btn')}
|
||||
variant="outlined"
|
||||
color="destructive"
|
||||
variant="solid"
|
||||
color="danger"
|
||||
size="sm"
|
||||
onClick={(): void => onRemove(field)}
|
||||
>
|
||||
|
||||
@@ -98,11 +98,15 @@
|
||||
user-select: none;
|
||||
font-size: 13px;
|
||||
|
||||
> [data-slot='button'] {
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--l2-background);
|
||||
|
||||
.removeBtn,
|
||||
.addBtn {
|
||||
> [data-slot='button'] {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
@@ -137,14 +141,6 @@
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.removeBtn,
|
||||
.addBtn {
|
||||
padding: 4px 10px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease-in-out;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
@@ -173,6 +173,7 @@ function FieldsSelectorContent({
|
||||
{hasUnsavedChanges && (
|
||||
<div className={styles.footer}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
onClick={handleDiscard}
|
||||
@@ -181,6 +182,7 @@ function FieldsSelectorContent({
|
||||
Discard
|
||||
</Button>
|
||||
<Button
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="primary"
|
||||
onClick={handleSave}
|
||||
|
||||
@@ -138,7 +138,6 @@ function OtherFields({
|
||||
<span className={styles.fieldKey}>{attr.name}</span>
|
||||
{!isAtLimit && (
|
||||
<Button
|
||||
className={cx(styles.addBtn, 'periscope-btn')}
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
|
||||
@@ -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 { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
import { handleContactSupport } from 'container/Integrations/utils';
|
||||
@@ -102,10 +102,12 @@ function FeedbackModal({ onClose }: { onClose: () => void }): JSX.Element {
|
||||
return (
|
||||
<div className="feedback-modal-container">
|
||||
<div className="feedback-modal-header">
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
type="single"
|
||||
value={activeTab}
|
||||
className="feedback-modal-tabs"
|
||||
onChange={setActiveTab}
|
||||
items={items}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
.header-right-section-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
gap: var(--spacing-8);
|
||||
padding-right: var(--spacing-8);
|
||||
}
|
||||
|
||||
.header-ai-assistant-btn-container {
|
||||
@@ -121,78 +122,26 @@
|
||||
}
|
||||
|
||||
.feedback-modal-container {
|
||||
.feedback-modal-tabs {
|
||||
width: 100%;
|
||||
.feedback-modal-tab-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.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);
|
||||
}
|
||||
.tab-icon {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.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);
|
||||
}
|
||||
.feedback-tab {
|
||||
background-color: var(--danger-background);
|
||||
}
|
||||
|
||||
.ant-tabs-nav-list {
|
||||
.ant-tabs-tab {
|
||||
padding: 6px 16px;
|
||||
.bug-tab {
|
||||
background-color: var(--warning-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);
|
||||
}
|
||||
}
|
||||
}
|
||||
.feature-tab {
|
||||
background-color: var(--primary-background);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import Noz from 'components/Noz/Noz';
|
||||
import { NOZ_TOOLTIP_TITLE } from 'components/Noz/Noz.constants';
|
||||
import { Popover } from 'antd';
|
||||
@@ -113,24 +113,26 @@ function HeaderRightSection({
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -147,13 +149,15 @@ function HeaderRightSection({
|
||||
onOpenChange={handleOpenFeedbackModalChange}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="share-feedback-btn"
|
||||
color="primary"
|
||||
variant="link"
|
||||
size="md"
|
||||
icon
|
||||
aria-label="Feedback"
|
||||
prefix={<SquarePen size={14} />}
|
||||
onClick={handleOpenFeedbackModal}
|
||||
/>
|
||||
>
|
||||
<SquarePen size={14} />
|
||||
</Button>
|
||||
</Popover>
|
||||
)}
|
||||
|
||||
@@ -170,16 +174,19 @@ function HeaderRightSection({
|
||||
onOpenChange={handleOpenAnnouncementsModalChange}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
color="primary"
|
||||
variant="link"
|
||||
size="md"
|
||||
icon
|
||||
aria-label="Announcements"
|
||||
prefix={<Inbox size={14} />}
|
||||
onClick={(): void => {
|
||||
logEvent('Announcements: Clicked', {
|
||||
page: location.pathname,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<Inbox size={14} />
|
||||
</Button>
|
||||
</Popover>
|
||||
)}
|
||||
|
||||
@@ -196,12 +203,15 @@ function HeaderRightSection({
|
||||
onOpenChange={handleOpenShareURLModalChange}
|
||||
>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
color="primary"
|
||||
variant="link"
|
||||
size="md"
|
||||
icon
|
||||
aria-label="Share"
|
||||
prefix={<Globe size={14} />}
|
||||
onClick={handleOpenShareURLModal}
|
||||
/>
|
||||
>
|
||||
<Globe size={14} />
|
||||
</Button>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -149,6 +149,9 @@ 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 => {
|
||||
@@ -173,6 +176,8 @@ 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)}
|
||||
/>
|
||||
|
||||
@@ -69,23 +69,23 @@ describe('FeedbackModal', () => {
|
||||
const user = userEvent.setup();
|
||||
render(<FeedbackModal onClose={mockOnClose} />);
|
||||
|
||||
// Initially, feedback radio should be active
|
||||
const feedbackRadio = screen.getByRole('radio', { name: 'Feedback' });
|
||||
expect(feedbackRadio).toBeChecked();
|
||||
// Initially, feedback button should be active
|
||||
const feedbackButton = screen.getByRole('button', { name: 'Feedback' });
|
||||
expect(feedbackButton).toHaveAttribute('aria-pressed', 'true');
|
||||
|
||||
const bugTab = screen.getByText('Report a bug');
|
||||
await user.click(bugTab);
|
||||
|
||||
// Bug radio should now be active
|
||||
const bugRadio = screen.getByRole('radio', { name: 'Report a bug' });
|
||||
expect(bugRadio).toBeChecked();
|
||||
// Bug button should now be active
|
||||
const bugButton = screen.getByRole('button', { name: 'Report a bug' });
|
||||
expect(bugButton).toHaveAttribute('aria-pressed', 'true');
|
||||
|
||||
const featureTab = screen.getByText('Feature request');
|
||||
await user.click(featureTab);
|
||||
|
||||
// Feature radio should now be active
|
||||
const featureRadio = screen.getByRole('radio', { name: 'Feature request' });
|
||||
expect(featureRadio).toBeChecked();
|
||||
// Feature button should now be active
|
||||
const featureButton = screen.getByRole('button', { name: 'Feature request' });
|
||||
expect(featureButton).toHaveAttribute('aria-pressed', 'true');
|
||||
});
|
||||
|
||||
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 radio is now active
|
||||
const bugRadio = screen.getByRole('radio', { name: 'Report a bug' });
|
||||
expect(bugRadio).toBeChecked();
|
||||
// Verify bug report button is now active
|
||||
const bugButton = screen.getByRole('button', { name: 'Report a bug' });
|
||||
expect(bugButton).toHaveAttribute('aria-pressed', 'true');
|
||||
|
||||
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 radio is now active
|
||||
const featureRadio = screen.getByRole('radio', { name: 'Feature request' });
|
||||
expect(featureRadio).toBeChecked();
|
||||
// Verify feature request button is now active
|
||||
const featureButton = screen.getByRole('button', { name: 'Feature request' });
|
||||
expect(featureButton).toHaveAttribute('aria-pressed', 'true');
|
||||
|
||||
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 radio is reset to default (Feedback radio)
|
||||
const feedbackRadio = screen.getByRole('radio', { name: 'Feedback' });
|
||||
expect(feedbackRadio).toBeChecked();
|
||||
// Verify active button is reset to default (Feedback button)
|
||||
const feedbackButton = screen.getByRole('button', { name: 'Feedback' });
|
||||
expect(feedbackButton).toHaveAttribute('aria-pressed', 'true');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -176,7 +176,7 @@ describe('ShareURLModal', () => {
|
||||
expect(
|
||||
screen.getByText('Please select / enter valid relative time to toggle.'),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByRole('switch')).toBeDisabled();
|
||||
expect(screen.getByRole('switch')).toHaveAttribute('aria-disabled', 'true');
|
||||
});
|
||||
|
||||
it('should process URL with absolute time for non-custom time', async () => {
|
||||
|
||||
@@ -1,44 +1,32 @@
|
||||
import { Badge } from '@signozhq/ui/badge';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
|
||||
type BadgeColor =
|
||||
| 'vanilla'
|
||||
| 'robin'
|
||||
| 'forest'
|
||||
| 'amber'
|
||||
| 'sienna'
|
||||
| 'cherry'
|
||||
| 'sakura'
|
||||
| 'aqua';
|
||||
function getStatusCodeColor(statusCode: number): BadgeColorType {
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
return 'success';
|
||||
}
|
||||
if (statusCode >= 300 && statusCode < 400) {
|
||||
return 'primary';
|
||||
}
|
||||
if (statusCode >= 400 && statusCode < 500) {
|
||||
return 'warning';
|
||||
}
|
||||
if (statusCode >= 500) {
|
||||
return 'danger';
|
||||
}
|
||||
if (statusCode >= 100 && statusCode < 200) {
|
||||
return 'secondary';
|
||||
}
|
||||
return 'primary';
|
||||
}
|
||||
|
||||
interface HttpStatusBadgeProps {
|
||||
statusCode: string | number;
|
||||
testId?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function getStatusCodeColor(statusCode: number): BadgeColor {
|
||||
if (statusCode >= 200 && statusCode < 300) {
|
||||
return 'forest'; // Success - green
|
||||
}
|
||||
if (statusCode >= 300 && statusCode < 400) {
|
||||
return 'robin'; // Redirect - blue
|
||||
}
|
||||
if (statusCode >= 400 && statusCode < 500) {
|
||||
return 'amber'; // Client error - amber
|
||||
}
|
||||
if (statusCode >= 500) {
|
||||
return 'cherry'; // Server error - red
|
||||
}
|
||||
if (statusCode >= 100 && statusCode < 200) {
|
||||
return 'vanilla'; // Informational - neutral
|
||||
}
|
||||
return 'robin'; // Default fallback
|
||||
}
|
||||
|
||||
function HttpStatusBadge({
|
||||
statusCode,
|
||||
testId,
|
||||
className,
|
||||
}: HttpStatusBadgeProps): JSX.Element | null {
|
||||
const numericStatusCode = Number(statusCode);
|
||||
|
||||
@@ -49,12 +37,7 @@ function HttpStatusBadge({
|
||||
const color = getStatusCodeColor(numericStatusCode);
|
||||
|
||||
return (
|
||||
<Badge
|
||||
color={color}
|
||||
variant="outline"
|
||||
data-testid={testId}
|
||||
className={className}
|
||||
>
|
||||
<Badge color={color} variant="outlined" testId={testId}>
|
||||
{statusCode}
|
||||
</Badge>
|
||||
);
|
||||
|
||||
@@ -119,11 +119,13 @@ function InviteMembers({
|
||||
<div className={styles.cellAction}>
|
||||
{canRemoveRow && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
color="destructive"
|
||||
size="md"
|
||||
variant="solid"
|
||||
color="danger"
|
||||
onClick={(): void => removeRow(row.id)}
|
||||
aria-label="Remove row"
|
||||
data-testid={`invite-remove-${row.id}`}
|
||||
testId={`invite-remove-${row.id}`}
|
||||
icon
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
</Button>
|
||||
@@ -136,11 +138,12 @@ function InviteMembers({
|
||||
{showAddButton && (
|
||||
<div className={styles.addRow}>
|
||||
<Button
|
||||
size="md"
|
||||
variant="dashed"
|
||||
color="secondary"
|
||||
prefix={<Plus size={12} />}
|
||||
onClick={addRow}
|
||||
data-testid="invite-add-row"
|
||||
testId="invite-add-row"
|
||||
>
|
||||
Add another
|
||||
</Button>
|
||||
|
||||
@@ -161,37 +161,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.views-tabs {
|
||||
color: var(--l2-foreground);
|
||||
|
||||
.view-title {
|
||||
display: flex;
|
||||
gap: var(--margin-2);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--font-size-xs);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-normal);
|
||||
}
|
||||
|
||||
> button {
|
||||
border: 1px solid var(--l1-border);
|
||||
width: 114px;
|
||||
|
||||
&::before {
|
||||
background: var(--l1-border);
|
||||
}
|
||||
|
||||
&[data-state='on'] {
|
||||
background: var(--l3-background);
|
||||
color: var(--l1-foreground);
|
||||
border: 1px solid var(--l1-border);
|
||||
|
||||
&::before {
|
||||
background: var(--l1-border);
|
||||
}
|
||||
}
|
||||
}
|
||||
.view-title {
|
||||
display: flex;
|
||||
gap: var(--margin-2);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: var(--font-size-xs);
|
||||
font-style: normal;
|
||||
font-weight: var(--font-weight-normal);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
@@ -242,42 +219,4 @@
|
||||
align-items: center;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.log-arrow-btn {
|
||||
padding: 0;
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 4px;
|
||||
background: var(--l2-background);
|
||||
color: var(--l2-foreground);
|
||||
border: 1px solid var(--l1-border);
|
||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.log-arrow-btn-up,
|
||||
.log-arrow-btn-down {
|
||||
background: var(--l2-background);
|
||||
}
|
||||
|
||||
.log-arrow-btn:active,
|
||||
.log-arrow-btn:focus {
|
||||
background: var(--l3-background);
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
|
||||
.log-arrow-btn[disabled] {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
background: var(--l1-background);
|
||||
color: var(--l3-foreground);
|
||||
|
||||
.log-arrow-btn:hover:not([disabled]) {
|
||||
background: var(--l3-background);
|
||||
color: var(--l1-foreground);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,6 @@
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.tooltipContent {
|
||||
--tooltip-z-index: 2100;
|
||||
}
|
||||
|
||||
.dropdownContent {
|
||||
--dropdown-menu-content-z-index: 2100;
|
||||
}
|
||||
|
||||
.leftSection {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Divider } from '@signozhq/ui/divider';
|
||||
import { DropdownMenuSimple as Dropdown } from '@signozhq/ui/dropdown-menu';
|
||||
import { Dropdown, type DropdownItemType } from '@signozhq/ui/dropdown';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import { aggregateAttributesResourcesToString } from 'container/LogDetailedView/utils';
|
||||
import { toast } from '@signozhq/ui/sonner';
|
||||
@@ -23,8 +23,6 @@ import { useCopyToClipboard } from 'react-use';
|
||||
|
||||
import styles from './LogDetailsHeader.module.scss';
|
||||
|
||||
const TOOLTIP_CONTENT_PROPS = { className: styles.tooltipContent };
|
||||
|
||||
interface LogDetailsHeaderProps {
|
||||
log: ILog;
|
||||
onNavigatePrev: () => void;
|
||||
@@ -53,17 +51,19 @@ function LogDetailsHeader({
|
||||
toast.success('Copied to clipboard', { position: 'bottom-right' });
|
||||
};
|
||||
|
||||
const menuItems = [
|
||||
const menuItems: DropdownItemType[] = [
|
||||
{
|
||||
key: 'copy-log',
|
||||
type: 'item',
|
||||
value: 'copy-log',
|
||||
label: 'Copy log',
|
||||
icon: <Copy size={14} />,
|
||||
prefix: <Copy size={14} />,
|
||||
onClick: handleCopyLog,
|
||||
},
|
||||
{
|
||||
key: 'copy-link',
|
||||
type: 'item',
|
||||
value: 'copy-link',
|
||||
label: 'Copy link to log',
|
||||
icon: <Link size={14} />,
|
||||
prefix: <Link size={14} />,
|
||||
onClick: (): void => onLogCopy(),
|
||||
},
|
||||
];
|
||||
@@ -91,6 +91,7 @@ function LogDetailsHeader({
|
||||
<div className={styles.actions}>
|
||||
{showOpenInExplorer && (
|
||||
<Button
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<Compass size={16} />}
|
||||
@@ -100,51 +101,57 @@ function LogDetailsHeader({
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Dropdown
|
||||
menu={{ items: menuItems }}
|
||||
align="end"
|
||||
className={styles.dropdownContent}
|
||||
onClick={(e: MouseEvent): void => e.stopPropagation()}
|
||||
>
|
||||
<Dropdown items={menuItems} nativeButton align="end" side="bottom">
|
||||
<Button
|
||||
size="md"
|
||||
variant="link"
|
||||
color="secondary"
|
||||
prefix={<Ellipsis size={16} />}
|
||||
data-testid="log-details-header-menu"
|
||||
/>
|
||||
icon
|
||||
aria-label="Log actions"
|
||||
testId="log-details-header-menu"
|
||||
onClick={(e: MouseEvent): void => e.stopPropagation()}
|
||||
>
|
||||
<Ellipsis size={16} />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
|
||||
<div className={styles.arrows}>
|
||||
<TooltipSimple
|
||||
title="Move to previous log"
|
||||
<Tooltip
|
||||
title={isPrevDisabled ? undefined : 'Move to previous log'}
|
||||
side="top"
|
||||
open={isPrevDisabled ? false : undefined}
|
||||
tooltipContentProps={TOOLTIP_CONTENT_PROPS}
|
||||
>
|
||||
<Button
|
||||
disabledTooltip="No previous log"
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<ChevronUp size={14} />}
|
||||
icon
|
||||
aria-label="Move to previous log"
|
||||
disabled={isPrevDisabled}
|
||||
onClick={onNavigatePrev}
|
||||
data-testid="log-details-header-prev"
|
||||
/>
|
||||
</TooltipSimple>
|
||||
<TooltipSimple
|
||||
title="Move to next log"
|
||||
testId="log-details-header-prev"
|
||||
>
|
||||
<ChevronUp size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={isNextDisabled ? undefined : 'Move to next log'}
|
||||
side="top"
|
||||
open={isNextDisabled ? false : undefined}
|
||||
tooltipContentProps={TOOLTIP_CONTENT_PROPS}
|
||||
>
|
||||
<Button
|
||||
disabledTooltip="No next log"
|
||||
size="md"
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
prefix={<ChevronDown size={14} />}
|
||||
icon
|
||||
aria-label="Move to next log"
|
||||
disabled={isNextDisabled}
|
||||
onClick={onNavigateNext}
|
||||
data-testid="log-details-header-next"
|
||||
/>
|
||||
</TooltipSimple>
|
||||
testId="log-details-header-next"
|
||||
>
|
||||
<ChevronDown size={14} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,13 +12,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.valueBadge {
|
||||
--badge-font-size: 13px;
|
||||
box-sizing: border-box;
|
||||
max-width: 100%;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
// Truncating text inside a badge
|
||||
.badgeText {
|
||||
min-width: 0;
|
||||
@@ -29,11 +22,14 @@
|
||||
|
||||
.serviceDot {
|
||||
width: 6px;
|
||||
min-width: 6px;
|
||||
height: 6px;
|
||||
min-height: 6px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent-forest);
|
||||
flex-shrink: 0;
|
||||
margin-right: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.traceLink {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { Badge, BadgeColor } from '@signozhq/ui/badge';
|
||||
import { Badge, type BadgeColorType } from '@signozhq/ui/badge';
|
||||
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
|
||||
import { getLogIndicatorType } from 'components/Logs/LogStateIndicator/utils';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
@@ -8,13 +8,13 @@ import styles from './LogHighlights.module.scss';
|
||||
import TraceIdField from './TraceIdField';
|
||||
|
||||
// Severity badge color mirrors the LogStateIndicator bar
|
||||
const SEVERITY_COLOR: Record<string, BadgeColor> = {
|
||||
[LogType.TRACE]: 'forest',
|
||||
[LogType.DEBUG]: 'aqua',
|
||||
[LogType.INFO]: 'robin',
|
||||
[LogType.WARN]: 'amber',
|
||||
[LogType.ERROR]: 'cherry',
|
||||
[LogType.FATAL]: 'sakura',
|
||||
const SEVERITY_COLOR: Record<string, BadgeColorType> = {
|
||||
[LogType.TRACE]: 'success',
|
||||
[LogType.DEBUG]: 'info',
|
||||
[LogType.INFO]: 'primary',
|
||||
[LogType.WARN]: 'warning',
|
||||
[LogType.ERROR]: 'danger',
|
||||
[LogType.FATAL]: 'highlight-danger',
|
||||
};
|
||||
|
||||
export interface LogHighlightConfig {
|
||||
@@ -32,9 +32,13 @@ const getAttr = (log: ILog, key: string): string =>
|
||||
|
||||
const valueBadge = (
|
||||
value: string,
|
||||
options?: { prefix?: ReactNode; color?: BadgeColor },
|
||||
options?: { prefix?: ReactNode; color?: BadgeColorType },
|
||||
): ReactNode => (
|
||||
<Badge color={options?.color ?? 'vanilla'} className={styles.valueBadge}>
|
||||
<Badge
|
||||
variant="solid"
|
||||
color={options?.color ?? 'secondary'}
|
||||
textTransform="none"
|
||||
>
|
||||
{options?.prefix}
|
||||
<span className={styles.badgeText} title={value}>
|
||||
{value}
|
||||
|
||||
@@ -129,6 +129,18 @@ describe('LogDetail drawer — header', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps the drawer open when a ⋯ menu item is clicked', async () => {
|
||||
const user = userEvent.setup({ pointerEventsCheck: 0 });
|
||||
const onClose = jest.fn();
|
||||
|
||||
renderDrawer({ onClose });
|
||||
|
||||
await user.click(screen.getByTestId('log-details-header-menu'));
|
||||
await user.click(await screen.findByText('Copy log'));
|
||||
|
||||
expect(onClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('shows "Open in Explorer" when a handleOpenInExplorer handler is provided', () => {
|
||||
renderDrawer({ handleOpenInExplorer: jest.fn() });
|
||||
|
||||
@@ -217,8 +229,14 @@ describe('LogDetail drawer — header', () => {
|
||||
// Active log is the first one.
|
||||
renderDrawer({ log: logs[0], logs, onNavigateLog });
|
||||
|
||||
expect(screen.getByTestId('log-details-header-prev')).toBeDisabled();
|
||||
expect(screen.getByTestId('log-details-header-next')).toBeEnabled();
|
||||
expect(screen.getByTestId('log-details-header-prev')).toHaveAttribute(
|
||||
'aria-disabled',
|
||||
'true',
|
||||
);
|
||||
expect(screen.getByTestId('log-details-header-next')).not.toHaveAttribute(
|
||||
'aria-disabled',
|
||||
'true',
|
||||
);
|
||||
|
||||
await user.click(screen.getByTestId('log-details-header-next'));
|
||||
expect(onNavigateLog).toHaveBeenLastCalledWith(logs[1]);
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { Color, Spacing } from '@signozhq/design-tokens';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { Drawer, Tooltip } from 'antd';
|
||||
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
|
||||
import { ToggleGroup } from '@signozhq/ui/toggle-group';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
import LogStateIndicator from 'components/Logs/LogStateIndicator/LogStateIndicator';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
@@ -87,7 +87,8 @@ function LogDetailInner({
|
||||
target.closest('.cm-tooltip-autocomplete') ||
|
||||
target.closest('.drawer-popover') ||
|
||||
target.closest('.query-status-popover') ||
|
||||
target.closest('[data-radix-popper-content-wrapper]')
|
||||
target.closest('[data-radix-popper-content-wrapper]') ||
|
||||
target.closest('[data-base-ui-portal]')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -317,9 +318,12 @@ function LogDetailInner({
|
||||
<div className="log-detail-drawer__section-divider" />
|
||||
|
||||
<div className="tabs-and-search">
|
||||
<ToggleGroupSimple
|
||||
<ToggleGroup
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
type="single"
|
||||
className="views-tabs"
|
||||
testId="log-detail-views-tabs"
|
||||
onChange={handleModeChange}
|
||||
value={selectedView}
|
||||
items={[
|
||||
@@ -365,9 +369,12 @@ function LogDetailInner({
|
||||
variant="link"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
prefix={<Filter size="lg" />}
|
||||
icon
|
||||
aria-label="Show Filters"
|
||||
onClick={handleFilterVisible}
|
||||
/>
|
||||
>
|
||||
<Filter size="lg" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ReactElement, ReactNode } from 'react';
|
||||
import {
|
||||
Bold,
|
||||
CodeXml,
|
||||
@@ -11,16 +11,17 @@ import {
|
||||
Type,
|
||||
} from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import { Typography } from '@signozhq/ui/typography';
|
||||
|
||||
import { READ_ONLY_TOOLTIP } from './constants';
|
||||
import InsertVariableMenu from './InsertVariableMenu';
|
||||
import MarkdownHelp from './MarkdownHelp';
|
||||
import type { EditorCommand, EditorVariable } from './types';
|
||||
|
||||
import styles from './MarkdownEditor.module.scss';
|
||||
|
||||
const COMMAND_ICONS: Record<string, ReactNode> = {
|
||||
const COMMAND_ICONS: Record<string, ReactElement> = {
|
||||
heading: <Heading size={14} />,
|
||||
bold: <Bold size={14} />,
|
||||
italic: <Italic size={14} />,
|
||||
@@ -61,20 +62,22 @@ function EditorToolbar({
|
||||
<span className={styles.toolbarDivider} />
|
||||
<div className={styles.commands}>
|
||||
{commands.map((command) => (
|
||||
<TooltipSimple key={command.id} title={command.label}>
|
||||
<Tooltip key={command.id} title={disabled ? undefined : command.label}>
|
||||
<Button
|
||||
disabledTooltip={READ_ONLY_TOOLTIP}
|
||||
type="button"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
disabled={disabled}
|
||||
aria-label={command.label}
|
||||
data-testid={`markdown-command-${command.id}`}
|
||||
testId={`markdown-command-${command.id}`}
|
||||
onClick={(): void => onRunCommand(command)}
|
||||
>
|
||||
{COMMAND_ICONS[command.id]}
|
||||
</Button>
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.toolbarEnd}>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { ChevronDown, DollarSign, Search } from '@signozhq/icons';
|
||||
import { ChevronDown, DollarSign } from '@signozhq/icons';
|
||||
import { Button } from '@signozhq/ui/button';
|
||||
import { DropdownMenuSimple, type MenuItem } from '@signozhq/ui/dropdown-menu';
|
||||
import { Dropdown, type DropdownActionItemType } from '@signozhq/ui/dropdown';
|
||||
|
||||
import { READ_ONLY_TOOLTIP } from './constants';
|
||||
import type { EditorVariable } from './types';
|
||||
|
||||
import styles from './MarkdownEditor.module.scss';
|
||||
@@ -17,9 +18,10 @@ interface InsertVariableMenuProps {
|
||||
function toMenuItems(
|
||||
variables: EditorVariable[],
|
||||
onSelect: (name: string) => void,
|
||||
): MenuItem[] {
|
||||
): DropdownActionItemType[] {
|
||||
return variables.map((variable) => ({
|
||||
key: variable.name,
|
||||
type: 'item',
|
||||
value: variable.name,
|
||||
label: (
|
||||
<span
|
||||
className={styles.variableRow}
|
||||
@@ -60,15 +62,17 @@ function InsertVariableMenu({
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenuSimple
|
||||
className={styles.variableMenu}
|
||||
menu={{
|
||||
items,
|
||||
search: {
|
||||
placeholder: 'Search variables',
|
||||
searchIcon: <Search size={14} />,
|
||||
onSearchChange: setSearch,
|
||||
},
|
||||
<Dropdown
|
||||
items={items}
|
||||
nativeButton
|
||||
align="end"
|
||||
side="bottom"
|
||||
contentMaxWidth={320}
|
||||
disabled={disabled}
|
||||
disabledTooltip={READ_ONLY_TOOLTIP}
|
||||
searchInputProps={{
|
||||
placeholder: 'Search variables',
|
||||
onChange: setSearch,
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
@@ -76,15 +80,13 @@ function InsertVariableMenu({
|
||||
variant="outlined"
|
||||
color="secondary"
|
||||
size="sm"
|
||||
disabled={disabled}
|
||||
prefix={<DollarSign size={14} className={styles.insertVariableIcon} />}
|
||||
suffix={<ChevronDown size={14} />}
|
||||
className={styles.insertVariable}
|
||||
data-testid="markdown-insert-variable"
|
||||
testId="markdown-insert-variable"
|
||||
>
|
||||
Insert variable
|
||||
</Button>
|
||||
</DropdownMenuSimple>
|
||||
</Dropdown>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -74,27 +74,10 @@
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.insertVariable {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.insertVariableIcon {
|
||||
color: var(--text-amber-400);
|
||||
}
|
||||
|
||||
// The ui library's dropdown assumes a global border-box reset this app doesn't
|
||||
// have (`box-sizing` is set on `body` only and doesn't inherit): its items are
|
||||
// `width: 100%` + padding, so in the portal they lay out content-box and
|
||||
// overflow the popup by the padding — clipping the flush-right badge.
|
||||
.variableMenu,
|
||||
.variableMenu * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.variableMenu {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
// Shrinkable, so a clamped popup truncates the name instead of clipping the
|
||||
// badge at the content's `overflow: hidden` edge.
|
||||
.variableRow {
|
||||
|
||||
@@ -15,9 +15,10 @@ function MarkdownHelp(): JSX.Element {
|
||||
type="button"
|
||||
variant="ghost"
|
||||
color="secondary"
|
||||
size="icon"
|
||||
size="sm"
|
||||
icon
|
||||
aria-label="Markdown syntax help"
|
||||
data-testid="markdown-help-trigger"
|
||||
testId="markdown-help-trigger"
|
||||
>
|
||||
<CircleHelp size={14} />
|
||||
</Button>
|
||||
|
||||
@@ -256,8 +256,14 @@ describe('MarkdownEditor', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('markdown-command-bold')).toBeDisabled();
|
||||
expect(screen.getByTestId('markdown-insert-variable')).toBeDisabled();
|
||||
expect(screen.getByTestId('markdown-command-bold')).toHaveAttribute(
|
||||
'aria-disabled',
|
||||
'true',
|
||||
);
|
||||
expect(screen.getByTestId('markdown-insert-variable')).toHaveAttribute(
|
||||
'aria-disabled',
|
||||
'true',
|
||||
);
|
||||
});
|
||||
|
||||
it('hides the insert-variable control when none are available', () => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// The body is persisted inline in the dashboard JSON, so its length is capped.
|
||||
export const MARKDOWN_MAX_LENGTH = 16000;
|
||||
|
||||
export const READ_ONLY_TOOLTIP = 'The editor is read-only';
|
||||
|
||||
/** The canonical syntax; the renderer resolves the other three too. */
|
||||
export const formatVariableToken = (name: string): string => `$${name}`;
|
||||
|
||||
|
||||
@@ -95,7 +95,6 @@
|
||||
font-weight: var(--uppercase-small-500-font-weight);
|
||||
line-height: 100%;
|
||||
letter-spacing: 0.44px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,14 +55,14 @@ function NameEmailCell({
|
||||
function StatusBadge({ status }: { status: MemberRow['status'] }): JSX.Element {
|
||||
if (status === MemberStatus.Active) {
|
||||
return (
|
||||
<Badge color="forest" variant="outline">
|
||||
<Badge color="success" variant="outlined">
|
||||
ACTIVE
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
if (status === MemberStatus.Deleted) {
|
||||
return (
|
||||
<Badge color="cherry" variant="outline">
|
||||
<Badge color="danger" variant="outlined">
|
||||
DELETED
|
||||
</Badge>
|
||||
);
|
||||
@@ -70,13 +70,17 @@ function StatusBadge({ status }: { status: MemberRow['status'] }): JSX.Element {
|
||||
|
||||
if (status === MemberStatus.Invited) {
|
||||
return (
|
||||
<Badge color="amber" variant="outline">
|
||||
<Badge color="warning" variant="outlined">
|
||||
INVITED
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
return <Badge color="vanilla">⎯</Badge>;
|
||||
return (
|
||||
<Badge variant="solid" color="secondary">
|
||||
⎯
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
function MembersEmptyState({
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { Button, Select } from 'antd';
|
||||
import { Checkbox } from '@signozhq/ui/checkbox';
|
||||
import { TooltipProvider, TooltipSimple } from '@signozhq/ui/tooltip';
|
||||
import { Tooltip } from '@signozhq/ui/tooltip';
|
||||
import cx from 'classnames';
|
||||
import TextToolTip from 'components/TextToolTip/TextToolTip';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
@@ -764,9 +764,14 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
>
|
||||
<Checkbox
|
||||
color="primary"
|
||||
value={isSelected}
|
||||
className="option-checkbox"
|
||||
onClick={(e): void => selectFromButton(e, 'checkbox')}
|
||||
width="100%"
|
||||
onChange={(): void => {
|
||||
handleItemSelection('checkbox');
|
||||
setActiveChipIndex(-1);
|
||||
setActiveIndex(-1);
|
||||
}}
|
||||
>
|
||||
<div className="option-content">
|
||||
<span ref={textRef} className="option-label-text">
|
||||
@@ -1604,7 +1609,7 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
|
||||
<Checkbox value={allOptionsSelected} className="option-checkbox">
|
||||
<Checkbox color="primary" value={allOptionsSelected} width="100%">
|
||||
<div className="option-content">
|
||||
<div className="all-option-text">ALL</div>
|
||||
</div>
|
||||
@@ -1983,13 +1988,9 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
|
||||
// `label` arrives already cut to maxTagTextLength, so the reveal reads the
|
||||
// option's own text (falling back to the raw value for freeform tags).
|
||||
return (
|
||||
<TooltipSimple
|
||||
side="top"
|
||||
delayDuration={300}
|
||||
title={findOptionLabelText(options, value)}
|
||||
>
|
||||
<Tooltip side="top" title={findOptionLabelText(options, value)}>
|
||||
{tag}
|
||||
</TooltipSimple>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2025,56 +2026,51 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
|
||||
return (
|
||||
// Self-provided so the per-tag tooltips work wherever this select is rendered,
|
||||
// without every consumer having to sit under an app-level provider.
|
||||
<TooltipProvider>
|
||||
<div
|
||||
className={cx('custom-multiselect-wrapper', {
|
||||
'all-selected': allOptionShown || isAllSelected,
|
||||
<div
|
||||
className={cx('custom-multiselect-wrapper', {
|
||||
'all-selected': allOptionShown || isAllSelected,
|
||||
})}
|
||||
>
|
||||
{(allOptionShown || isAllSelected) && !searchText && (
|
||||
<div className="all-text">ALL</div>
|
||||
)}
|
||||
<Select
|
||||
ref={selectRef}
|
||||
className={cx('custom-multiselect', className, {
|
||||
'has-selection': selectedChips.length > 0 && !isAllSelected,
|
||||
'is-all-selected': isAllSelected,
|
||||
})}
|
||||
>
|
||||
{(allOptionShown || isAllSelected) && !searchText && (
|
||||
<div className="all-text">ALL</div>
|
||||
)}
|
||||
<Select
|
||||
ref={selectRef}
|
||||
className={cx('custom-multiselect', className, {
|
||||
'has-selection': selectedChips.length > 0 && !isAllSelected,
|
||||
'is-all-selected': isAllSelected,
|
||||
})}
|
||||
placeholder={placeholder}
|
||||
mode="multiple"
|
||||
showSearch
|
||||
filterOption={false}
|
||||
onSearch={handleSearch}
|
||||
value={displayValue}
|
||||
onChange={(newValue): void => {
|
||||
handleInternalChange(newValue, false);
|
||||
}}
|
||||
onClear={onClearHandler}
|
||||
onDropdownVisibleChange={handleDropdownVisibleChange}
|
||||
open={isOpen}
|
||||
defaultActiveFirstOption={defaultActiveFirstOption}
|
||||
popupMatchSelectWidth={dropdownMatchSelectWidth}
|
||||
allowClear={allowClear}
|
||||
getPopupContainer={getPopupContainer ?? popupContainer}
|
||||
suffixIcon={<ChevronDown style={{ cursor: 'default' }} size="md" />}
|
||||
dropdownRender={customDropdownRender}
|
||||
menuItemSelectedIcon={null}
|
||||
popupClassName={cx(
|
||||
'custom-multiselect-dropdown-container',
|
||||
popupClassName,
|
||||
)}
|
||||
notFoundContent={<div className="empty-message">{noDataMessage}</div>}
|
||||
onKeyDown={handleKeyDown}
|
||||
tagRender={tagRender as any}
|
||||
placement={placement}
|
||||
listHeight={300}
|
||||
searchValue={searchText}
|
||||
maxTagTextLength={maxTagTextLength}
|
||||
maxTagCount={isAllSelected ? undefined : maxTagCount}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
</TooltipProvider>
|
||||
placeholder={placeholder}
|
||||
mode="multiple"
|
||||
showSearch
|
||||
filterOption={false}
|
||||
onSearch={handleSearch}
|
||||
value={displayValue}
|
||||
onChange={(newValue): void => {
|
||||
handleInternalChange(newValue, false);
|
||||
}}
|
||||
onClear={onClearHandler}
|
||||
onDropdownVisibleChange={handleDropdownVisibleChange}
|
||||
open={isOpen}
|
||||
defaultActiveFirstOption={defaultActiveFirstOption}
|
||||
popupMatchSelectWidth={dropdownMatchSelectWidth}
|
||||
allowClear={allowClear}
|
||||
getPopupContainer={getPopupContainer ?? popupContainer}
|
||||
suffixIcon={<ChevronDown style={{ cursor: 'default' }} size="md" />}
|
||||
dropdownRender={customDropdownRender}
|
||||
menuItemSelectedIcon={null}
|
||||
popupClassName={cx('custom-multiselect-dropdown-container', popupClassName)}
|
||||
notFoundContent={<div className="empty-message">{noDataMessage}</div>}
|
||||
onKeyDown={handleKeyDown}
|
||||
tagRender={tagRender as any}
|
||||
placement={placement}
|
||||
listHeight={300}
|
||||
searchValue={searchText}
|
||||
maxTagTextLength={maxTagTextLength}
|
||||
maxTagCount={isAllSelected ? undefined : maxTagCount}
|
||||
{...rest}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
X,
|
||||
} from '@signozhq/icons';
|
||||
import { Color } from '@signozhq/design-tokens';
|
||||
import { TooltipProvider } from '@signozhq/ui/tooltip';
|
||||
import { Select } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
@@ -740,36 +739,33 @@ const CustomSelect: React.FC<CustomSelectProps> = ({
|
||||
|
||||
// ===== Component Rendering =====
|
||||
return (
|
||||
// Self-provided so label tooltips work wherever this select is rendered.
|
||||
<TooltipProvider>
|
||||
<Select
|
||||
ref={selectRef}
|
||||
className={cx('custom-select', className)}
|
||||
placeholder={placeholder}
|
||||
showSearch
|
||||
filterOption={false}
|
||||
onSearch={handleSearch}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onDropdownVisibleChange={handleDropdownVisibleChange}
|
||||
open={isOpen}
|
||||
options={optionsWithHighlight}
|
||||
defaultActiveFirstOption={defaultActiveFirstOption}
|
||||
popupMatchSelectWidth={popupMatchSelectWidth}
|
||||
allowClear={allowClear ? { clearIcon } : false}
|
||||
getPopupContainer={getPopupContainer ?? popupContainer}
|
||||
suffixIcon={<ChevronDown style={{ cursor: 'default' }} size="md" />}
|
||||
dropdownRender={customDropdownRender}
|
||||
menuItemSelectedIcon={null}
|
||||
popupClassName={cx('custom-select-dropdown-container', popupClassName)}
|
||||
listHeight={300}
|
||||
placement={placement}
|
||||
optionFilterProp="label"
|
||||
notFoundContent={<div className="empty-message">{noDataMessage}</div>}
|
||||
onKeyDown={handleKeyDown}
|
||||
{...rest}
|
||||
/>
|
||||
</TooltipProvider>
|
||||
<Select
|
||||
ref={selectRef}
|
||||
className={cx('custom-select', className)}
|
||||
placeholder={placeholder}
|
||||
showSearch
|
||||
filterOption={false}
|
||||
onSearch={handleSearch}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
onDropdownVisibleChange={handleDropdownVisibleChange}
|
||||
open={isOpen}
|
||||
options={optionsWithHighlight}
|
||||
defaultActiveFirstOption={defaultActiveFirstOption}
|
||||
popupMatchSelectWidth={popupMatchSelectWidth}
|
||||
allowClear={allowClear ? { clearIcon } : false}
|
||||
getPopupContainer={getPopupContainer ?? popupContainer}
|
||||
suffixIcon={<ChevronDown style={{ cursor: 'default' }} size="md" />}
|
||||
dropdownRender={customDropdownRender}
|
||||
menuItemSelectedIcon={null}
|
||||
popupClassName={cx('custom-select-dropdown-container', popupClassName)}
|
||||
listHeight={300}
|
||||
placement={placement}
|
||||
optionFilterProp="label"
|
||||
notFoundContent={<div className="empty-message">{noDataMessage}</div>}
|
||||
onKeyDown={handleKeyDown}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||