Compare commits

..

1 Commits

Author SHA1 Message Date
Naman Verma
4afbaccd91 fix: make span gaps accept day and week as units 2026-07-10 10:55:35 +05:30
14 changed files with 225 additions and 3011 deletions

View File

@@ -75,7 +75,7 @@
"crypto-js": "4.2.0",
"d3-hierarchy": "3.1.2",
"dayjs": "^1.10.7",
"dompurify": "3.4.11",
"dompurify": "3.4.0",
"event-source-polyfill": "1.0.31",
"eventemitter3": "5.0.1",
"history": "4.10.1",
@@ -238,18 +238,10 @@
"prismjs": "1.30.0",
"got": "11.8.5",
"form-data": "4.0.6",
"brace-expansion": "^2.0.3",
"brace-expansion": "^2.0.2",
"on-headers": "^1.1.0",
"js-cookie": "^3.0.7",
"tmp": "0.2.7",
"vite": "npm:rolldown-vite@7.3.1",
"dompurify": "3.4.11",
"js-yaml@3": "3.15.0",
"js-yaml@4": "4.2.0",
"yaml@1": "1.10.3",
"react-router@6": "6.30.4",
"markdown-it": "14.2.0",
"mdast-util-to-hast@13": "13.2.1",
"protocol-buffers-schema": "3.6.1"
"vite": "npm:rolldown-vite@7.3.1"
}
}

152
frontend/pnpm-lock.yaml generated
View File

@@ -19,19 +19,11 @@ overrides:
prismjs: 1.30.0
got: 11.8.5
form-data: 4.0.6
brace-expansion: ^2.0.3
brace-expansion: ^2.0.2
on-headers: ^1.1.0
js-cookie: ^3.0.7
tmp: 0.2.7
vite: npm:rolldown-vite@7.3.1
dompurify: 3.4.11
js-yaml@3: 3.15.0
js-yaml@4: 4.2.0
yaml@1: 1.10.3
react-router@6: 6.30.4
markdown-it: 14.2.0
mdast-util-to-hast@13: 13.2.1
protocol-buffers-schema: 3.6.1
importers:
@@ -87,7 +79,7 @@ importers:
version: 0.0.2(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
'@signozhq/ui':
specifier: 0.0.23
version: 0.0.23(@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.4(react@18.2.0))(react@18.2.0)
version: 0.0.23(@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.3(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)
@@ -161,8 +153,8 @@ importers:
specifier: ^1.10.7
version: 1.11.20
dompurify:
specifier: 3.4.11
version: 3.4.11
specifier: 3.4.0
version: 3.4.0
event-source-polyfill:
specifier: 1.0.31
version: 1.0.31
@@ -204,7 +196,7 @@ importers:
version: 12.4.13(@emotion/is-prop-valid@1.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
nuqs:
specifier: 2.8.8
version: 2.8.8(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.4(react@18.2.0))(react@18.2.0)
version: 2.8.8(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.3(react@18.2.0))(react@18.2.0)
overlayscrollbars:
specifier: ^2.8.1
version: 2.9.2
@@ -3048,10 +3040,6 @@ packages:
resolution: {integrity: sha512-Ic6m2U/rMjTkhERIa/0ZtXJP17QUi2CbWE7cqx4J58M8aA3QTfW+2UlQ4psvTX9IO1RfNVhK3pcpdjej7L+t2w==}
engines: {node: '>=14.0.0'}
'@remix-run/router@1.23.3':
resolution: {integrity: sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q==}
engines: {node: '>=14.0.0'}
'@rolldown/binding-android-arm64@1.0.0-beta.53':
resolution: {integrity: sha512-Ok9V8o7o6YfSdTTYA/uHH30r3YtOxLD6G3wih/U9DO0ucBBFq8WPt/DslU53OgfteLRHITZny9N/qCUxMf9kjQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -4155,8 +4143,8 @@ packages:
boolbase@1.0.0:
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
brace-expansion@2.1.1:
resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==}
brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
@@ -4817,8 +4805,14 @@ packages:
resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
engines: {node: '>= 4'}
dompurify@3.4.11:
resolution: {integrity: sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==}
dompurify@3.2.4:
resolution: {integrity: sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==}
dompurify@3.2.7:
resolution: {integrity: sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==}
dompurify@3.4.0:
resolution: {integrity: sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==}
domutils@2.8.0:
resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==}
@@ -6061,12 +6055,12 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
js-yaml@3.15.0:
resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==}
js-yaml@3.14.1:
resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
hasBin: true
js-yaml@4.2.0:
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
js-yaml@4.1.1:
resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
hasBin: true
jsdom@20.0.3:
@@ -6250,8 +6244,8 @@ packages:
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
linkify-it@5.0.2:
resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==}
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
lint-staged@17.0.4:
resolution: {integrity: sha512-+rU9lSUyVOZ/hDUmRLVGzyS2v73cDdQjX+XQz1AaOdIE4RysLq0HoPW2HrrgeNCLklkhi904VBU1bmgWLHVnkA==}
@@ -6370,8 +6364,8 @@ packages:
mapbox-to-css-font@2.4.5:
resolution: {integrity: sha512-VJ6nB8emkO9VODI0Fk+TQ/0zKBTqmf/Pkt8Xv0kHstoc0iXRajA00DAid4Kc3K5xeFIOoiZrVxijEzj0GLVO2w==}
markdown-it@14.2.0:
resolution: {integrity: sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==}
markdown-it@14.1.1:
resolution: {integrity: sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==}
hasBin: true
markdown-table@3.0.4:
@@ -6435,8 +6429,8 @@ packages:
mdast-util-to-hast@12.3.0:
resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==}
mdast-util-to-hast@13.2.1:
resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==}
mdast-util-to-hast@13.1.0:
resolution: {integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==}
mdast-util-to-markdown@1.5.0:
resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==}
@@ -6778,7 +6772,7 @@ packages:
'@tanstack/react-router': ^1
next: '>=14.2.0'
react: '>=18.2.0 || ^19.0.0-0'
react-router: 6.30.4
react-router: ^5 || ^6 || ^7
react-router-dom: ^5 || ^6 || ^7
peerDependenciesMeta:
'@remix-run/react':
@@ -6799,7 +6793,7 @@ packages:
'@tanstack/react-router': ^1
next: '>=14.2.0'
react: '>=18.2.0 || ^19.0.0-0'
react-router: 6.30.4
react-router: ^5 || ^6 || ^7
react-router-dom: ^5 || ^6 || ^7
peerDependenciesMeta:
'@remix-run/react':
@@ -7180,8 +7174,8 @@ packages:
property-information@6.3.0:
resolution: {integrity: sha512-gVNZ74nqhRMiIUYWGQdosYetaKc83x8oT41a0LlV3AAFCAZwCpg4vmGkq8t34+cUhp3cnM4XDiU/7xlgK7HGrg==}
protocol-buffers-schema@3.6.1:
resolution: {integrity: sha512-VG2K63Igkiv9p76tk1lilczEK1cT+kCjKtkdhw1dQZV3k3IXJbd3o6Ho8b9zJZaHSnT2hKe4I+ObmX9w6m5SmQ==}
protocol-buffers-schema@3.6.0:
resolution: {integrity: sha512-TdDRD+/QNdrCGCE7v8340QyuXd4kIWIgapsE2+n/SaGiSSbomYl4TjHlvIoCWRpE7wFt02EpB35VVA2ImcBVqw==}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
@@ -7696,8 +7690,8 @@ packages:
peerDependencies:
react: '>=15'
react-router@6.30.4:
resolution: {integrity: sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA==}
react-router@6.30.3:
resolution: {integrity: sha512-XRnlbKMTmktBkjCLE8/XcZFlnHvr2Ltdr1eJX4idL55/9BbORzyZEaIkBFDhFGCEWBBItsVrDxwx3gnisMitdw==}
engines: {node: '>=14.0.0'}
peerDependencies:
react: '>=16.8'
@@ -8952,8 +8946,8 @@ packages:
yallist@4.0.0:
resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
yaml@1.10.3:
resolution: {integrity: sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==}
yaml@1.10.2:
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
engines: {node: '>= 6'}
yaml@2.8.2:
@@ -9074,7 +9068,7 @@ snapshots:
dependencies:
'@jsdevtools/ono': 7.1.3
'@types/json-schema': 7.0.15
js-yaml: 4.2.0
js-yaml: 4.1.1
'@babel/code-frame@7.29.0':
dependencies:
@@ -10333,7 +10327,7 @@ snapshots:
'@types/string-hash': 1.1.3
d3-interpolate: 3.0.1
date-fns: 4.1.0
dompurify: 3.4.11
dompurify: 3.2.4
eventemitter3: 5.0.1
fast_array_intersect: 1.1.0
history: 4.10.1
@@ -10484,7 +10478,7 @@ snapshots:
camelcase: 5.3.1
find-up: 4.1.0
get-package-type: 0.1.0
js-yaml: 3.15.0
js-yaml: 3.14.1
resolve-from: 5.0.0
'@istanbuljs/schema@0.1.3': {}
@@ -11811,8 +11805,6 @@ snapshots:
'@remix-run/router@1.23.2': {}
'@remix-run/router@1.23.3': {}
'@rolldown/binding-android-arm64@1.0.0-beta.53':
optional: true
@@ -12050,7 +12042,7 @@ snapshots:
- react-dom
- tailwindcss
'@signozhq/ui@0.0.23(@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.4(react@18.2.0))(react@18.2.0)':
'@signozhq/ui@0.0.23(@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.3(react@18.2.0))(react@18.2.0)':
dependencies:
'@chenglou/pretext': 0.0.5
'@radix-ui/react-checkbox': 1.3.3(@types/react-dom@18.0.10)(@types/react@18.0.26)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -12079,7 +12071,7 @@ snapshots:
lodash-es: 4.18.1
motion: 11.18.2(@emotion/is-prop-valid@1.2.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
next-themes: 0.4.6(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
nuqs: 2.8.9(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.4(react@18.2.0))(react@18.2.0)
nuqs: 2.8.9(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.3(react@18.2.0))(react@18.2.0)
react: 18.2.0
react-day-picker: 9.9.0(react@18.2.0)
react-dom: 18.2.0(react@18.2.0)
@@ -13065,7 +13057,7 @@ snapshots:
boolbase@1.0.0: {}
brace-expansion@2.1.1:
brace-expansion@2.0.2:
dependencies:
balanced-match: 1.0.2
@@ -13389,14 +13381,14 @@ snapshots:
import-fresh: 3.3.1
parse-json: 5.2.0
path-type: 4.0.0
yaml: 1.10.3
yaml: 1.10.2
optional: true
cosmiconfig@9.0.1(typescript@5.9.3):
dependencies:
env-paths: 2.2.1
import-fresh: 3.3.1
js-yaml: 4.2.0
js-yaml: 4.1.1
parse-json: 5.2.0
optionalDependencies:
typescript: 5.9.3
@@ -13728,7 +13720,15 @@ snapshots:
dependencies:
domelementtype: 2.3.0
dompurify@3.4.11:
dompurify@3.2.4:
optionalDependencies:
'@types/trusted-types': 2.0.7
dompurify@3.2.7:
optionalDependencies:
'@types/trusted-types': 2.0.7
dompurify@3.4.0:
optionalDependencies:
'@types/trusted-types': 2.0.7
@@ -14416,7 +14416,7 @@ snapshots:
hast-util-from-parse5: 8.0.1
hast-util-to-parse5: 8.0.0
html-void-elements: 3.0.0
mdast-util-to-hast: 13.2.1
mdast-util-to-hast: 13.1.0
parse5: 7.3.0
unist-util-position: 5.0.0
unist-util-visit: 5.0.0
@@ -15342,12 +15342,12 @@ snapshots:
js-tokens@4.0.0: {}
js-yaml@3.15.0:
js-yaml@3.14.1:
dependencies:
argparse: 1.0.10
esprima: 4.0.1
js-yaml@4.2.0:
js-yaml@4.1.1:
dependencies:
argparse: 2.0.1
@@ -15396,7 +15396,7 @@ snapshots:
'@types/json-schema': 7.0.15
'@types/lodash': 4.17.24
is-glob: 4.0.3
js-yaml: 4.2.0
js-yaml: 4.1.1
lodash: 4.18.1
minimist: 1.2.8
prettier: 3.8.3
@@ -15533,7 +15533,7 @@ snapshots:
lines-and-columns@1.2.4: {}
linkify-it@5.0.2:
linkify-it@5.0.0:
dependencies:
uc.micro: 2.1.0
@@ -15654,11 +15654,11 @@ snapshots:
mapbox-to-css-font@2.4.5: {}
markdown-it@14.2.0:
markdown-it@14.1.1:
dependencies:
argparse: 2.0.1
entities: 4.5.0
linkify-it: 5.0.2
linkify-it: 5.0.0
mdurl: 2.0.0
punycode.js: 2.3.1
uc.micro: 2.1.0
@@ -15772,7 +15772,7 @@ snapshots:
unist-util-position: 4.0.4
unist-util-visit: 4.1.2
mdast-util-to-hast@13.2.1:
mdast-util-to-hast@13.1.0:
dependencies:
'@types/hast': 3.0.4
'@types/mdast': 4.0.3
@@ -16049,19 +16049,19 @@ snapshots:
minimatch@10.2.5:
dependencies:
brace-expansion: 2.1.1
brace-expansion: 2.0.2
minimatch@3.1.5:
dependencies:
brace-expansion: 2.1.1
brace-expansion: 2.0.2
minimatch@5.1.9:
dependencies:
brace-expansion: 2.1.1
brace-expansion: 2.0.2
minimatch@9.0.9:
dependencies:
brace-expansion: 2.1.1
brace-expansion: 2.0.2
minimist@1.2.8: {}
@@ -16075,7 +16075,7 @@ snapshots:
monaco-editor@0.55.1:
dependencies:
dompurify: 3.4.11
dompurify: 3.2.7
marked: 14.0.0
motion-dom@11.18.1:
@@ -16216,20 +16216,20 @@ snapshots:
dependencies:
boolbase: 1.0.0
nuqs@2.8.8(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.4(react@18.2.0))(react@18.2.0):
nuqs@2.8.8(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.3(react@18.2.0))(react@18.2.0):
dependencies:
'@standard-schema/spec': 1.0.0
react: 18.2.0
optionalDependencies:
react-router: 6.30.4(react@18.2.0)
react-router: 6.30.3(react@18.2.0)
react-router-dom: 5.3.4(react@18.2.0)
nuqs@2.8.9(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.4(react@18.2.0))(react@18.2.0):
nuqs@2.8.9(react-router-dom@5.3.4(react@18.2.0))(react-router@6.30.3(react@18.2.0))(react@18.2.0):
dependencies:
'@standard-schema/spec': 1.0.0
react: 18.2.0
optionalDependencies:
react-router: 6.30.4(react@18.2.0)
react-router: 6.30.3(react@18.2.0)
react-router-dom: 5.3.4(react@18.2.0)
nwsapi@2.2.23: {}
@@ -16336,7 +16336,7 @@ snapshots:
find-up: 8.0.0
fs-extra: 11.3.3
jiti: 2.6.1
js-yaml: 4.2.0
js-yaml: 4.1.1
remeda: 2.34.0
string-argv: 0.3.2
tsconfck: 3.1.6(typescript@5.9.3)
@@ -16549,7 +16549,7 @@ snapshots:
postcss-load-config@3.1.4(postcss@8.5.14)(ts-node@10.9.1(@types/node@16.18.25)(typescript@5.9.3)):
dependencies:
lilconfig: 2.1.0
yaml: 1.10.3
yaml: 1.10.2
optionalDependencies:
postcss: 8.5.14
ts-node: 10.9.1(@types/node@16.18.25)(typescript@5.9.3)
@@ -16654,7 +16654,7 @@ snapshots:
property-information@6.3.0: {}
protocol-buffers-schema@3.6.1: {}
protocol-buffers-schema@3.6.0: {}
proxy-from-env@1.1.0: {}
@@ -17264,7 +17264,7 @@ snapshots:
history: 5.3.0
react: 18.2.0
react-dom: 18.2.0(react@18.2.0)
react-router: 6.30.4(react@18.2.0)
react-router: 6.30.3(react@18.2.0)
react-router-dom: 5.3.4(react@18.2.0)
react-router-dom@5.3.4(react@18.2.0):
@@ -17291,9 +17291,9 @@ snapshots:
tiny-invariant: 1.3.3
tiny-warning: 1.0.3
react-router@6.30.4(react@18.2.0):
react-router@6.30.3(react@18.2.0):
dependencies:
'@remix-run/router': 1.23.3
'@remix-run/router': 1.23.2
react: 18.2.0
react-style-singleton@2.2.3(@types/react@18.0.26)(react@18.2.0):
@@ -17517,7 +17517,7 @@ snapshots:
resolve-protobuf-schema@2.1.0:
dependencies:
protocol-buffers-schema: 3.6.1
protocol-buffers-schema: 3.6.0
resolve@1.22.11:
dependencies:
@@ -18209,7 +18209,7 @@ snapshots:
dependencies:
'@gerrit0/mini-shiki': 3.23.0
lunr: 2.3.9
markdown-it: 14.2.0
markdown-it: 14.1.1
minimatch: 10.2.5
typescript: 5.9.3
yaml: 2.8.4
@@ -18673,7 +18673,7 @@ snapshots:
yallist@4.0.0: {}
yaml@1.10.3: {}
yaml@1.10.2: {}
yaml@2.8.2: {}

View File

@@ -43,7 +43,7 @@
&__title {
color: var(--l1-foreground);
font-size: var(--periscope-font-size-base);
font-size: 14px;
font-weight: 500;
line-height: 20px;
letter-spacing: -0.07px;
@@ -51,14 +51,14 @@
&__subtitle {
color: var(--l2-foreground);
font-size: var(--periscope-font-size-base);
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: -0.07px;
}
&__description {
font-size: var(--periscope-font-size-base);
font-size: 14px;
color: var(--l2-foreground);
line-height: 20px;
}
@@ -67,7 +67,7 @@
margin: 0;
margin-top: 8px;
color: var(--l2-foreground);
font-size: var(--periscope-font-size-base);
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: -0.07px;
@@ -106,7 +106,7 @@
border: 1px dashed var(--l1-border);
background: transparent;
color: var(--l2-foreground);
font-size: var(--periscope-font-size-base);
font-size: 14px;
font-weight: 400;
line-height: 18px;
letter-spacing: -0.07px;
@@ -120,15 +120,15 @@
gap: 6px;
}
// Stack the message and the resources card; card matches the content
// width above it and is capped so it doesn't sprawl in a wide panel.
&__row {
display: flex;
flex-direction: column;
align-items: stretch;
gap: 16px;
width: fit-content;
max-width: 600px;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-end;
max-width: 825px;
gap: 25px;
justify-content: center;
margin-left: 21px;
}
&__content {
@@ -142,7 +142,7 @@
background: var(--l2-background);
border: 1px solid var(--l1-border);
border-radius: 4px;
width: 100%; // match the content width above
width: 332px;
}
&__resources-title {
@@ -155,6 +155,7 @@
text-transform: uppercase;
padding: 16px 16px 12px;
border-bottom: 1px solid var(--l1-border);
height: 46px;
}
&__resources-links {

View File

@@ -34,22 +34,6 @@
min-width: 0;
}
// Logs-tab footer. Sibling of the scrolling .panelBody inside .panel, so it's
// a true fixed footer flush with the panel's bottom edge — always visible
// regardless of how the body is scrolled.
.logsFooter {
flex-shrink: 0;
display: flex;
justify-content: flex-end;
align-items: center;
// Inset to match panelBody's 12px padding so the border-top aligns with
// the content edges instead of bleeding to the panel border.
margin: 0 12px;
padding: 8px 0 12px;
border-top: 1px solid var(--l2-border);
background: var(--l1-background);
}
// Single scroll: when the summary sits above (non-docked modes) it scrolls away
// and the tab section pins to the top; tab content scrolls inside `.tabsScroll`.
.tabsSection {

View File

@@ -1,4 +1,4 @@
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useMemo } from 'react';
import { Badge } from '@signozhq/ui/badge';
import {
TabsContent,
@@ -49,7 +49,6 @@ import DockModeSwitcher from './DockModeSwitcher';
import { useSpanAttributeActions } from './hooks/useSpanAttributeActions';
import { useTracePinnedFields } from './hooks/useTracePinnedFields';
import Events from './Events/Events';
import OpenInLogsExplorer from './SpanLogs/OpenInLogsExplorer';
import SpanLogs from './SpanLogs/SpanLogs';
import { useSpanContextLogs } from './SpanLogs/useSpanContextLogs';
import SpanSummary from './SpanSummary';
@@ -69,9 +68,6 @@ interface SpanDetailsPanelProps {
// dock, or a floating/right panel widened to match). ~right-dock max width.
const WIDE_PANEL_BREAKPOINT = 720;
// Context-log window padding around the span's trace time range.
const FIVE_MINUTES_IN_MS = 5 * 60 * 1000;
function SpanDetailsContent({
selectedSpan,
traceStartTime,
@@ -81,15 +77,12 @@ function SpanDetailsContent({
traceStartTime?: number;
traceEndTime?: number;
}): JSX.Element {
const FIVE_MINUTES_IN_MS = 5 * 60 * 1000;
const [bodyRef, { width: bodyWidth }] = useMeasure<HTMLDivElement>();
const spanAttributeActions = useSpanAttributeActions();
const logTraceEvent = useTraceDetailLogEvent('v3', selectedSpan.trace_id);
// Tracked so the panel can render tab-specific chrome (the Logs footer)
// outside the scrolling tab content.
const [activeTab, setActiveTab] = useState('overview');
const handleTabChange = useCallback(
(tab: string): void => {
setActiveTab(tab);
logTraceEvent(TraceDetailEvents.SpanPanelTabChanged, {
[TraceDetailEventKeys.Tab]: tab,
[TraceDetailEventKeys.SpanId]: selectedSpan.span_id,
@@ -288,99 +281,89 @@ function SpanDetailsContent({
const eventsCount = selectedSpan.events?.length || 0;
return (
<>
<div className={styles.panelBody} ref={bodyRef}>
{!isWide && <div className={styles.detailsSection}>{summary}</div>}
<div className={styles.panelBody} ref={bodyRef}>
{!isWide && <div className={styles.detailsSection}>{summary}</div>}
<div className={styles.tabsSection}>
{/* Step 9: ContentTabs */}
<TabsRoot defaultValue="overview" onValueChange={handleTabChange}>
<TabsList variant="secondary">
<TabsTrigger value="overview" variant="secondary">
<Bookmark size={14} /> Overview
</TabsTrigger>
<TabsTrigger value="events" variant="secondary">
<ScrollText size={14} /> Events
{eventsCount > 0 && (
<Badge color="secondary" className={styles.eventsBadge}>
{eventsCount}
</Badge>
)}
</TabsTrigger>
<TabsTrigger value="logs" variant="secondary">
<List size={14} /> Logs
</TabsTrigger>
{infraMetadata && (
<TabsTrigger value="metrics" variant="secondary">
<ChartColumnBig size={14} /> Metrics
</TabsTrigger>
<div className={styles.tabsSection}>
{/* Step 9: ContentTabs */}
<TabsRoot defaultValue="overview" onValueChange={handleTabChange}>
<TabsList variant="secondary">
<TabsTrigger value="overview" variant="secondary">
<Bookmark size={14} /> Overview
</TabsTrigger>
<TabsTrigger value="events" variant="secondary">
<ScrollText size={14} /> Events
{eventsCount > 0 && (
<Badge color="secondary" className={styles.eventsBadge}>
{eventsCount}
</Badge>
)}
</TabsList>
</TabsTrigger>
<TabsTrigger value="logs" variant="secondary">
<List size={14} /> Logs
</TabsTrigger>
{infraMetadata && (
<TabsTrigger value="metrics" variant="secondary">
<ChartColumnBig size={14} /> Metrics
</TabsTrigger>
)}
</TabsList>
<div className={styles.tabsScroll}>
<TabsContent value="overview">
{isWide && summary}
<DataViewer
data={spanDisplayData}
drawerKey="trace-details"
prettyViewProps={{
showPinned: true,
actions: prettyViewCustomActions,
visibleActions: VISIBLE_ACTIONS,
pinnedFieldsValue,
onPinnedFieldsChange,
}}
<div className={styles.tabsScroll}>
<TabsContent value="overview">
{isWide && summary}
<DataViewer
data={spanDisplayData}
drawerKey="trace-details"
prettyViewProps={{
showPinned: true,
actions: prettyViewCustomActions,
visibleActions: VISIBLE_ACTIONS,
pinnedFieldsValue,
onPinnedFieldsChange,
}}
/>
</TabsContent>
<TabsContent value="events">
<Events
span={selectedSpan}
startTime={traceStartTime || 0}
isSearchVisible
/>
</TabsContent>
<TabsContent value="logs">
<SpanLogs
traceId={selectedSpan.trace_id}
spanId={selectedSpan.span_id}
timeRange={{
startTime: (traceStartTime || 0) - FIVE_MINUTES_IN_MS,
endTime: (traceEndTime || 0) + FIVE_MINUTES_IN_MS,
}}
logs={logs}
isLoading={isLogsLoading}
isError={isLogsError}
isFetching={isLogsFetching}
isLogSpanRelated={isLogSpanRelated}
handleExplorerPageRedirect={handleExplorerPageRedirect}
emptyStateConfig={!hasTraceIdLogs ? emptyLogsStateConfig : undefined}
/>
</TabsContent>
{infraMetadata && (
<TabsContent value="metrics">
<InfraMetrics
clusterName={infraMetadata.clusterName}
podName={infraMetadata.podName}
nodeName={infraMetadata.nodeName}
hostName={infraMetadata.hostName}
timestamp={infraMetadata.spanTimestamp}
dataSource={DataSource.TRACES}
/>
</TabsContent>
<TabsContent value="events">
<Events
span={selectedSpan}
startTime={traceStartTime || 0}
isSearchVisible
/>
</TabsContent>
<TabsContent value="logs">
<SpanLogs
traceId={selectedSpan.trace_id}
spanId={selectedSpan.span_id}
timeRange={{
startTime: (traceStartTime || 0) - FIVE_MINUTES_IN_MS,
endTime: (traceEndTime || 0) + FIVE_MINUTES_IN_MS,
}}
logs={logs}
isLoading={isLogsLoading}
isError={isLogsError}
isFetching={isLogsFetching}
isLogSpanRelated={isLogSpanRelated}
handleExplorerPageRedirect={handleExplorerPageRedirect}
emptyStateConfig={!hasTraceIdLogs ? emptyLogsStateConfig : undefined}
/>
</TabsContent>
{infraMetadata && (
<TabsContent value="metrics">
<InfraMetrics
clusterName={infraMetadata.clusterName}
podName={infraMetadata.podName}
nodeName={infraMetadata.nodeName}
hostName={infraMetadata.hostName}
timestamp={infraMetadata.spanTimestamp}
dataSource={DataSource.TRACES}
/>
</TabsContent>
)}
</div>
</TabsRoot>
</div>
)}
</div>
</TabsRoot>
</div>
{/* Sibling of the scrolling panelBody, so it's a true fixed footer flush
with the panel's bottom edge — always visible, never scrolls. */}
{activeTab === 'logs' && (
<div className={styles.logsFooter}>
<OpenInLogsExplorer onClick={handleExplorerPageRedirect} />
</div>
)}
</>
</div>
);
}

View File

@@ -1,25 +0,0 @@
import { Button } from '@signozhq/ui/button';
import { Compass } from '@signozhq/icons';
interface OpenInLogsExplorerProps {
onClick: () => void;
}
// Opens the full Logs Explorer (new tab) filtered to this span's trace.
// Placement/alignment is the caller's responsibility.
function OpenInLogsExplorer({ onClick }: OpenInLogsExplorerProps): JSX.Element {
return (
<Button
variant="solid"
color="secondary"
size="md"
onClick={onClick}
prefix={<Compass size={16} />}
data-testid="open-in-explorer-button"
>
Open in Logs Explorer
</Button>
);
}
export default OpenInLogsExplorer;

View File

@@ -1,4 +1,5 @@
.spanLogs {
margin-inline: var(--spacing-8);
height: 100%;
display: flex;
flex-direction: column;

View File

@@ -1,8 +1,6 @@
import ROUTES from 'constants/routes';
import { getEmptyLogsListConfig } from 'container/LogsExplorerList/utils';
import { server } from 'mocks-server/server';
import { render, screen, userEvent } from 'tests/test-utils';
import { ILog } from 'types/api/logs/log';
import SpanLogs from '../SpanLogs';
@@ -84,10 +82,8 @@ jest.mock('providers/preferences/context/PreferenceContextProvider', () => ({
),
}));
// Mock OverlayScrollbar (default export — needs __esModule for the interop
// unwrap, otherwise the default import resolves to the module object).
// Mock OverlayScrollbar
jest.mock('components/OverlayScrollbar/OverlayScrollbar', () => ({
__esModule: true,
default: ({ children }: any): JSX.Element => (
<div data-testid="overlay-scrollbar">{children}</div>
),
@@ -114,13 +110,6 @@ jest.mock(
const TEST_TRACE_ID = 'test-trace-id';
const TEST_SPAN_ID = 'test-span-id';
const sampleLog = {
id: 'log-1',
body: 'sample log body',
timestamp: '1640995200000',
spanID: TEST_SPAN_ID,
} as unknown as ILog;
const defaultProps = {
traceId: TEST_TRACE_ID,
spanId: TEST_SPAN_ID,
@@ -218,22 +207,4 @@ describe('SpanLogs', () => {
expect(mockHandleExplorerPageRedirect).toHaveBeenCalledTimes(1);
});
it('opens a new tab to Logs Explorer with the trace_id + span_id query when a log row is clicked', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(<SpanLogs {...defaultProps} logs={[sampleLog]} />);
await user.click(screen.getByTestId(`raw-log-${sampleLog.id}`));
expect(mockWindowOpen).toHaveBeenCalledTimes(1);
const [url, target] = mockWindowOpen.mock.calls[0];
// Opens Logs Explorer in a new tab, filtered to this trace/span, and
// deep-links to the clicked log.
expect(url).toContain(ROUTES.LOGS_EXPLORER);
expect(url).toContain(TEST_TRACE_ID);
expect(url).toContain(TEST_SPAN_ID);
expect(url).toContain(sampleLog.id); // activeLogId
expect(target).toBe('_blank');
});
});

View File

@@ -1,19 +1,11 @@
import { screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ROUTES from 'constants/routes';
import { render } from 'tests/test-utils';
import { SpanV3 } from 'types/api/trace/getTraceV3';
import { SpanDetailVariant } from '../constants';
import SpanDetailsPanel from '../SpanDetailsPanel';
// Mock window.open for the Open in Logs Explorer footer redirect.
const mockWindowOpen = jest.fn();
Object.defineProperty(window, 'open', {
value: mockWindowOpen,
writable: true,
});
// Placement is width-driven via useMeasure (jsdom reports 0), so we control the
// reported width per test. `mock` prefix lets the jest.mock factory reference it.
let mockWidth = 0;
@@ -165,42 +157,3 @@ describe('SpanDetailsPanel tabs', () => {
expect(screen.getByTestId('logs-tab')).toBeInTheDocument();
});
});
// The footer lives on the panel body (not inside the tab content) so it stays
// pinned to the panel's visible bottom edge; it should track the active tab.
describe('SpanDetailsPanel Open in Logs Explorer footer', () => {
beforeEach(() => {
mockWindowOpen.mockClear();
});
it('appears only while the Logs tab is active', async () => {
const user = userEvent.setup({ delay: null });
renderPanel(400);
expect(
screen.queryByTestId('open-in-explorer-button'),
).not.toBeInTheDocument();
await user.click(screen.getByRole('tab', { name: /logs/i }));
expect(screen.getByTestId('open-in-explorer-button')).toBeInTheDocument();
await user.click(screen.getByRole('tab', { name: /overview/i }));
expect(
screen.queryByTestId('open-in-explorer-button'),
).not.toBeInTheDocument();
});
it('opens Logs Explorer in a new tab filtered to the span trace when clicked', async () => {
const user = userEvent.setup({ delay: null });
renderPanel(400);
await user.click(screen.getByRole('tab', { name: /logs/i }));
await user.click(screen.getByTestId('open-in-explorer-button'));
expect(mockWindowOpen).toHaveBeenCalledTimes(1);
const [url, target] = mockWindowOpen.mock.calls[0];
expect(url).toContain(ROUTES.LOGS_EXPLORER);
expect(url).toContain('trace-1');
expect(target).toBe('_blank');
});
});

View File

@@ -8,86 +8,7 @@
"logs": true
},
"dataCollected": {
"metrics": [
{
"name": "cloudsql.googleapis.com/database/up",
"unit": "Count",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/cpu/utilization",
"unit": "Percent",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/memory/utilization",
"unit": "Percent",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/memory/usage",
"unit": "Bytes",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/disk/bytes_used",
"unit": "Bytes",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/postgresql/num_backends",
"unit": "Count",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/postgresql/num_backends_by_state",
"unit": "Count",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/postgresql/transaction_count",
"unit": "Count",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/postgresql/deadlock_count",
"unit": "Count",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/postgresql/vacuum/oldest_transaction_age",
"unit": "Count",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/postgresql/insights/aggregate/execution_time",
"unit": "Microseconds",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/postgresql/insights/perquery/execution_time",
"unit": "Microseconds",
"type": "Gauge",
"description": ""
},
{
"name": "cloudsql.googleapis.com/database/postgresql/replication/replica_byte_lag",
"unit": "Bytes",
"type": "Gauge",
"description": ""
}
],
"metrics": [],
"logs": []
},
"telemetryCollectionStrategy": {

View File

@@ -38,7 +38,7 @@ func newTestDashboardV2(t *testing.T, orgID valuer.UUID, source Source) *Dashboa
LineInterpolation: LineInterpolationSpline,
LineStyle: LineStyleSolid,
FillMode: FillModeSolid,
SpanGaps: SpanGaps{FillLessThan: valuer.MustParseTextDuration("60s")},
SpanGaps: SpanGaps{FillLessThan: "60s"},
},
Legend: Legend{Position: LegendPositionBottom, Mode: LegendModeList},
},

View File

@@ -6,7 +6,6 @@ import (
"os"
"strings"
"testing"
"time"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/perses/spec/go/dashboard"
@@ -1371,7 +1370,7 @@ func TestSpanGaps(t *testing.T) {
t.Run("defaults", func(t *testing.T) {
var sg SpanGaps
assert.False(t, sg.FillOnlyBelow, "expected FillOnlyBelow default false")
assert.True(t, sg.FillLessThan.IsZero(), "expected FillLessThan default zero")
assert.Empty(t, sg.FillLessThan, "expected FillLessThan default empty")
})
t.Run("fillOnlyBelow true", func(t *testing.T) {
@@ -1382,12 +1381,27 @@ func TestSpanGaps(t *testing.T) {
t.Run("fillLessThan duration", func(t *testing.T) {
sg := unmarshal(t, `{"fillOnlyBelow": false, "fillLessThan": "5m"}`)
assert.False(t, sg.FillOnlyBelow)
assert.Equal(t, 5*time.Minute, sg.FillLessThan.Duration())
assert.Equal(t, "5m", sg.FillLessThan)
})
t.Run("fillLessThan compound duration", func(t *testing.T) {
sg := unmarshal(t, `{"fillLessThan": "1h30m"}`)
assert.Equal(t, 90*time.Minute, sg.FillLessThan.Duration())
assert.Equal(t, "1h30m", sg.FillLessThan)
})
t.Run("fillLessThan day duration", func(t *testing.T) {
sg := unmarshal(t, `{"fillLessThan": "1d"}`)
assert.Equal(t, "1d", sg.FillLessThan)
})
t.Run("invalid fillLessThan rejected on unmarshal", func(t *testing.T) {
var sg SpanGaps
require.Error(t, json.Unmarshal([]byte(`{"fillLessThan": "not-a-duration"}`), &sg))
})
t.Run("non-positive fillLessThan rejected on unmarshal", func(t *testing.T) {
var sg SpanGaps
require.Error(t, json.Unmarshal([]byte(`{"fillLessThan": "0s"}`), &sg))
})
}

View File

@@ -8,6 +8,7 @@ import (
qb "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/SigNoz/signoz/pkg/valuer"
"github.com/prometheus/common/model"
"github.com/swaggest/jsonschema-go"
)
@@ -621,8 +622,35 @@ func (fm *FillMode) UnmarshalJSON(data []byte) error {
}
type SpanGaps struct {
FillOnlyBelow bool `json:"fillOnlyBelow" description:"Controls whether lines connect across null values. When false (default), all gaps are connected. When true, only gaps smaller than fillLessThan are connected."`
FillLessThan valuer.TextDuration `json:"fillLessThan" description:"The maximum gap size to connect when fillOnlyBelow is true. Gaps larger than this duration are left disconnected."`
FillOnlyBelow bool `json:"fillOnlyBelow" description:"Controls whether lines connect across null values. When false (default), all gaps are connected. When true, only gaps smaller than fillLessThan are connected."`
FillLessThan string `json:"fillLessThan" description:"The maximum gap size to connect when fillOnlyBelow is true. Gaps larger than this duration are left disconnected."`
}
func (sg *SpanGaps) UnmarshalJSON(data []byte) error {
type alias SpanGaps
var tmp alias
if err := json.Unmarshal(data, &tmp); err != nil {
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "invalid spanGaps")
}
*sg = SpanGaps(tmp)
return sg.validate()
}
// validate checks that FillLessThan, when set, is a valid positive duration.
// It uses prometheus's parser so day/week/year units (e.g. "1d") are accepted;
// time.ParseDuration caps at hours.
func (sg SpanGaps) validate() error {
if sg.FillLessThan == "" {
return nil
}
d, err := model.ParseDuration(sg.FillLessThan)
if err != nil {
return errors.WrapInvalidInputf(err, ErrCodeDashboardInvalidInput, "invalid spanGaps.fillLessThan duration %q", sg.FillLessThan)
}
if d <= 0 {
return errors.NewInvalidInputf(ErrCodeDashboardInvalidInput, "spanGaps.fillLessThan duration must be positive, got %q", sg.FillLessThan)
}
return nil
}
type PrecisionOption struct{ valuer.String }