Compare commits

..

273 Commits

Author SHA1 Message Date
Naman Verma
8e39f44a9e Merge branch 'main' into nv/v2-dashboard-update 2026-05-27 18:42:47 +05:30
Naman Verma
e92a0e953c fix: remove unused module methods 2026-05-27 17:56:30 +05:30
Naman Verma
7add0b924c fix: remove unused store method 2026-05-27 17:54:38 +05:30
Naman Verma
d2070d4844 Merge branch 'main' into nv/v2-dashboard-update 2026-05-27 17:25:31 +05:30
Naman Verma
e9b0c32f19 fix: fix build error in test after merge conflict 2026-05-27 17:20:53 +05:30
Naman Verma
ae18dbc722 Merge branch 'main' into nv/v2-dashboard-update 2026-05-27 17:19:18 +05:30
Naman Verma
87ff61e887 fix: build error fix 2026-05-26 12:56:10 +05:30
Naman Verma
8afb247046 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-update 2026-05-26 12:54:07 +05:30
Naman Verma
3ee706c68e chore: make tags required in postable 2026-05-26 12:52:10 +05:30
Naman Verma
58767e67c0 chore: generate api specs 2026-05-26 12:50:39 +05:30
Naman Verma
ccecc13bb9 feat: add flag to generate unique name in backend 2026-05-26 12:41:04 +05:30
Naman Verma
1c8b2c1d21 Merge branch 'main' into nv/v2-dashboard-create 2026-05-26 12:03:27 +05:30
Naman Verma
8fbec89aab chore: remove enum def of threshold comparison operator 2026-05-26 12:02:18 +05:30
Naman Verma
40da02e5b4 test: add unit tests for type conversions 2026-05-26 12:01:02 +05:30
Naman Verma
6704e15a01 fix: correct convertor method name 2026-05-26 11:29:48 +05:30
Naman Verma
fbf30cc192 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-update 2026-05-26 11:06:47 +05:30
Naman Verma
ecd9670bf6 Merge branch 'main' into nv/v2-dashboard-create 2026-05-26 11:05:56 +05:30
Naman Verma
d8da99fcbb test: fix build errors and tests based on name related changes 2026-05-25 15:24:08 +05:30
Naman Verma
075e8d3029 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-update 2026-05-25 14:39:27 +05:30
Naman Verma
f7fc3eade8 feat: add validation on dashboard name 2026-05-25 14:32:45 +05:30
Naman Verma
9415c06166 Merge branch 'main' into nv/v2-dashboard-create 2026-05-25 14:12:27 +05:30
Naman Verma
fa8139b279 chore: remove integration test for now (will add along with list api) 2026-05-25 14:12:05 +05:30
Naman Verma
3271e35eeb fix: set display name in unmarshal json 2026-05-25 14:10:34 +05:30
Naman Verma
a48beef8ec chore: increase MaxTagsPerDashboard to 10 2026-05-25 14:06:15 +05:30
Naman Verma
ad4e3dcf45 fix: remove unneeded comment 2026-05-25 14:05:15 +05:30
Naman Verma
2e8295f0b3 fix: improve api descriptions 2026-05-25 14:03:32 +05:30
Naman Verma
3e6394ba50 fix: remove unused param in constructor 2026-05-25 13:59:53 +05:30
Naman Verma
95da13ecb9 feat: add immutable name in dashboard v2 api specs 2026-05-25 13:58:01 +05:30
Naman Verma
8b38e3969f Merge branch 'main' into nv/v2-dashboard-create 2026-05-25 13:56:54 +05:30
Naman Verma
f3d097a61a feat: add immutable name in dashboard v2 2026-05-25 13:55:18 +05:30
Naman Verma
81249342ff feat: add immutable name in dashboard v2 2026-05-25 13:55:08 +05:30
Naman Verma
5ce097ed2a fix: add some required fields 2026-05-25 12:51:06 +05:30
Naman Verma
3c985af24c Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-update 2026-05-25 11:38:56 +05:30
Naman Verma
c3f50b5db8 chore: incorporate source 2026-05-25 11:38:35 +05:30
Naman Verma
000dcc10b2 chore: incorporate source in api spec 2026-05-25 11:33:15 +05:30
Naman Verma
d7ffbe3f9f chore: incorporate source 2026-05-25 11:31:03 +05:30
Naman Verma
72cbc0450b Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-update 2026-05-25 11:14:19 +05:30
Naman Verma
7d7fd425e2 fix: add source for v2 dashboards 2026-05-25 11:13:48 +05:30
Naman Verma
55cfe57a9e Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-update 2026-05-25 10:22:04 +05:30
Naman Verma
0a4c75d899 Merge branch 'main' into nv/v2-dashboard-create 2026-05-25 10:21:47 +05:30
Naman Verma
6702930688 feat: patch dashboard api (#11182)
* feat: lock, unlock, create public, update public v2 dashboard APIs

* feat: delete dashboard v2 API and hard delete cron job

* feat: patch dashboard api

* chore: update api specs

* chore: update api specs

* chore: update api specs

* chore: remove delete related work

* fix: add examples of structs for value param in param description

* test: unit test fixes

* fix: use new pattern of checking for admin permission

* fix: remove soft delete reference

* test: key value tags in test

* fix: build error in patch module method

* fix: build error in Apply method

* fix: use sync tags method in update

* fix: fix build errors

* fix: fix all patch application tests

* chore: add more mapper methods
2026-05-21 10:04:38 +05:30
Naman Verma
4a24ee44e8 chore: generate api specs 2026-05-20 23:44:22 +05:30
Naman Verma
0045ecadab Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-update 2026-05-20 23:43:34 +05:30
Naman Verma
69aad53eb4 chore: generate api specs 2026-05-20 23:42:24 +05:30
Naman Verma
7bacb03483 Merge branch 'main' into nv/v2-dashboard-create 2026-05-20 23:41:18 +05:30
Naman Verma
b610056954 chore: update frontend schema 2026-05-18 20:34:45 +05:30
Naman Verma
db77b398e7 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-update 2026-05-18 20:34:17 +05:30
Naman Verma
574867bafb chore: update frontend schema 2026-05-18 20:33:38 +05:30
Naman Verma
d87edca9d1 Merge branch 'main' into nv/v2-dashboard-create 2026-05-18 20:32:34 +05:30
Naman Verma
a7debaa6ed feat: lock, unlock, create public, update public v2 dashboard APIs (#11167)
* feat: lock, unlock, create public, update public v2 dashboard APIs

* chore: update api specs

* fix: use new pattern of checking for admin permission

* fix: remove soft delete reference

* chore: revert all frontend changes

* fix: fix build errors and remove v2 create/update public apis

* chore: use v1 methods wherever possible

* fix: use update v2 store method
2026-05-15 13:12:04 +05:30
Naman Verma
85ac805fae fix: fix build errors post merge conflict resolution 2026-05-15 12:21:25 +05:30
Naman Verma
8a0441293a chore: revert all frontend changes 2026-05-15 12:17:44 +05:30
Naman Verma
25ae787ecb Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-update 2026-05-15 12:15:58 +05:30
Naman Verma
f0ed0a8967 feat: v2 dashboard GET API (#11136)
* feat: v2 dashboard GET API

* Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get

* chore: update api specs

* fix: remove soft delete references

* chore: embed StorableDashboard into joinedRow in store method

* fix: fix build error

* chore: revert all frontend changes

* fix: remove public dashboard from get v2 call
2026-05-15 11:26:02 +05:30
Naman Verma
b47343bc09 Merge branch 'main' into nv/v2-dashboard-create 2026-05-15 10:52:06 +05:30
Naman Verma
bb39c52229 feat: follow the metadata+spec key structure in open api spec 2026-05-15 00:36:49 +05:30
Naman Verma
5fe69473c9 feat: follow the metadata+spec key structure 2026-05-15 00:34:12 +05:30
Naman Verma
9be77ace42 chore: change dashboardData to dashboardSpec 2026-05-14 20:23:54 +05:30
Naman Verma
c804d8f9b6 fix: remove sqlstore passage in ee pkg 2026-05-14 20:21:12 +05:30
Naman Verma
996bd949f2 fix: add ctx needed in sqlstore 2026-05-14 20:20:44 +05:30
Naman Verma
84225023a5 chore: rename module to m 2026-05-14 20:04:25 +05:30
Naman Verma
a5b9dd279c chore: move NewDashboardV2 to NewDashboardV2WithoutTags 2026-05-14 20:03:44 +05:30
Naman Verma
172418a337 fix: use binding package to get request 2026-05-14 20:02:46 +05:30
Naman Verma
d1d5a9fa32 fix: use store.RunInTx instead of taking in sqlstore 2026-05-14 20:01:18 +05:30
Naman Verma
6f81e9f364 chore: revert idx generation to resolve conflicts 2026-05-14 19:50:09 +05:30
Naman Verma
1933bec786 Merge branch 'main' into nv/v2-dashboard-create 2026-05-14 19:48:57 +05:30
Naman Verma
6d3d9bfb49 Merge branch 'main' into nv/v2-dashboard-create 2026-05-14 19:46:00 +05:30
Naman Verma
8b89f4af85 chore: remove uploaded grafana flag from metadata 2026-05-14 17:51:10 +05:30
Naman Verma
66e4132504 Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-14 17:49:37 +05:30
Naman Verma
29e14ce9c6 chore: bump migration number 2026-05-14 17:47:34 +05:30
Naman Verma
d7dc789a58 Merge branch 'main' into nv/tags 2026-05-14 17:45:55 +05:30
Naman Verma
d2d129eea9 chore: comment out unique index test 2026-05-14 16:03:21 +05:30
Naman Verma
9e1704615f feat: add created at to tag relations 2026-05-14 15:58:52 +05:30
Naman Verma
db06557c12 chore: comment out unique index test 2026-05-14 15:51:18 +05:30
Naman Verma
1475e2b53a chore: add a todo comment 2026-05-14 15:37:07 +05:30
Naman Verma
e58119a416 chore: comment out tags unique index for now 2026-05-14 15:27:53 +05:30
Naman Verma
3dc5b53cc3 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-13 22:42:32 +05:30
Naman Verma
60fef93100 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-13 22:41:57 +05:30
Naman Verma
f5935ccaf4 Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-13 22:41:17 +05:30
Naman Verma
d354044fbe Merge branch 'main' into nv/tags 2026-05-13 22:40:29 +05:30
Naman Verma
fe6cbc3c0c test: add unit tests for new idx type 2026-05-13 22:39:01 +05:30
Naman Verma
45fa0c739c chore: move tag resolution to module 2026-05-13 17:35:24 +05:30
Naman Verma
e1527dd148 chore: combine functional unique index with unique index 2026-05-13 17:23:22 +05:30
Naman Verma
5063be6467 chore: use tagtypestest package for mock store 2026-05-13 17:09:09 +05:30
Naman Verma
b453655dea chore: rename create method to createOrGet 2026-05-13 16:54:58 +05:30
Naman Verma
08a59145cc fix: use sync tags method in update 2026-05-13 14:29:08 +05:30
Naman Verma
744856680e Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-13 14:23:07 +05:30
Naman Verma
681e205ac1 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-13 14:22:38 +05:30
Naman Verma
4cb27e330e Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-13 14:22:20 +05:30
Naman Verma
b47bc6bcc4 fix: only ascii in regex 2026-05-13 13:51:02 +05:30
Naman Verma
5321e9ee87 feat: functional unique index in sql schema 2026-05-13 13:27:44 +05:30
Naman Verma
dd615869f6 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-13 12:21:59 +05:30
Naman Verma
fc4f326953 fix: use sync tags in create api 2026-05-13 12:21:45 +05:30
Naman Verma
2af4cbf0f9 Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-13 12:11:30 +05:30
Naman Verma
e82f568a27 chore: remove methods that shouldn't be exposed 2026-05-13 12:11:09 +05:30
Naman Verma
c2aac3a278 fix: add regex for tags 2026-05-13 12:00:26 +05:30
Naman Verma
ddfec3e5f7 fix: add len check on tags keys and values 2026-05-13 11:32:31 +05:30
Naman Verma
af623f66e8 chore: bump migration number 2026-05-13 11:23:03 +05:30
Naman Verma
1cdecceece fix: fix build error 2026-05-13 11:00:52 +05:30
Naman Verma
fb4f0a9c63 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-13 10:59:55 +05:30
Naman Verma
268e747f5c fix: fix build error 2026-05-13 10:59:12 +05:30
Naman Verma
3fc72329c9 Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-13 10:56:15 +05:30
Naman Verma
10ecb7524c fix: add ID in tag relation 2026-05-13 10:33:27 +05:30
Naman Verma
8fc21ca6b9 fix: remove user auditable 2026-05-13 10:19:51 +05:30
Naman Verma
422149369d fix: add org id filter in all list and delete queries 2026-05-13 10:11:55 +05:30
Naman Verma
2063697350 chore: change entity id to resource id 2026-05-13 09:41:01 +05:30
Naman Verma
685faa9211 Merge branch 'main' into nv/tags 2026-05-13 09:23:28 +05:30
Naman Verma
de6fcb9fbb chore: bump migration number 2026-05-12 14:19:47 +05:30
Naman Verma
828619a9e6 Merge branch 'main' into nv/tags 2026-05-12 14:19:17 +05:30
Naman Verma
aff2e1be6b fix: fix build errors in dashboard module 2026-05-12 14:13:40 +05:30
Naman Verma
9728d17a0a fix: remove entity type definition 2026-05-12 14:10:29 +05:30
Naman Verma
ffaf334dfd Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-12 14:09:58 +05:30
Naman Verma
d87e7241c0 feat: add SyncTags method that covers creation and linking 2026-05-12 14:09:19 +05:30
Naman Verma
ae184315a9 feat: foreign key on tag id 2026-05-12 13:55:33 +05:30
Naman Verma
29f782a3a0 chore: remove org ID from tag relation 2026-05-12 13:51:14 +05:30
Naman Verma
71d8dafce1 fix: singular table name 2026-05-12 13:45:02 +05:30
Naman Verma
412320d7d9 fix: use coretypes.Kind instead of defining entity type 2026-05-12 13:42:35 +05:30
Naman Verma
9b5d78b5a0 Merge branch 'main' into nv/tags 2026-05-12 13:21:20 +05:30
Naman Verma
444464ae15 fix: created and updated by schema 2026-05-12 13:04:48 +05:30
Naman Verma
d5841f8daa fix: correct pk in bun model for tag relations 2026-05-12 12:29:29 +05:30
Naman Verma
c344cd256f fix: diff error codes for invalid keys and values 2026-05-12 12:27:46 +05:30
Naman Verma
48f4838b93 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-11 23:42:55 +05:30
Naman Verma
ce7735d348 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-11 23:42:37 +05:30
Naman Verma
2f6b7b6260 Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-11 23:42:21 +05:30
Naman Verma
5e61be1606 feat: method to build postable tags from tags 2026-05-11 23:42:08 +05:30
Naman Verma
d8f7e62565 fix: remove soft delete references 2026-05-11 22:47:02 +05:30
Naman Verma
9380569223 fix: compile error fix 2026-05-11 22:42:40 +05:30
Naman Verma
1605b1c1ec Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-11 22:36:00 +05:30
Naman Verma
42660ca8a6 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-11 22:34:22 +05:30
Naman Verma
1f7032953c Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-11 22:34:02 +05:30
Naman Verma
173037d3be fix: extend bun in joinedRow 2026-05-11 22:33:37 +05:30
Naman Verma
e9aab5a618 chore: embed StorableDashboard into joinedRow in store method 2026-05-11 22:32:35 +05:30
Naman Verma
c0113324ca fix: remove soft delete references 2026-05-11 22:17:11 +05:30
Naman Verma
6d59fa4700 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-11 22:13:40 +05:30
Naman Verma
4713fd4839 chore: generate api spec 2026-05-11 14:47:02 +05:30
Naman Verma
4ad872b722 fix: add back api endpoint 2026-05-11 14:24:53 +05:30
Naman Verma
642fb66831 Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-11 14:11:28 +05:30
Naman Verma
d12c846212 chore: change where tag module is instantiated 2026-05-11 14:10:07 +05:30
Naman Verma
4e5bd7cf6f fix: lint fix regarding nil, nil return in test file 2026-05-11 14:06:08 +05:30
Naman Verma
3982cce603 chore: merge conflicts error fixing pt 1 2026-05-11 13:59:41 +05:30
Naman Verma
1a43c85cb8 Merge branch 'nv/tags' into nv/v2-dashboard-create 2026-05-11 13:53:21 +05:30
Naman Verma
bd11e985e1 feat: new module for tags 2026-05-11 13:45:10 +05:30
Naman Verma
abd7e41f97 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-08 18:12:33 +05:30
Naman Verma
3ebde75ebd Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-08 18:11:59 +05:30
Naman Verma
3e849ee2d3 feat: reserved DSL key validation for tags 2026-05-08 18:10:28 +05:30
Naman Verma
f7d9a57637 fix: pass entity type in create many 2026-05-08 17:56:58 +05:30
Naman Verma
7dfa474dc1 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-08 15:49:04 +05:30
Naman Verma
5c223e9b04 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-08 15:49:01 +05:30
Naman Verma
fceb770337 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-08 15:48:58 +05:30
Naman Verma
44496d9d8d feat: entity type column in tags 2026-05-08 15:48:51 +05:30
Naman Verma
3b0fa192d8 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-08 13:38:28 +05:30
Naman Verma
8c44c42e13 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-08 13:38:25 +05:30
Naman Verma
398943fe41 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-08 13:38:22 +05:30
Naman Verma
a17debc61b feat: move tags to key:value pairs model 2026-05-08 13:38:13 +05:30
Naman Verma
0fae729715 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-07 12:06:39 +05:30
Naman Verma
6079e9869c Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-07 12:06:36 +05:30
Naman Verma
3113b82904 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-07 12:06:33 +05:30
Naman Verma
71c60c3f2a test: fix mock interface in test 2026-05-07 12:06:27 +05:30
Naman Verma
3cc2a689c8 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-07 02:25:14 +05:30
Naman Verma
b74f5854fc Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-07 02:25:11 +05:30
Naman Verma
3b824d50a3 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-07 02:25:09 +05:30
Naman Verma
d0a693b034 feat: method to fetch tags for multiple entries at once 2026-05-07 02:24:42 +05:30
Naman Verma
cd7899795d Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-07 02:18:59 +05:30
Naman Verma
ad2d1467ec Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-07 02:18:56 +05:30
Naman Verma
90377f8116 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-07 02:18:54 +05:30
Naman Verma
cabfd7271b Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-05-07 02:18:50 +05:30
Naman Verma
750d63cf6b test: unit test fixes 2026-05-07 02:16:05 +05:30
Naman Verma
44cf8ed8e7 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-06 11:25:41 +05:30
Naman Verma
4d1129c85f Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-06 11:25:39 +05:30
Naman Verma
e4c4acb5df Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-06 11:25:36 +05:30
Naman Verma
c9235cd3d2 Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-05-06 11:22:25 +05:30
Naman Verma
ec837c7006 fix: allow only 1 query in a panel 2026-05-06 11:21:49 +05:30
Naman Verma
59b8fa0e05 chore: update api specs 2026-05-05 16:53:34 +05:30
Naman Verma
133a3a0057 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-05 16:52:12 +05:30
Naman Verma
b4e524dae0 chore: update api specs 2026-05-05 16:51:56 +05:30
Naman Verma
4de0092664 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-05 16:41:18 +05:30
Naman Verma
337d23c91f Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get
# Conflicts:
#	pkg/modules/tag/impltag/module.go
#	pkg/modules/tag/tag.go
#	pkg/types/tagtypes/tag_test.go
2026-05-05 16:41:04 +05:30
Naman Verma
a1f73655ca Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-05 16:39:35 +05:30
Naman Verma
0d6081d0d0 feat: consolidate tag module and tagtypes changes from downstream branches 2026-05-05 16:39:13 +05:30
Naman Verma
2c0c7240a4 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-05 11:56:39 +05:30
Naman Verma
28cb0a8be7 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-05 11:54:54 +05:30
Naman Verma
54832cad34 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-05 11:54:38 +05:30
Naman Verma
a45178d709 Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-05-05 11:54:21 +05:30
Naman Verma
c4224ecf72 Merge branch 'main' into nv/dashboardv2 2026-05-05 11:53:56 +05:30
Naman Verma
8bf650192e Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-04 19:27:14 +05:30
Naman Verma
f8fb7e5f8d Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-04 19:27:02 +05:30
Naman Verma
ff578f7d92 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-04 19:26:49 +05:30
Naman Verma
cd630b1152 Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-05-04 19:26:36 +05:30
Naman Verma
bd0842ac17 fix: query-less panels not allowed 2026-05-04 19:25:49 +05:30
Naman Verma
e37e427079 fix: merge fixes 2026-05-04 17:40:46 +05:30
Naman Verma
1e99ab4659 Merge branch 'nv/v2-dashboard-get' into nv/v2-dashboard-update 2026-05-04 17:40:26 +05:30
Naman Verma
3353cda021 Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-04 17:35:33 +05:30
Naman Verma
f5a71037bf Merge branch 'nv/v2-dashboard-create' into nv/v2-dashboard-get 2026-05-04 17:33:03 +05:30
Naman Verma
97b85c386a fix: no v2 package and its consequences 2026-05-04 17:27:58 +05:30
Naman Verma
00bdf50c1c fix: no v2 package and its consequences 2026-05-04 17:26:12 +05:30
Naman Verma
5dec4ec580 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-04 17:18:39 +05:30
Naman Verma
325767c240 Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-05-04 17:17:32 +05:30
Naman Verma
5fed2a4585 chore: no v2 subpackage 2026-05-04 17:16:39 +05:30
Naman Verma
664337ae0f Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-05-04 16:19:29 +05:30
Naman Verma
a0ea276681 Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-05-04 16:18:03 +05:30
Naman Verma
2dc8699f08 fix: wrap errors 2026-05-04 14:55:38 +05:30
Naman Verma
ed81ed8ab5 fix: no need for copying textboxvariablespec 2026-05-04 14:44:42 +05:30
Naman Verma
48c9da19df fix: return 500 err if spec is nil for composite kind w/ code comment 2026-05-04 14:34:16 +05:30
Naman Verma
eb9663d518 fix: remove extra (un)marshal cycle 2026-05-04 14:18:37 +05:30
Naman Verma
a56a862338 fix: add allowed values in err messages 2026-05-04 14:16:22 +05:30
Naman Verma
021f33f65e Merge branch 'main' into nv/dashboardv2 2026-05-04 12:52:31 +05:30
Naman Verma
f311fcabf7 feat: v2 dashboard update API 2026-04-29 18:39:48 +05:30
Naman Verma
a37c07f881 feat: v2 dashboard GET API 2026-04-29 15:12:47 +05:30
Naman Verma
4d9386f418 fix: merge conflicts 2026-04-29 14:36:39 +05:30
Naman Verma
737473521d Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-04-29 14:33:25 +05:30
Naman Verma
1863db8ba8 Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-04-29 14:32:14 +05:30
Naman Verma
661af09a13 Merge branch 'main' into nv/dashboardv2 2026-04-29 14:31:59 +05:30
Naman Verma
6024fa2b91 fix: remove extra spec from builder query marshalling 2026-04-29 14:31:16 +05:30
Naman Verma
8996a96387 chore: use existing mapper 2026-04-29 14:09:34 +05:30
Naman Verma
d6db5c2aab test: integration test fixes 2026-04-29 12:56:14 +05:30
Naman Verma
709590ea1b test: integration tests for create API 2026-04-29 12:23:12 +05:30
Naman Verma
1add46b4c5 fix: module should also validate postable dashboard 2026-04-28 20:05:38 +05:30
Naman Verma
8401261e20 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-04-28 20:04:33 +05:30
Naman Verma
0ff34a7274 Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-04-28 20:04:08 +05:30
Naman Verma
44e3bd9608 chore: separate method for validation 2026-04-28 20:03:48 +05:30
Naman Verma
c3944d779e fix: more dashboard request validations 2026-04-28 19:59:11 +05:30
Naman Verma
f5ec783a53 fix: go lint fix 2026-04-28 19:33:28 +05:30
Naman Verma
35b729c425 Merge branch 'nv/tags-for-dashboard-create' into nv/v2-dashboard-create 2026-04-28 19:30:42 +05:30
Naman Verma
4f43c3d803 fix: use existing tag's casing if new tag is a prefix of an existing tag 2026-04-28 19:30:07 +05:30
Naman Verma
5dbde6c64d fix: only return name of a tag in dashboard response 2026-04-28 19:13:03 +05:30
Naman Verma
fb6fdd54ec feat: v2 create dashboard API 2026-04-28 15:05:29 +05:30
Naman Verma
64b8ba62da Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-04-28 15:04:11 +05:30
Naman Verma
7c66df408b Merge branch 'main' into nv/dashboardv2 2026-04-28 15:04:03 +05:30
Naman Verma
54049de391 chore: follow proper unmarshal json method structure 2026-04-28 15:02:49 +05:30
Naman Verma
a82f4237c8 Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-04-28 09:52:23 +05:30
Naman Verma
89606b6238 Merge branch 'main' into nv/dashboardv2 2026-04-28 09:52:13 +05:30
Naman Verma
db5ce958eb Merge branch 'nv/dashboardv2' into nv/tags-for-dashboard-create 2026-04-28 09:49:01 +05:30
Naman Verma
c8d3a9a54b feat: enum for entity type that other modules can register 2026-04-28 09:47:24 +05:30
Naman Verma
637870b1fc feat: define tags module for v2 dashboard creation 2026-04-27 22:14:47 +05:30
Naman Verma
d46a7e24c9 Merge branch 'main' into nv/dashboardv2 2026-04-27 22:12:12 +05:30
Naman Verma
2a451e1c31 test: test for drift detection mechanics 2026-04-27 18:57:41 +05:30
Naman Verma
60b6d1d890 chore: better method name extractKindAndSpec 2026-04-27 18:42:31 +05:30
Naman Verma
36f755b232 chore: cleanup comments 2026-04-27 18:39:41 +05:30
Naman Verma
c1b3e3683a chore: code movement 2026-04-27 18:37:57 +05:30
Naman Verma
4c68544b1a chore: go lint fix (godot) 2026-04-27 18:37:05 +05:30
Naman Verma
90d9ab95f9 chore: code movement 2026-04-27 18:35:18 +05:30
Naman Verma
065e712e0c chore: code movement 2026-04-27 18:33:48 +05:30
Naman Verma
50db309ecd chore: code movement 2026-04-27 18:32:41 +05:30
Naman Verma
261bc552b0 chore: cleanup testing code 2026-04-27 18:24:52 +05:30
Naman Verma
bab720e98b Merge branch 'main' into nv/dashboardv2 2026-04-27 18:21:09 +05:30
Naman Verma
71fef6636b chore: better method name 2026-04-27 18:18:14 +05:30
Naman Verma
fc3cdecbbb chore: cleaner comment 2026-04-27 18:15:21 +05:30
Naman Verma
860fcfa641 chore: cleaner comment 2026-04-27 18:14:27 +05:30
Naman Verma
a090e3a4aa chore: cleaner comment 2026-04-27 18:14:02 +05:30
Naman Verma
6cf73e2ade chore: better comment to explain what restrictKindToLiteral does 2026-04-27 18:13:34 +05:30
Naman Verma
bbcb6a45d6 chore: renames and code rearrangement 2026-04-27 17:53:54 +05:30
Naman Verma
d13934febc fix: remove textbox plugin from openapi spec 2026-04-27 17:29:36 +05:30
Naman Verma
d5a7b7523d fix: strict decode variable spec as well 2026-04-27 17:27:51 +05:30
Naman Verma
5b8984f131 Merge branch 'main' into nv/dashboardv2 2026-04-27 17:18:44 +05:30
Naman Verma
6ddc5f1f12 chore: better error messages 2026-04-27 17:18:11 +05:30
Naman Verma
055968bfad fix: dot at the end of a comment 2026-04-27 17:07:58 +05:30
Naman Verma
1bf0f38ed9 fix: js lint errors 2026-04-27 17:07:38 +05:30
Naman Verma
842125e20a chore: too many comments 2026-04-27 16:50:41 +05:30
Naman Verma
6dab35caf8 chore: better file name 2026-04-27 16:43:42 +05:30
Naman Verma
047e9e2001 chore: better file names 2026-04-27 16:42:31 +05:30
Naman Verma
45eaa7db58 test: add tests for spec wrappers 2026-04-27 16:36:27 +05:30
Naman Verma
8a3d894eba chore: comment cleanup 2026-04-27 16:32:29 +05:30
Naman Verma
5239060b53 chore: move plugin maps to correct file 2026-04-27 16:30:33 +05:30
Naman Verma
42c6f507ac test: more descriptive test file name 2026-04-27 15:42:54 +05:30
Naman Verma
1b695a0b80 chore: separate file for perses replicas 2026-04-27 15:42:21 +05:30
Naman Verma
438cfab155 chore: comment clean up 2026-04-27 15:39:46 +05:30
Naman Verma
69f7617e01 Merge branch 'main' into nv/dashboardv2 2026-04-27 15:36:58 +05:30
Naman Verma
4420a7e1fc test: much bigger json for data column 2026-04-24 22:16:03 +05:30
Naman Verma
b4bc68c5c5 test: data column in perf tests should match real data 2026-04-24 17:17:37 +05:30
Naman Verma
eb9eb317cc test: perf test script for both sql flavours 2026-04-23 17:14:33 +05:30
Naman Verma
0b1eb16a42 test: fixes in dashboard perf testing data generator 2026-04-23 15:42:58 +05:30
Naman Verma
05a4d12183 test: script to generate test dashboard data in a sql db 2026-04-23 14:19:58 +05:30
Naman Verma
bbaf64c4f0 feat: openapi spec generation 2026-04-21 13:41:06 +05:30
155 changed files with 4072 additions and 2779 deletions

View File

@@ -445,12 +445,7 @@ authz:
##################### Meter Reporter #####################
meterreporter:
# The interval between collection ticks. Minimum 10m, maximum 24h.
# The interval between collection ticks. Minimum 5m.
interval: 6h
# Whether to backfill sealed days from the license creation day.
backfill: true
# Random jitter applied to the first collect and to every subsequent cycle.
# The first collect fires at a random time in [0, jitter); each cycle then takes
# interval - random(0, jitter). Must be between 10m and interval. Defaults to
# min(interval, 2h) when unset.
jitter: 2h

View File

@@ -2645,6 +2645,42 @@ components:
legend:
$ref: '#/components/schemas/DashboardtypesLegend'
type: object
DashboardtypesJSONPatchDocument:
items:
$ref: '#/components/schemas/DashboardtypesJSONPatchOperation'
nullable: true
type: array
DashboardtypesJSONPatchOperation:
properties:
from:
description: Source JSON Pointer for move/copy ops; ignored for other ops.
type: string
op:
enum:
- add
- remove
- replace
- move
- copy
- test
type: string
path:
description: JSON Pointer (RFC 6901) into the dashboard's postable shape
— e.g. /data/display/name, /data/panels/<id>, /data/panels/<id>/spec/queries/0,
/tags/-.
type: string
value:
description: 'Value to add/replace/test against. The expected type depends
on the path. Common shapes (see referenced schemas for the exact field
set): /data/panels/<id> takes a DashboardtypesPanel; /data/panels/<id>/spec/queries/N
(or /-) takes a DashboardtypesQuery; /data/variables/N takes a DashboardtypesVariable;
/data/layouts/N takes a DashboardtypesLayout; /tags/N (or /-) takes a
TagtypesPostableTag; /data/display/name and other leaf string fields take
a string. Required for add/replace/test; ignored for remove/move/copy.'
required:
- op
- path
type: object
DashboardtypesLayout:
oneOf:
- $ref: '#/components/schemas/DashboardtypesLayoutEnvelopeGithubComPersesPersesPkgModelApiV1DashboardGridLayoutSpec'
@@ -12769,6 +12805,212 @@ paths:
summary: Get dashboard (v2)
tags:
- dashboard
patch:
deprecated: false
description: This endpoint applies an RFC 6902 JSON Patch to a v2-shape dashboard.
The patch is applied against the postable view of the dashboard (metadata,
data, tags), so individual panels, queries, variables, layouts, or tags can
be updated without re-sending the rest of the dashboard. Locked dashboards
are rejected.
operationId: PatchDashboardV2
parameters:
- in: path
name: id
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DashboardtypesJSONPatchDocument'
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/DashboardtypesGettableDashboardV2'
status:
type: string
required:
- status
- data
type: object
description: OK
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- EDITOR
- tokenizer:
- EDITOR
summary: Patch dashboard (v2)
tags:
- dashboard
put:
deprecated: false
description: This endpoint updates a v2-shape dashboard's metadata, data, and
tag set. Locked dashboards are rejected.
operationId: UpdateDashboardV2
parameters:
- in: path
name: id
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/DashboardtypesPostableDashboardV2'
responses:
"200":
content:
application/json:
schema:
properties:
data:
$ref: '#/components/schemas/DashboardtypesGettableDashboardV2'
status:
type: string
required:
- status
- data
type: object
description: OK
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- EDITOR
- tokenizer:
- EDITOR
summary: Update dashboard (v2)
tags:
- dashboard
/api/v2/dashboards/{id}/lock:
delete:
deprecated: false
description: This endpoint unlocks a v2-shape dashboard. Only the dashboard's
creator or an org admin may lock or unlock.
operationId: UnlockDashboardV2
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
"204":
content:
application/json:
schema:
type: string
description: No Content
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- EDITOR
- tokenizer:
- EDITOR
summary: Unlock dashboard (v2)
tags:
- dashboard
put:
deprecated: false
description: This endpoint locks a v2-shape dashboard. Only the dashboard's
creator or an org admin may lock or unlock.
operationId: LockDashboardV2
parameters:
- in: path
name: id
required: true
schema:
type: string
responses:
"204":
content:
application/json:
schema:
type: string
description: No Content
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- EDITOR
- tokenizer:
- EDITOR
summary: Lock dashboard (v2)
tags:
- dashboard
/api/v2/factor_password/forgot:
post:
deprecated: false

View File

@@ -221,6 +221,18 @@ func (module *module) GetV2(ctx context.Context, orgID valuer.UUID, id valuer.UU
return module.pkgDashboardModule.GetV2(ctx, orgID, id)
}
func (module *module) UpdateV2(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, updateable dashboardtypes.UpdateableDashboardV2) (*dashboardtypes.DashboardV2, error) {
return module.pkgDashboardModule.UpdateV2(ctx, orgID, id, updatedBy, updateable)
}
func (module *module) PatchV2(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, patch dashboardtypes.PatchableDashboardV2) (*dashboardtypes.DashboardV2, error) {
return module.pkgDashboardModule.PatchV2(ctx, orgID, id, updatedBy, patch)
}
func (module *module) LockUnlockV2(ctx context.Context, orgID valuer.UUID, id valuer.UUID, updatedBy string, isAdmin bool, lock bool) error {
return module.pkgDashboardModule.LockUnlockV2(ctx, orgID, id, updatedBy, isAdmin, lock)
}
func (module *module) Get(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*dashboardtypes.Dashboard, error) {
return module.pkgDashboardModule.Get(ctx, orgID, id)
}

View File

@@ -17,14 +17,9 @@ const BANNED_COMPONENTS = {
Typography:
'Use @signozhq/ui/typography Typography instead of antd Typography.',
Switch: 'Use @signozhq/ui/switch Switch instead of antd Switch.',
Dropdown:
'Use @signozhq/ui DropdownMenuSimple (or the composable DropdownMenu primitives) from @signozhq/ui/dropdown-menu instead of antd Dropdown.',
Badge: 'Use @signozhq/ui/badge instead of antd Badge.',
Radio:
'Use @signozhq/ui/radio-group RadioGroup (dots) or @signozhq/ui/toggle-group ToggleGroup (segmented buttons) instead of antd Radio.',
Progress: 'Use @signozhq/ui/progress instead of antd Progress.',
Avatar: 'Use @signozhq/ui/avatar instead of antd Avatar.',
Divider: 'Use @signozhq/ui/divider Divider instead of antd Divider.',
};
export default {

View File

@@ -21,6 +21,7 @@ import type {
CreateDashboardV2201,
CreatePublicDashboard201,
CreatePublicDashboardPathParameters,
DashboardtypesJSONPatchDocumentDTO,
DashboardtypesPostableDashboardV2DTO,
DashboardtypesPostablePublicDashboardDTO,
DashboardtypesUpdatablePublicDashboardDTO,
@@ -33,7 +34,13 @@ import type {
GetPublicDashboardPathParameters,
GetPublicDashboardWidgetQueryRange200,
GetPublicDashboardWidgetQueryRangePathParameters,
LockDashboardV2PathParameters,
PatchDashboardV2200,
PatchDashboardV2PathParameters,
RenderErrorResponseDTO,
UnlockDashboardV2PathParameters,
UpdateDashboardV2200,
UpdateDashboardV2PathParameters,
UpdatePublicDashboardPathParameters,
} from '../sigNoz.schemas';
@@ -816,3 +823,360 @@ export const invalidateGetDashboardV2 = async (
return queryClient;
};
/**
* This endpoint applies an RFC 6902 JSON Patch to a v2-shape dashboard. The patch is applied against the postable view of the dashboard (metadata, data, tags), so individual panels, queries, variables, layouts, or tags can be updated without re-sending the rest of the dashboard. Locked dashboards are rejected.
* @summary Patch dashboard (v2)
*/
export const patchDashboardV2 = (
{ id }: PatchDashboardV2PathParameters,
dashboardtypesJSONPatchDocumentDTONull?: BodyType<DashboardtypesJSONPatchDocumentDTO | null> | null,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<PatchDashboardV2200>({
url: `/api/v2/dashboards/${id}`,
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
data: dashboardtypesJSONPatchDocumentDTONull,
signal,
});
};
export const getPatchDashboardV2MutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof patchDashboardV2>>,
TError,
{
pathParams: PatchDashboardV2PathParameters;
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof patchDashboardV2>>,
TError,
{
pathParams: PatchDashboardV2PathParameters;
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
},
TContext
> => {
const mutationKey = ['patchDashboardV2'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof patchDashboardV2>>,
{
pathParams: PatchDashboardV2PathParameters;
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
}
> = (props) => {
const { pathParams, data } = props ?? {};
return patchDashboardV2(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type PatchDashboardV2MutationResult = NonNullable<
Awaited<ReturnType<typeof patchDashboardV2>>
>;
export type PatchDashboardV2MutationBody =
| BodyType<DashboardtypesJSONPatchDocumentDTO | null>
| undefined;
export type PatchDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Patch dashboard (v2)
*/
export const usePatchDashboardV2 = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof patchDashboardV2>>,
TError,
{
pathParams: PatchDashboardV2PathParameters;
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof patchDashboardV2>>,
TError,
{
pathParams: PatchDashboardV2PathParameters;
data?: BodyType<DashboardtypesJSONPatchDocumentDTO | null>;
},
TContext
> => {
return useMutation(getPatchDashboardV2MutationOptions(options));
};
/**
* This endpoint updates a v2-shape dashboard's metadata, data, and tag set. Locked dashboards are rejected.
* @summary Update dashboard (v2)
*/
export const updateDashboardV2 = (
{ id }: UpdateDashboardV2PathParameters,
dashboardtypesPostableDashboardV2DTO?: BodyType<DashboardtypesPostableDashboardV2DTO>,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<UpdateDashboardV2200>({
url: `/api/v2/dashboards/${id}`,
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
data: dashboardtypesPostableDashboardV2DTO,
signal,
});
};
export const getUpdateDashboardV2MutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateDashboardV2>>,
TError,
{
pathParams: UpdateDashboardV2PathParameters;
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
},
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof updateDashboardV2>>,
TError,
{
pathParams: UpdateDashboardV2PathParameters;
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
},
TContext
> => {
const mutationKey = ['updateDashboardV2'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof updateDashboardV2>>,
{
pathParams: UpdateDashboardV2PathParameters;
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
}
> = (props) => {
const { pathParams, data } = props ?? {};
return updateDashboardV2(pathParams, data);
};
return { mutationFn, ...mutationOptions };
};
export type UpdateDashboardV2MutationResult = NonNullable<
Awaited<ReturnType<typeof updateDashboardV2>>
>;
export type UpdateDashboardV2MutationBody =
| BodyType<DashboardtypesPostableDashboardV2DTO>
| undefined;
export type UpdateDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Update dashboard (v2)
*/
export const useUpdateDashboardV2 = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof updateDashboardV2>>,
TError,
{
pathParams: UpdateDashboardV2PathParameters;
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
},
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof updateDashboardV2>>,
TError,
{
pathParams: UpdateDashboardV2PathParameters;
data?: BodyType<DashboardtypesPostableDashboardV2DTO>;
},
TContext
> => {
return useMutation(getUpdateDashboardV2MutationOptions(options));
};
/**
* This endpoint unlocks a v2-shape dashboard. Only the dashboard's creator or an org admin may lock or unlock.
* @summary Unlock dashboard (v2)
*/
export const unlockDashboardV2 = (
{ id }: UnlockDashboardV2PathParameters,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<string>({
url: `/api/v2/dashboards/${id}/lock`,
method: 'DELETE',
signal,
});
};
export const getUnlockDashboardV2MutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof unlockDashboardV2>>,
TError,
{ pathParams: UnlockDashboardV2PathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof unlockDashboardV2>>,
TError,
{ pathParams: UnlockDashboardV2PathParameters },
TContext
> => {
const mutationKey = ['unlockDashboardV2'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof unlockDashboardV2>>,
{ pathParams: UnlockDashboardV2PathParameters }
> = (props) => {
const { pathParams } = props ?? {};
return unlockDashboardV2(pathParams);
};
return { mutationFn, ...mutationOptions };
};
export type UnlockDashboardV2MutationResult = NonNullable<
Awaited<ReturnType<typeof unlockDashboardV2>>
>;
export type UnlockDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Unlock dashboard (v2)
*/
export const useUnlockDashboardV2 = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof unlockDashboardV2>>,
TError,
{ pathParams: UnlockDashboardV2PathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof unlockDashboardV2>>,
TError,
{ pathParams: UnlockDashboardV2PathParameters },
TContext
> => {
return useMutation(getUnlockDashboardV2MutationOptions(options));
};
/**
* This endpoint locks a v2-shape dashboard. Only the dashboard's creator or an org admin may lock or unlock.
* @summary Lock dashboard (v2)
*/
export const lockDashboardV2 = (
{ id }: LockDashboardV2PathParameters,
signal?: AbortSignal,
) => {
return GeneratedAPIInstance<string>({
url: `/api/v2/dashboards/${id}/lock`,
method: 'PUT',
signal,
});
};
export const getLockDashboardV2MutationOptions = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof lockDashboardV2>>,
TError,
{ pathParams: LockDashboardV2PathParameters },
TContext
>;
}): UseMutationOptions<
Awaited<ReturnType<typeof lockDashboardV2>>,
TError,
{ pathParams: LockDashboardV2PathParameters },
TContext
> => {
const mutationKey = ['lockDashboardV2'];
const { mutation: mutationOptions } = options
? options.mutation &&
'mutationKey' in options.mutation &&
options.mutation.mutationKey
? options
: { ...options, mutation: { ...options.mutation, mutationKey } }
: { mutation: { mutationKey } };
const mutationFn: MutationFunction<
Awaited<ReturnType<typeof lockDashboardV2>>,
{ pathParams: LockDashboardV2PathParameters }
> = (props) => {
const { pathParams } = props ?? {};
return lockDashboardV2(pathParams);
};
return { mutationFn, ...mutationOptions };
};
export type LockDashboardV2MutationResult = NonNullable<
Awaited<ReturnType<typeof lockDashboardV2>>
>;
export type LockDashboardV2MutationError = ErrorType<RenderErrorResponseDTO>;
/**
* @summary Lock dashboard (v2)
*/
export const useLockDashboardV2 = <
TError = ErrorType<RenderErrorResponseDTO>,
TContext = unknown,
>(options?: {
mutation?: UseMutationOptions<
Awaited<ReturnType<typeof lockDashboardV2>>,
TError,
{ pathParams: LockDashboardV2PathParameters },
TContext
>;
}): UseMutationResult<
Awaited<ReturnType<typeof lockDashboardV2>>,
TError,
{ pathParams: LockDashboardV2PathParameters },
TContext
> => {
return useMutation(getLockDashboardV2MutationOptions(options));
};

View File

@@ -4636,6 +4636,43 @@ export interface DashboardtypesGettablePublicDashboardDataDTO {
publicDashboard?: DashboardtypesGettablePublicDasbhboardDTO;
}
export enum DashboardtypesJSONPatchOperationDTOOp {
add = 'add',
remove = 'remove',
replace = 'replace',
move = 'move',
copy = 'copy',
test = 'test',
}
export interface DashboardtypesJSONPatchOperationDTO {
/**
* @type string
* @description Source JSON Pointer for move/copy ops; ignored for other ops.
*/
from?: string;
/**
* @enum add,remove,replace,move,copy,test
* @type string
*/
op: DashboardtypesJSONPatchOperationDTOOp;
/**
* @type string
* @description JSON Pointer (RFC 6901) into the dashboard's postable shape — e.g. /data/display/name, /data/panels/<id>, /data/panels/<id>/spec/queries/0, /tags/-.
*/
path: string;
/**
* @description Value to add/replace/test against. The expected type depends on the path. Common shapes (see referenced schemas for the exact field set): /data/panels/<id> takes a DashboardtypesPanel; /data/panels/<id>/spec/queries/N (or /-) takes a DashboardtypesQuery; /data/variables/N takes a DashboardtypesVariable; /data/layouts/N takes a DashboardtypesLayout; /tags/N (or /-) takes a TagtypesPostableTag; /data/display/name and other leaf string fields take a string. Required for add/replace/test; ignored for remove/move/copy.
*/
value?: unknown;
}
/**
* @nullable
*/
export type DashboardtypesJSONPatchDocumentDTO =
| DashboardtypesJSONPatchOperationDTO[]
| null;
export enum DashboardtypesPanelPluginKindDTO {
'signoz/TimeSeriesPanel' = 'signoz/TimeSeriesPanel',
'signoz/BarChartPanel' = 'signoz/BarChartPanel',
@@ -9408,6 +9445,34 @@ export type GetDashboardV2200 = {
status: string;
};
export type PatchDashboardV2PathParameters = {
id: string;
};
export type PatchDashboardV2200 = {
data: DashboardtypesGettableDashboardV2DTO;
/**
* @type string
*/
status: string;
};
export type UpdateDashboardV2PathParameters = {
id: string;
};
export type UpdateDashboardV2200 = {
data: DashboardtypesGettableDashboardV2DTO;
/**
* @type string
*/
status: string;
};
export type UnlockDashboardV2PathParameters = {
id: string;
};
export type LockDashboardV2PathParameters = {
id: string;
};
export type GetFeatures200 = {
/**
* @type array

View File

@@ -1,5 +1,4 @@
import { Breadcrumb } from 'antd';
import { Divider } from '@signozhq/ui/divider';
import { Breadcrumb, Divider } from 'antd';
import styles from './AlertBreadcrumb.module.scss';
import BreadcrumbItem, { BreadcrumbItemConfig } from './BreadcrumbItem';

View File

@@ -1,7 +1,6 @@
import { useState } from 'react';
import { Color, Spacing } from '@signozhq/design-tokens';
import { Drawer } from 'antd';
import { Divider } from '@signozhq/ui/divider';
import { Divider, Drawer } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import { PANEL_TYPES } from 'constants/queryBuilder';

View File

@@ -1,6 +1,5 @@
import { useCallback, useMemo, useState } from 'react';
import { Button, Popover, Tooltip } from 'antd';
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
import { Button, Popover, Radio, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { TelemetryFieldKey } from 'api/v5/v5';
import { useExportRawData } from 'hooks/useDownloadOptionsMenu/useDownloadOptionsMenu';
@@ -64,30 +63,27 @@ 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>
<Radio.Group
value={exportFormat}
onChange={(e): void => setExportFormat(e.target.value)}
>
<Radio value={DownloadFormats.CSV}>csv</Radio>
<Radio value={DownloadFormats.JSONL}>jsonl</Radio>
</Radio.Group>
</div>
<div className="horizontal-line" />
<div className="row-limit">
<Typography.Text className="title">Number of Rows</Typography.Text>
<RadioGroup
value={String(rowLimit)}
onChange={(value): void => setRowLimit(Number(value))}
<Radio.Group
value={rowLimit}
onChange={(e): void => setRowLimit(e.target.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>
<Radio value={DownloadRowCounts.TEN_K}>10k</Radio>
<Radio value={DownloadRowCounts.THIRTY_K}>30k</Radio>
<Radio value={DownloadRowCounts.FIFTY_K}>50k</Radio>
</Radio.Group>
</div>
{dataSource !== DataSource.TRACES && (
@@ -96,12 +92,13 @@ 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>
<Radio.Group
value={columnsScope}
onChange={(e): void => setColumnsScope(e.target.value)}
>
<Radio value={DownloadColumnsScopes.ALL}>All</Radio>
<Radio value={DownloadColumnsScopes.SELECTED}>Selected</Radio>
</Radio.Group>
</div>
</>
)}

View File

@@ -0,0 +1,7 @@
.dropdown-button {
color: var(--l1-foreground);
}
.dropdown-icon {
font-size: 1.2rem;
}

View File

@@ -0,0 +1,51 @@
import { useState } from 'react';
import { Ellipsis } from '@signozhq/icons';
import { Button, Dropdown, MenuProps } from 'antd';
import './DropDown.styles.scss';
function DropDown({
element,
onDropDownItemClick,
}: {
element: JSX.Element[];
onDropDownItemClick?: MenuProps['onClick'];
}): JSX.Element {
const items: MenuProps['items'] = element.map(
(e: JSX.Element, index: number) => ({
label: e,
key: index,
}),
);
const [isDdOpen, setDdOpen] = useState<boolean>(false);
return (
<Dropdown
menu={{
items,
onMouseEnter: (): void => setDdOpen(true),
onMouseLeave: (): void => setDdOpen(false),
onClick: (item): void => onDropDownItemClick?.(item),
}}
open={isDdOpen}
>
<Button
type="link"
className={`dropdown-button`}
onClick={(e): void => {
e.preventDefault();
setDdOpen(true);
}}
>
<Ellipsis className="dropdown-icon" size={16} />
</Button>
</Dropdown>
);
}
DropDown.defaultProps = {
onDropDownItemClick: (): void => {},
};
export default DropDown;

View File

@@ -1,7 +1,15 @@
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 {
Button,
Col,
Dropdown,
MenuProps,
Popover,
Row,
Select,
Space,
} from 'antd';
import { Typography } from '@signozhq/ui/typography';
import axios from 'axios';
import TextToolTip from 'components/TextToolTip';
@@ -233,9 +241,9 @@ function ExplorerCard({
</Popover>
<Share2 onClick={onCopyUrlHandler} size="md" />
{viewKey && (
<DropdownMenuSimple menu={moreOptionMenu}>
<Button type="text" size="small" icon={<Ellipsis size="md" />} />
</DropdownMenuSimple>
<Dropdown trigger={['click']} menu={moreOptionMenu}>
<Ellipsis size="md" />
</Dropdown>
)}
</Space>
</OffSetCol>

View File

@@ -1,8 +1,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 { Button, Input, Radio, RadioChangeEvent } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import { handleContactSupport } from 'container/Integrations/utils';
@@ -102,12 +101,13 @@ function FeedbackModal({ onClose }: { onClose: () => void }): JSX.Element {
return (
<div className="feedback-modal-container">
<div className="feedback-modal-header">
<ToggleGroupSimple
type="single"
<Radio.Group
value={activeTab}
defaultValue={activeTab}
optionType="button"
className="feedback-modal-tabs"
onChange={setActiveTab}
items={items}
options={items}
onChange={(e: RadioChangeEvent): void => setActiveTab(e.target.value)}
/>
</div>
<div className="feedback-modal-content">

View File

@@ -158,23 +158,23 @@
font-weight: var(--font-weight-normal);
}
> button {
.tab {
border: 1px solid var(--l1-border);
width: 114px;
}
&::before {
background: var(--l1-border);
}
.tab::before {
background: var(--l1-border);
}
&[data-state='on'] {
background: var(--l3-background);
color: var(--l1-foreground);
border: 1px solid var(--l1-border);
.selected_view {
background: var(--l3-background);
color: var(--l1-foreground);
border: 1px solid var(--l1-border);
}
&::before {
background: var(--l1-border);
}
}
.selected_view::before {
background: var(--l1-border);
}
}

View File

@@ -4,10 +4,9 @@ import { useSelector } from 'react-redux'; // old code, TODO: fix this correctly
import { useCopyToClipboard, useLocation } from 'react-use';
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 { Divider } from '@signozhq/ui/divider';
import { Divider, Drawer, Radio, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import type { RadioChangeEvent } from 'antd/lib';
import cx from 'classnames';
import { LogType } from 'components/Logs/LogStateIndicator/LogStateIndicator';
import QuerySearch from 'components/QueryBuilderV2/QueryV2/QuerySearch/QuerySearch';
@@ -198,8 +197,8 @@ function LogDetailInner({
const LogJsonData = log ? aggregateAttributesResourcesToString(log) : '';
const handleModeChange = (value: string): void => {
setSelectedView(value as VIEWS);
const handleModeChange = (e: RadioChangeEvent): void => {
setSelectedView(e.target.value);
setIsEdit(false);
setIsFilterVisible(false);
};
@@ -453,50 +452,56 @@ function LogDetailInner({
</div>
<div className="tabs-and-search">
<ToggleGroupSimple
type="single"
<Radio.Group
className="views-tabs"
onChange={handleModeChange}
value={selectedView}
items={[
{
value: VIEW_TYPES.OVERVIEW,
label: (
<div className="view-title">
<Table size={14} />
Overview
</div>
),
},
{
value: VIEW_TYPES.JSON,
label: (
<div className="view-title">
<Braces size={14} />
JSON
</div>
),
},
{
value: VIEW_TYPES.CONTEXT,
label: (
<div className="view-title">
<TextSelect size={14} />
Context
</div>
),
},
{
value: VIEW_TYPES.INFRAMETRICS,
label: (
<div className="view-title">
<Histogram size="md" />
Metrics
</div>
),
},
]}
/>
>
<Radio.Button
className={
selectedView === VIEW_TYPES.OVERVIEW ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.OVERVIEW}
>
<div className="view-title">
<Table size={14} />
Overview
</div>
</Radio.Button>
<Radio.Button
className={
selectedView === VIEW_TYPES.JSON ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.JSON}
>
<div className="view-title">
<Braces size={14} />
JSON
</div>
</Radio.Button>
<Radio.Button
className={
selectedView === VIEW_TYPES.CONTEXT ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.CONTEXT}
>
<div className="view-title">
<TextSelect size={14} />
Context
</div>
</Radio.Button>
<Radio.Button
className={
selectedView === VIEW_TYPES.INFRAMETRICS ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.INFRAMETRICS}
>
<div className="view-title">
<Histogram size="md" />
Metrics
</div>
</Radio.Button>
</Radio.Group>
<div className="log-detail-drawer__actions">
{selectedView === VIEW_TYPES.CONTEXT && (

View File

@@ -18,8 +18,7 @@ import {
RefreshCw,
} from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens';
import { Button, Select } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Button, Checkbox, Select } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
import TextToolTip from 'components/TextToolTip/TextToolTip';
@@ -750,7 +749,7 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
tabIndex={isActive ? 0 : -1}
>
<Checkbox
value={isSelected}
checked={isSelected}
className="option-checkbox"
onClick={(e): void => {
e.stopPropagation();
@@ -1585,7 +1584,7 @@ const CustomMultiSelect: React.FC<CustomMultiSelectProps> = ({
}}
>
<div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<Checkbox value={allOptionsSelected} className="option-checkbox">
<Checkbox checked={allOptionsSelected} className="option-checkbox">
<div className="option-content">
<div className="all-option-text">ALL</div>
</div>

View File

@@ -2,13 +2,6 @@
.query-add-ons {
width: 100%;
.add-on-tab-title {
display: flex;
align-items: center;
justify-content: center;
gap: var(--margin-2);
}
}
.add-ons-list {
@@ -32,7 +25,7 @@
color: var(--l2-foreground);
}
> button {
.tab {
border: 1px solid var(--l1-border);
border-left: none;
min-width: 120px;
@@ -42,21 +35,21 @@
&:first-child {
border-left: 1px solid var(--l1-border);
}
}
&::before {
background: var(--l1-border);
}
.tab::before {
background: var(--l1-border);
}
&[data-state='on'] {
color: var(--text-robin-500);
border: 1px solid var(--l1-border);
.selected-view {
color: var(--text-robin-500);
border: 1px solid var(--l1-border);
display: none;
display: none;
}
&::before {
background: var(--l1-border);
}
}
.selected-view::before {
background: var(--l1-border);
}
}

View File

@@ -1,6 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import { Button, Tooltip } from 'antd';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { Button, Radio, RadioChangeEvent, Tooltip } from 'antd';
import InputWithLabel from 'components/InputWithLabel/InputWithLabel';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { GroupByFilter } from 'container/QueryBuilder/filters/GroupByFilter/GroupByFilter';
@@ -251,7 +250,8 @@ function QueryAddOns({
);
}, [panelType, isListViewPanel, query, showReduceTo]);
const handleOptionClick = (clickedAddOn: AddOn): void => {
const handleOptionClick = (e: RadioChangeEvent): void => {
const clickedAddOn = e.target.value as AddOn;
const isAlreadySelected = selectedViews.some(
(view) => view.key === clickedAddOn.key,
);
@@ -515,27 +515,15 @@ function QueryAddOns({
</div>
)}
<ToggleGroupSimple
type="multiple"
className="add-ons-tabs"
value={selectedViews.map((view) => view.key)}
onChange={(newKeys: string[]): void => {
const oldKeys = selectedViews.map((view) => view.key);
const toggledKey =
newKeys.find((k) => !oldKeys.includes(k)) ??
oldKeys.find((k) => !newKeys.includes(k));
if (!toggledKey) {
return;
}
const clickedAddOn = addOns.find((a) => a.key === toggledKey);
if (clickedAddOn) {
handleOptionClick(clickedAddOn);
}
}}
items={addOns.map((addOn) => ({
value: addOn.key,
label: (
<div className="add-ons-list">
<Radio.Group
className="add-ons-tabs"
onChange={handleOptionClick}
value={selectedViews}
>
{addOns.map((addOn) => (
<Tooltip
key={addOn.key}
title={
<TooltipContent
label={addOn.label}
@@ -546,17 +534,26 @@ function QueryAddOns({
placement="top"
mouseEnterDelay={0.5}
>
<span
className="add-on-tab-title"
data-testid={`query-add-on-${addOn.key}`}
<Radio.Button
className={
selectedViews.find((view) => view.key === addOn.key)
? 'selected-view tab'
: 'tab'
}
value={addOn}
>
{addOn.icon}
{addOn.label}
</span>
<div
className="add-on-tab-title"
data-testid={`query-add-on-${addOn.key}`}
>
{addOn.icon}
{addOn.label}
</div>
</Radio.Button>
</Tooltip>
),
}))}
/>
))}
</Radio.Group>
</div>
</div>
);
}

View File

@@ -6,12 +6,12 @@ import {
useMemo,
useState,
} from 'react';
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
import { Dropdown } from 'antd';
import cx from 'classnames';
import { ENTITY_VERSION_V4, ENTITY_VERSION_V5 } from 'constants/app';
import { PANEL_TYPES } from 'constants/queryBuilder';
import QBEntityOptions from 'container/QueryBuilder/components/QBEntityOptions/QBEntityOptions';
import { QueryProps } from 'container/QueryBuilder/type';
import { QueryProps } from 'container/QueryBuilder/components/Query/Query.interfaces';
import SpanScopeSelector from 'container/QueryBuilder/filters/QueryBuilderSearchV2/SpanScopeSelector';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
@@ -195,7 +195,7 @@ export const QueryV2 = forwardRef(function QueryV2(
)}
{isMultiQueryAllowed && (
<DropdownMenuSimple
<Dropdown
className="query-actions-dropdown"
menu={{
items: [
@@ -217,10 +217,10 @@ export const QueryV2 = forwardRef(function QueryV2(
: []),
],
}}
align="end"
placement="bottomRight"
>
<Ellipsis size={16} />
</DropdownMenuSimple>
</Dropdown>
)}
</div>
</div>

View File

@@ -55,7 +55,6 @@
display: flex;
align-items: center;
gap: 8px;
min-height: 24px;
.checkbox-value-section {
display: flex;

View File

@@ -1,7 +1,6 @@
/* eslint-disable sonarjs/no-identical-functions */
import { Fragment, useMemo, useState } from 'react';
import { Button, Input, Skeleton } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Button, Checkbox, Input, Skeleton } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import cx from 'classnames';
import { removeKeysFromExpression } from 'components/QueryBuilderV2/utils';
@@ -635,12 +634,10 @@ export default function CheckboxFilter(props: ICheckboxProps): JSX.Element {
)}
<div className="value">
<Checkbox
onChange={(checked): void =>
onChange(value, checked === true, false)
}
value={currentFilterState[value]}
onChange={(e): void => onChange(value, e.target.checked, false)}
checked={currentFilterState[value]}
disabled={isFilterDisabled}
className="check-box"
rootClassName="check-box"
/>
<div

View File

@@ -4,14 +4,14 @@ import type {
TableColumnsType as ColumnsType,
TableColumnType as ColumnType,
} from 'antd';
import { Button, Flex } from 'antd';
import { DropdownMenuSimple, type MenuItem } from '@signozhq/ui/dropdown-menu';
import { Button, Dropdown, Flex, MenuProps } from 'antd';
import { Switch } from '@signozhq/ui/switch';
import logEvent from 'api/common/logEvent';
import LaunchChatSupport from 'components/LaunchChatSupport/LaunchChatSupport';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import useUrlQuery from 'hooks/useUrlQuery';
import { SlidersHorizontal } from '@signozhq/icons';
import { popupContainer } from 'utils/selectPopupContainer';
import ResizeTable from './ResizeTable';
import { DynamicColumnTableProps } from './types';
@@ -84,9 +84,8 @@ function DynamicColumnTable({
);
};
const items: MenuItem[] =
const items: MenuProps['items'] =
dynamicColumns?.map((column, index) => ({
key: String(index),
label: (
<div
className="dynamicColumnsTable-items"
@@ -100,6 +99,8 @@ function DynamicColumnTable({
/>
</div>
),
key: index,
type: 'checkbox',
})) || [];
// Get current page from URL or default to 1
@@ -128,14 +129,18 @@ function DynamicColumnTable({
<Flex justify="flex-end" align="center" gap={8}>
{facingIssueBtn && <LaunchChatSupport {...facingIssueBtn} />}
{dynamicColumns && (
<DropdownMenuSimple menu={{ items }}>
<Dropdown
getPopupContainer={popupContainer}
menu={{ items }}
trigger={['click']}
>
<Button
className="dynamicColumnTable-button filter-btn"
size="middle"
icon={<SlidersHorizontal size={14} />}
data-testid="additional-filters-button"
/>
</DropdownMenuSimple>
</Dropdown>
)}
</Flex>

View File

@@ -1,6 +1,5 @@
import { CircleAlert, RefreshCw } from '@signozhq/icons';
import { Select } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Checkbox, Select } from 'antd';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import { useListRoles } from 'api/generated/services/role';
import type { AuthtypesRoleDTO } from 'api/generated/services/sigNoz.schemas';
@@ -147,11 +146,12 @@ function RolesSelect(props: RolesSelectProps): JSX.Element {
options={options}
optionFilterProp="label"
optionRender={(option): JSX.Element => (
<div style={{ pointerEvents: 'none' }}>
<Checkbox value={value.includes(option.value as string)}>
{option.label}
</Checkbox>
</div>
<Checkbox
checked={value.includes(option.value as string)}
style={{ pointerEvents: 'none' }}
>
{option.label}
</Checkbox>
)}
getPopupContainer={getPopupContainer}
disabled={disabled}

View File

@@ -2,7 +2,7 @@ import type { Control, UseFormRegister } from 'react-hook-form';
import { Controller } from 'react-hook-form';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/ui/toggle-group';
import { DatePicker } from 'antd';
import AuthZTooltip from 'components/AuthZTooltip/AuthZTooltip';
import {
@@ -60,21 +60,30 @@ function KeyFormPhase({
name="expiryMode"
control={control}
render={({ field }): JSX.Element => (
<ToggleGroupSimple
<ToggleGroup
type="single"
value={field.value}
onChange={(val: string): void => {
onChange={(val): void => {
if (val) {
field.onChange(val);
}
}}
size="sm"
className="add-key-modal__expiry-toggle"
items={[
{ value: ExpiryMode.NONE, label: 'No Expiration' },
{ value: ExpiryMode.DATE, label: 'Set Expiration Date' },
]}
/>
>
<ToggleGroupItem
value={ExpiryMode.NONE}
className="add-key-modal__expiry-toggle-btn"
>
No Expiration
</ToggleGroupItem>
<ToggleGroupItem
value={ExpiryMode.DATE}
className="add-key-modal__expiry-toggle-btn"
>
Set Expiration Date
</ToggleGroupItem>
</ToggleGroup>
)}
/>
</div>

View File

@@ -4,7 +4,7 @@ import { LockKeyhole, Trash2, X } from '@signozhq/icons';
import { Badge } from '@signozhq/ui/badge';
import { Button } from '@signozhq/ui/button';
import { Input } from '@signozhq/ui/input';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/ui/toggle-group';
import { DatePicker } from 'antd';
import type { ServiceaccounttypesGettableFactorAPIKeyDTO } from 'api/generated/services/sigNoz.schemas';
import AuthZTooltip from 'components/AuthZTooltip/AuthZTooltip';
@@ -101,22 +101,31 @@ function EditKeyForm({
name="expiryMode"
control={control}
render={({ field }): JSX.Element => (
<ToggleGroupSimple
<ToggleGroup
type="single"
value={field.value}
onChange={(val: string): void => {
onChange={(val): void => {
if (val && canUpdate) {
field.onChange(val);
}
}}
size="sm"
disabled={!canUpdate}
className="edit-key-modal__expiry-toggle"
items={[
{ value: ExpiryMode.NONE, label: 'No Expiration' },
{ value: ExpiryMode.DATE, label: 'Set Expiration Date' },
]}
/>
>
<ToggleGroupItem
value={ExpiryMode.NONE}
disabled={!canUpdate}
className="edit-key-modal__expiry-toggle-btn"
>
No Expiration
</ToggleGroupItem>
<ToggleGroupItem
value={ExpiryMode.DATE}
disabled={!canUpdate}
className="edit-key-modal__expiry-toggle-btn"
>
Set Expiration Date
</ToggleGroupItem>
</ToggleGroup>
)}
/>
</div>

View File

@@ -4,7 +4,7 @@ import { Key, LayoutGrid, Plus, Trash2, X } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { DrawerWrapper } from '@signozhq/ui/drawer';
import { toast } from '@signozhq/ui/sonner';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/ui/toggle-group';
import { Pagination, Skeleton } from 'antd';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
import {
@@ -395,11 +395,11 @@ function ServiceAccountDrawer({
const drawerContent = (
<div className="sa-drawer__layout">
<div className="sa-drawer__tabs">
<ToggleGroupSimple
<ToggleGroup
type="single"
value={activeTab}
size="sm"
onChange={(val: string): void => {
onChange={(val): void => {
if (val) {
void setActiveTab(val as ServiceAccountDrawerTab);
if (val !== ServiceAccountDrawerTab.Keys) {
@@ -409,30 +409,25 @@ function ServiceAccountDrawer({
}
}}
className="sa-drawer__tab-group"
items={[
{
value: ServiceAccountDrawerTab.Overview,
label: (
<>
<LayoutGrid size={14} />
Overview
</>
),
},
{
value: ServiceAccountDrawerTab.Keys,
label: (
<>
<Key size={14} />
Keys
{keys.length > 0 && (
<span className="sa-drawer__tab-count">{keys.length}</span>
)}
</>
),
},
]}
/>
>
<ToggleGroupItem
value={ServiceAccountDrawerTab.Overview}
className="sa-drawer__tab"
>
<LayoutGrid size={14} />
Overview
</ToggleGroupItem>
<ToggleGroupItem
value={ServiceAccountDrawerTab.Keys}
className="sa-drawer__tab"
>
<Key size={14} />
Keys
{keys.length > 0 && (
<span className="sa-drawer__tab-count">{keys.length}</span>
)}
</ToggleGroupItem>
</ToggleGroup>
{activeTab === ServiceAccountDrawerTab.Keys && (
<AuthZTooltip
checks={[

View File

@@ -1,6 +1,12 @@
.signoz-radio-group {
.signoz-radio-group.ant-radio-group {
color: var(--l2-foreground);
&.ant-radio-group-disabled {
opacity: 0.5;
pointer-events: none;
cursor: not-allowed;
}
.view-title {
display: flex;
gap: var(--margin-2);
@@ -24,41 +30,43 @@
}
}
> button {
.tab {
border: 1px solid var(--l1-border);
background: var(--l1-background);
&:hover {
color: var(--l1-foreground);
}
&::before {
background: var(--l1-border);
}
}
&[data-state='on'] {
&,
&:hover {
background: var(--l3-background);
color: var(--l1-foreground);
border: 1px solid var(--l1-border);
}
&::before {
background: var(--l3-background);
border-left: 1px solid var(--l1-border);
}
.selected_view {
&,
&:hover {
background: var(--l3-background);
color: var(--l1-foreground);
border: 1px solid var(--l1-border);
}
&::before {
background: var(--l3-background);
border-left: 1px solid var(--l1-border);
}
}
&[disabled],
&[data-disabled] {
&.ant-radio-group-disabled {
.tab,
.selected_view {
background: var(--l2-background) !important;
border-color: var(--l1-border) !important;
color: var(--l1-foreground) !important;
}
&:hover {
background: var(--l2-background) !important;
border-color: var(--l1-border) !important;
color: var(--l1-foreground) !important;
}
.tab:hover,
.selected_view:hover {
background: var(--l2-background) !important;
border-color: var(--l1-border) !important;
color: var(--l1-foreground) !important;
}
}
}

View File

@@ -1,4 +1,4 @@
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { Radio, RadioChangeEvent } from 'antd';
import './SignozRadioGroup.styles.scss';
@@ -11,7 +11,7 @@ interface Option {
interface SignozRadioGroupProps {
value: string;
options: Option[];
onChange: (value: string) => void;
onChange: (e: RadioChangeEvent) => void;
className?: string;
disabled?: boolean;
}
@@ -24,22 +24,26 @@ function SignozRadioGroup({
disabled = false,
}: SignozRadioGroupProps): JSX.Element {
return (
<ToggleGroupSimple
type="single"
<Radio.Group
value={value}
buttonStyle="solid"
className={`signoz-radio-group ${className}`}
onChange={onChange}
disabled={disabled}
items={options.map((option) => ({
value: option.value,
label: (
>
{options.map((option) => (
<Radio.Button
key={option.value}
value={option.value}
className={value === option.value ? 'selected_view tab' : 'tab'}
>
<div className="view-title-container">
{option.icon && <div className="icon-container">{option.icon}</div>}
{option.label}
</div>
),
}))}
/>
</Radio.Button>
))}
</Radio.Group>
);
}

View File

@@ -4,10 +4,13 @@ import {
ButtonProps,
Col,
ColProps,
Divider,
DividerProps,
Row,
RowProps,
Space,
SpaceProps,
TabsProps,
} from 'antd';
import {
Typography,
@@ -31,11 +34,21 @@ const StyledRow = styled(Row)<TStyledRow>`
${styledClass}
`;
type TStyledDivider = DividerProps & IStyledClass;
const StyledDivider = styled(Divider)<TStyledDivider>`
${styledClass}
`;
type TStyledSpace = SpaceProps & IStyledClass;
const StyledSpace = styled(Space)<TStyledSpace>`
${styledClass}
`;
type TStyledTabs = TabsProps & IStyledClass;
const StyledTabs = styled(Divider)<TStyledTabs>`
${styledClass}
`;
type TStyledButton = ButtonProps & IStyledClass;
const StyledButton = styled(Button)<TStyledButton>`
${styledClass}
@@ -66,7 +79,9 @@ export {
StyledButton,
StyledCol,
StyledDiv,
StyledDivider,
StyledRow,
StyledSpace,
StyledTabs,
StyledTypography,
};

View File

@@ -1,7 +1,6 @@
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react';
import { ChevronDown, Globe } from '@signozhq/icons';
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
import { Button } from 'antd';
import { Button, Dropdown } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import TimeItems, {
timePreferance,
@@ -28,17 +27,20 @@ function TimePreference({
const menu = useMemo(
() => ({
items: menuItems.map((item) => ({
...item,
onClick: timeMenuItemOnChangeHandler,
})),
items: menuItems,
onClick: timeMenuItemOnChangeHandler,
}),
[timeMenuItemOnChangeHandler],
);
return (
<DropdownMenuSimple menu={menu} className="time-selection-menu">
<Button className="time-selection-target">
<Dropdown
menu={menu}
rootClassName="time-selection-menu"
className="time-selection-target"
trigger={['click']}
>
<Button>
<div className="button-selected-text">
<Globe size={14} />
<Typography.Text className="selected-value">
@@ -47,7 +49,7 @@ function TimePreference({
</div>
<ChevronDown size="md" />
</Button>
</DropdownMenuSimple>
</Dropdown>
);
}

View File

@@ -10,7 +10,7 @@ import {
DialogSubtitle,
DialogTitle,
} from '@signozhq/ui/dialog';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/ui/toggle-group';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import type {
ApprovalEventDTO,
@@ -132,43 +132,45 @@ export default function ApprovalCard({
<div className={styles.diffModalBody}>
<p className={styles.diffModalSummary}>{approval.summary}</p>
<div className={styles.diffToolbarRow}>
<ToggleGroupSimple
<ToggleGroup
type="single"
size="sm"
value={viewMode}
onChange={(next: string): void => {
// Radix `single` group can emit '' when the active item
// is clicked again — preserve the current mode.
onChange={(next): void => {
// Radix `single` group can emit '' when the active item is clicked again.
if (next === 'split' || next === 'unified') {
setViewMode(next);
}
}}
items={[
{
value: 'split',
'aria-label': 'Split view',
label: <Columns2 size={12} />,
},
{
value: 'unified',
'aria-label': 'Unified view',
label: <List size={12} />,
},
]}
/>
<ToggleGroupSimple
>
<TooltipSimple title="Split view">
<ToggleGroupItem value="split" aria-label="Split view">
<Columns2 size={12} />
</ToggleGroupItem>
</TooltipSimple>
<TooltipSimple title="Unified view">
<ToggleGroupItem value="unified" aria-label="Unified view">
<List size={12} />
</ToggleGroupItem>
</TooltipSimple>
</ToggleGroup>
<ToggleGroup
type="multiple"
size="sm"
value={wrapText ? ['wrap'] : []}
onChange={(next: string[]): void => setWrapText(next.includes('wrap'))}
items={[
{
value: 'wrap',
'aria-label': wrapText ? 'Disable text wrap' : 'Wrap long lines',
label: <WrapText size={12} />,
},
]}
/>
onChange={(next): void => setWrapText(next.includes('wrap'))}
>
<TooltipSimple
title={wrapText ? 'Disable text wrap' : 'Wrap long lines'}
>
<ToggleGroupItem
value="wrap"
aria-label={wrapText ? 'Disable text wrap' : 'Wrap long lines'}
>
<WrapText size={12} />
</ToggleGroupItem>
</TooltipSimple>
</ToggleGroup>
</div>
{approval.diff && (
<DiffView

View File

@@ -2,8 +2,7 @@ import { useState } from 'react';
import cx from 'classnames';
import { Button } from '@signozhq/ui/button';
import logEvent from 'api/common/logEvent';
import { Checkbox } from '@signozhq/ui/checkbox';
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
import { Checkbox, Radio } from 'antd';
import { AIAssistantEvents } from '../../../events';
import { useAIAssistantAnalyticsContext } from '../../../hooks/useAIAssistantAnalyticsContext';
@@ -82,43 +81,31 @@ export default function InteractiveQuestion({
{question && <p className={blockStyles.title}>{question}</p>}
{type === 'radio' ? (
<RadioGroup
<Radio.Group
className={styles.options}
onChange={(value): void => {
setSelected([value]);
handleSubmit([value]);
onChange={(e): void => {
setSelected([e.target.value]);
handleSubmit([e.target.value]);
}}
>
{normalized.map((opt) => (
<RadioGroupItem
key={opt.value}
value={opt.value}
className={styles.option}
>
<Radio key={opt.value} value={opt.value} className={styles.option}>
{opt.label}
</RadioGroupItem>
</Radio>
))}
</RadioGroup>
</Radio.Group>
) : (
<>
<div className={cx(styles.options, styles.checkbox)}>
<Checkbox.Group
className={cx(styles.options, styles.checkbox)}
onChange={(vals): void => setSelected(vals as string[])}
>
{normalized.map((opt) => (
<Checkbox
key={opt.value}
value={selected.includes(opt.value)}
onChange={(checked): void => {
setSelected((prev) =>
checked === true
? [...prev, opt.value]
: prev.filter((v) => v !== opt.value),
);
}}
className={styles.option}
>
<Checkbox key={opt.value} value={opt.value} className={styles.option}>
{opt.label}
</Checkbox>
))}
</div>
</Checkbox.Group>
<Button
variant="solid"
size="sm"

View File

@@ -1,6 +1,5 @@
import { useEffect, useRef, useState } from 'react';
import { Input } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Checkbox, Input } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { useIsDarkMode } from 'hooks/useDarkMode';
import useDebouncedFn from 'hooks/useDebouncedFunction';
@@ -321,8 +320,10 @@ function AnomalyAlertEvaluationView({
{filteredSeriesKeys.length > 0 && (
<Checkbox
className="anomaly-alert-evaluation-view-series-list-item"
type="checkbox"
name="series"
value={selectedSeries === null}
value="all"
checked={selectedSeries === null}
onChange={(): void => handleSeriesChange(null)}
>
Show All
@@ -334,8 +335,10 @@ function AnomalyAlertEvaluationView({
<Checkbox
className="anomaly-alert-evaluation-view-series-list-item"
key={seriesKey}
type="checkbox"
name="series"
value={selectedSeries === seriesKey}
value={seriesKey}
checked={selectedSeries === seriesKey}
onChange={(): void => handleSeriesChange(seriesKey)}
>
<div

View File

@@ -161,6 +161,41 @@
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--l1-border);
.views-tabs {
color: var(--l2-foreground);
.view-title {
display: flex;
gap: var(--margin-2);
align-items: center;
justify-content: center;
font-size: 14px;
font-style: normal;
font-weight: var(--font-weight-normal);
}
.tab {
padding: 0 32px;
background: var(--l3-background);
border: 1px solid var(--l1-border);
width: auto;
}
.tab::before {
background: var(--l1-border);
}
.selected_view {
background: none;
border: 1px solid var(--l1-border);
border-bottom: none;
}
.selected_view::before {
background: var(--l1-border);
}
}
}
}
@@ -664,6 +699,40 @@
left: 50%;
transform: translate(-50%, -50%);
}
.views-tabs {
color: var(--l2-foreground);
.ant-btn {
box-shadow: none;
position: relative;
}
.tab {
border: 1px solid var(--l1-border);
width: 114px;
z-index: 1;
}
.tab:hover {
z-index: 3;
}
.tab::before {
background: var(--l2-background);
}
.selected_view {
background: var(--l1-border);
color: var(--l1-foreground);
border: 1px solid var(--l1-border);
z-index: 2;
}
.selected_view::before {
background: var(--l2-background);
}
}
}
.top-services-content {

View File

@@ -2,10 +2,9 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
// eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux';
import { Spacing } from '@signozhq/design-tokens';
import { Button, Drawer } from 'antd';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { Divider } from '@signozhq/ui/divider';
import { Button, Divider, Drawer, Radio } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import type { RadioChangeEvent } from 'antd/lib';
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
import {
CustomTimeType,
@@ -56,9 +55,9 @@ function DomainDetails({
const [initialFiltersEndPointStats, setInitialFiltersEndPointStats] =
useState<IBuilderQuery['filters']>(domainListFilters);
const handleTabChange = (value: string): void => {
setSelectedView(value as VIEWS);
setParams({ selectedView: value });
const handleTabChange = (e: RadioChangeEvent): void => {
setSelectedView(e.target.value);
setParams({ selectedView: e.target.value });
};
const handleEndPointChange = (name: string): void => {
@@ -225,17 +224,38 @@ function DomainDetails({
timeRange={modalTimeRange}
/>
<div className="views-tabs-container">
<ToggleGroupSimple
type="single"
<Radio.Group
className="views-tabs"
onChange={handleTabChange}
value={selectedView}
size="lg"
items={[
{ value: VIEW_TYPES.ALL_ENDPOINTS, label: 'All Endpoints' },
{ value: VIEW_TYPES.ENDPOINT_STATS, label: 'Endpoint(s) Stats' },
{ value: VIEW_TYPES.TOP_ERRORS, label: 'Top 10 Errors' },
]}
/>
>
<Radio.Button
className={
selectedView === VIEW_TYPES.ALL_ENDPOINTS ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.ALL_ENDPOINTS}
>
<div className="view-title">All Endpoints</div>
</Radio.Button>
<Radio.Button
className={
selectedView === VIEW_TYPES.ENDPOINT_STATS
? 'tab selected_view'
: 'tab'
}
value={VIEW_TYPES.ENDPOINT_STATS}
>
<div className="view-title">Endpoint(s) Stats</div>
</Radio.Button>
<Radio.Button
className={
selectedView === VIEW_TYPES.TOP_ERRORS ? 'tab selected_view' : 'tab'
}
value={VIEW_TYPES.TOP_ERRORS}
>
<div className="view-title">Top 10 Errors</div>
</Radio.Button>
</Radio.Group>
</div>
{selectedView === VIEW_TYPES.ALL_ENDPOINTS && (
<AllEndPoints

View File

@@ -21,17 +21,14 @@ import { useResizeObserver } from 'hooks/useDimensions';
import { useNotifications } from 'hooks/useNotifications';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { LegendPosition } from 'lib/uPlotV2/components/types';
import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils';
import { useTimezone } from 'providers/Timezone';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import ErrorState from './ErrorState';
import {
getStepIntervalForQuery,
getTracesTimeRangeFromStepInterval,
prepareStatusCodeBarChartsConfig,
} from './utils';
import { prepareStatusCodeBarChartsConfig } from './utils';
function StatusCodeBarCharts({
endPointStatusCodeBarChartsDataQuery,
@@ -138,18 +135,6 @@ function StatusCodeBarCharts({
[domainName, filters],
);
const activeApiResponse = useMemo(
() =>
currentWidgetInfoIndex === 0
? formattedEndPointStatusCodeBarChartsDataPayload
: formattedEndPointStatusCodeLatencyBarChartsDataPayload,
[
currentWidgetInfoIndex,
formattedEndPointStatusCodeBarChartsDataPayload,
formattedEndPointStatusCodeLatencyBarChartsDataPayload,
],
);
const graphClickHandler = useCallback(
(
xValue: number,
@@ -159,14 +144,11 @@ function StatusCodeBarCharts({
metric?: { [key: string]: string },
queryData?: { queryName: string; inFocusOrNot: boolean },
): void => {
const TWO_AND_HALF_MINUTES_IN_MILLISECONDS = 2.5 * 60 * 1000; // 150,000 milliseconds
const customFilters = getCustomFiltersForBarChart(metric);
const stepInterval = getStepIntervalForQuery(
activeApiResponse,
queryData?.queryName,
);
const { start, end } = getTracesTimeRangeFromStepInterval(
const { start, end } = getStartAndEndTimesInMilliseconds(
xValue,
stepInterval,
TWO_AND_HALF_MINUTES_IN_MILLISECONDS,
);
handleGraphClick({
@@ -189,7 +171,6 @@ function StatusCodeBarCharts({
});
},
[
activeApiResponse,
widget,
navigateToExplorerPages,
navigateToExplorer,

View File

@@ -1,68 +0,0 @@
import {
getMinStepIntervalFromApiResponse,
getStepIntervalForQuery,
getTracesTimeRangeFromStepInterval,
} from '../utils';
describe('StatusCodeBarCharts utils', () => {
describe('getTracesTimeRangeFromStepInterval', () => {
const xValue = 1609459200; // seconds
it('keeps start at click time with a minimum 5 minute end range', () => {
const { start, end } = getTracesTimeRangeFromStepInterval(xValue, 60);
expect(start).toBe(xValue * 1000);
expect(end - start).toBe(5 * 60 * 1000);
expect(end).toBe(xValue * 1000 + 5 * 60 * 1000);
});
it('extends end when step interval is larger than 5 minutes', () => {
const stepInterval = 600; // 10 minutes
const { start, end } = getTracesTimeRangeFromStepInterval(
xValue,
stepInterval,
);
expect(start).toBe(xValue * 1000);
expect(end - start).toBe(10 * 60 * 1000);
expect(end).toBe(xValue * 1000 + 10 * 60 * 1000);
});
});
describe('getMinStepIntervalFromApiResponse', () => {
it('returns 60 when step intervals are missing', () => {
expect(getMinStepIntervalFromApiResponse({} as any)).toBe(60);
});
it('returns the minimum step interval from the response', () => {
const apiResponse = {
data: {
newResult: {
meta: {
stepIntervals: { A: 120, B: 60 },
},
},
},
};
expect(getMinStepIntervalFromApiResponse(apiResponse as any)).toBe(60);
});
});
describe('getStepIntervalForQuery', () => {
it('returns query-specific step interval when available', () => {
const apiResponse = {
data: {
newResult: {
meta: {
stepIntervals: { A: 120, B: 60 },
},
},
},
};
expect(getStepIntervalForQuery(apiResponse as any, 'A')).toBe(120);
expect(getStepIntervalForQuery(apiResponse as any, 'B')).toBe(60);
});
});
});

View File

@@ -13,65 +13,6 @@ import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { QueryData } from 'types/api/widgets/getQuery';
import { v4 } from 'uuid';
const DEFAULT_STEP_INTERVAL_SECONDS = 60;
const MIN_TRACES_TIME_RANGE_MINUTES = 5;
export function getMinStepIntervalFromApiResponse(
apiResponse: MetricRangePayloadProps,
): number {
const stepIntervals: ExecStats['stepIntervals'] = get(
apiResponse,
'data.newResult.meta.stepIntervals',
{},
);
const values = Object.values(stepIntervals).filter(
(value): value is number =>
typeof value === 'number' && Number.isFinite(value),
);
if (values.length === 0) {
return DEFAULT_STEP_INTERVAL_SECONDS;
}
return Math.min(...values);
}
export function getStepIntervalForQuery(
apiResponse: MetricRangePayloadProps,
queryName?: string,
): number {
const minStepInterval = getMinStepIntervalFromApiResponse(apiResponse);
if (!queryName) {
return minStepInterval;
}
const stepIntervals: ExecStats['stepIntervals'] = get(
apiResponse,
'data.newResult.meta.stepIntervals',
{},
);
return get(stepIntervals, queryName, minStepInterval) ?? minStepInterval;
}
export function getTracesTimeRangeFromStepInterval(
xValue: number,
stepIntervalSeconds: number,
): { start: number; end: number } {
const rangeMinutes = Math.max(
stepIntervalSeconds / 60,
MIN_TRACES_TIME_RANGE_MINUTES,
);
const rangeMs = rangeMinutes * 60 * 1000;
const start = Math.floor(xValue * 1000);
return {
start,
end: Math.ceil(start + rangeMs),
};
}
export const prepareStatusCodeBarChartsConfig = ({
timezone,
isDarkMode,
@@ -100,7 +41,7 @@ export const prepareStatusCodeBarChartsConfig = ({
'data.newResult.meta.stepIntervals',
{},
);
const minStepInterval = getMinStepIntervalFromApiResponse(apiResponse);
const minStepInterval = Math.min(...Object.values(stepIntervals));
const config = buildBaseConfig({
id: v4(),

View File

@@ -11,13 +11,8 @@ import {
} from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { Callout } from '@signozhq/ui/callout';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from '@signozhq/ui/dropdown-menu';
import { toast } from '@signozhq/ui/sonner';
import { Skeleton } from 'antd';
import { Dropdown, Skeleton } from 'antd';
import {
RenderErrorResponseDTO,
ZeustypesHostDTO,
@@ -205,15 +200,10 @@ export default function CustomDomainSettings(): JSX.Element {
!workspaceName ? 'workspace-name-hidden' : ''
}`}
>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="link" color="none" disabled={isFetchingHosts}>
<Link2 size={12} />
<span>{stripProtocol(activeHost?.url ?? '')}</span>
<ChevronDown size={12} />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="start">
<Dropdown
trigger={['click']}
disabled={isFetchingHosts}
dropdownRender={(): JSX.Element => (
<div className="workspace-url-dropdown">
<span className="workspace-url-dropdown-header">
All Workspace URLs
@@ -246,8 +236,14 @@ export default function CustomDomainSettings(): JSX.Element {
);
})}
</div>
</DropdownMenuContent>
</DropdownMenu>
)}
>
<Button variant="link" color="none">
<Link2 size={12} />
<span>{stripProtocol(activeHost?.url ?? '')}</span>
<ChevronDown size={12} />
</Button>
</Dropdown>
<span className="custom-domain-card-meta-timezone">
<Clock size={11} />
{timezone.offset}

View File

@@ -1,5 +1,4 @@
import { GetHosts200 } from 'api/generated/services/sigNoz.schemas';
import userEvent from '@testing-library/user-event';
import { rest, server } from 'mocks-server/server';
import { fireEvent, render, screen, waitFor } from 'tests/test-utils';
@@ -143,13 +142,12 @@ describe('CustomDomainSettings', () => {
});
it('shows all workspace URLs as links in the dropdown', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(<CustomDomainSettings />);
await screen.findByText(/custom-host\.test\.cloud/i);
// Open the URL dropdown
await user.click(
fireEvent.click(
screen.getByRole('button', { name: /custom-host\.test\.cloud/i }),
);

View File

@@ -1,7 +1,6 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Col, Input, Select, Space, Tooltip } from 'antd';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { Col, Input, Radio, Select, Space, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import AddTags from 'container/DashboardContainer/DashboardSettings/General/AddTags';
import { useDashboardCursorSyncMode } from 'hooks/dashboard/useDashboardCursorSyncMode';
@@ -214,18 +213,18 @@ function GeneralDashboardSettings(): JSX.Element {
Sync crosshair and tooltip across all the dashboard panels
</Typography.Text>
</div>
<ToggleGroupSimple
type="single"
<Radio.Group
value={cursorSyncMode}
onChange={(value: string): void => {
setCursorSyncMode(value as DashboardCursorSync);
onChange={(e): void => {
setCursorSyncMode(e.target.value as DashboardCursorSync);
}}
items={[
{ value: DashboardCursorSync.None, label: 'No Sync' },
{ value: DashboardCursorSync.Crosshair, label: 'Crosshair' },
{ value: DashboardCursorSync.Tooltip, label: 'Tooltip' },
]}
/>
>
<Radio.Button value={DashboardCursorSync.None}>No Sync</Radio.Button>
<Radio.Button value={DashboardCursorSync.Crosshair}>
Crosshair
</Radio.Button>
<Radio.Button value={DashboardCursorSync.Tooltip}>Tooltip</Radio.Button>
</Radio.Group>
</div>
{cursorSyncMode === DashboardCursorSync.Tooltip && (
<div className={styles.crossPanelSyncRow}>
@@ -238,21 +237,21 @@ function GeneralDashboardSettings(): JSX.Element {
matching ones highlighted
</Typography.Text>
</div>
<ToggleGroupSimple
type="single"
<Radio.Group
value={syncTooltipFilterMode}
onChange={(value: string): void => {
onChange={(e): void => {
logEvent(Events.TOOLTIP_SYNC_MODE_CHANGED, {
path: getAbsoluteUrl(window.location.pathname),
mode: value,
mode: e.target.value,
});
setSyncTooltipFilterMode(value as SyncTooltipFilterMode);
setSyncTooltipFilterMode(e.target.value as SyncTooltipFilterMode);
}}
items={[
{ value: SyncTooltipFilterMode.All, label: 'All' },
{ value: SyncTooltipFilterMode.Filtered, label: 'Filtered' },
]}
/>
>
<Radio.Button value={SyncTooltipFilterMode.All}>All</Radio.Button>
<Radio.Button value={SyncTooltipFilterMode.Filtered}>
Filtered
</Radio.Button>
</Radio.Group>
</div>
)}
</Col>

View File

@@ -395,8 +395,8 @@ describe('Dynamic Variable Default Behavior', () => {
// Check if the checkbox exists (it should be unchecked initially)
const checkbox = allOptionContainer?.querySelector(
'[role="checkbox"]',
) as HTMLElement;
'input[type="checkbox"]',
) as HTMLInputElement;
expect(checkbox).toBeInTheDocument();
// Should call onValueUpdate with all values (ALL selection)
@@ -516,10 +516,10 @@ describe('Dynamic Variable Default Behavior', () => {
// Check if the checkbox for ALL option is checked
const checkbox = dropdownAllOption.querySelector(
'[role="checkbox"]',
) as HTMLElement;
'input[type="checkbox"]',
) as HTMLInputElement;
expect(checkbox).toBeInTheDocument();
expect(checkbox).toHaveAttribute('data-state', 'checked');
expect(checkbox.checked).toBe(true);
});
});
});

View File

@@ -196,10 +196,10 @@ describe('Panel Management Tests', () => {
expect(allOption).toHaveClass('selected');
const allCheckbox = allOption.querySelector(
'[role="checkbox"]',
) as HTMLElement;
'input[type="checkbox"]',
) as HTMLInputElement;
expect(allCheckbox).toBeInTheDocument();
expect(allCheckbox).toHaveAttribute('data-state', 'checked');
expect(allCheckbox.checked).toBe(true);
});
});

View File

@@ -160,8 +160,8 @@ describe('getChartManagerColumns', () => {
expect(renderFn).toBeDefined();
const { container } = render(renderFn!(null, tableDataSet[1], 1));
const checkbox = container.querySelector('[role="checkbox"]');
const checkbox = container.querySelector('input[type="checkbox"]');
expect(checkbox).toBeInTheDocument();
expect(checkbox).toHaveAttribute('data-state', 'checked'); // graphVisibilityState[1] is true
expect(checkbox).toBeChecked(); // graphVisibilityState[1] is true
});
});

View File

@@ -1,7 +1,6 @@
import { useState } from 'react';
import { CloudDownload } from '@signozhq/icons';
import { DropdownMenuSimple, type MenuProps } from '@signozhq/ui/dropdown-menu';
import { Button, Flex } from 'antd';
import { Button, Dropdown, MenuProps, Flex } from 'antd';
import { unparse } from 'papaparse';
import { DownloadProps } from './Download.types';
@@ -68,7 +67,7 @@ function Download({ data, isLoading, fileName }: DownloadProps): JSX.Element {
};
return (
<DropdownMenuSimple menu={menu}>
<Dropdown menu={menu} trigger={['click']}>
<Button
className="download-button"
loading={isLoading || isDownloading}
@@ -80,7 +79,7 @@ function Download({ data, isLoading, fileName }: DownloadProps): JSX.Element {
Download
</Flex>
</Button>
</DropdownMenuSimple>
</Dropdown>
);
}

View File

@@ -2,8 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQuery } from 'react-query';
import { useLocation } from 'react-router-dom';
import { Button, Space } from 'antd';
import { Divider } from '@signozhq/ui/divider';
import { Button, Divider, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import getNextPrevId from 'api/errors/getNextPrevId';

View File

@@ -22,13 +22,13 @@ import { Color } from '@signozhq/design-tokens';
import {
Button,
ColorPicker,
Divider,
Input,
Modal,
RefSelectProps,
Select,
Tooltip,
} from 'antd';
import { Divider } from '@signozhq/ui/divider';
import { Typography } from '@signozhq/ui/typography';
import getLocalStorageKey from 'api/browser/localstorage/get';
import setLocalStorageKey from 'api/browser/localstorage/set';

View File

@@ -1,4 +1,8 @@
import { Col, Input as InputComponent } from 'antd';
import {
Col,
Dropdown as DropDownComponent,
Input as InputComponent,
} from 'antd';
import { Typography as TypographyComponent } from '@signozhq/ui/typography';
import styled from 'styled-components';
@@ -30,6 +34,16 @@ export const ButtonContainer = styled.div`
}
`;
export const Dropdown = styled(DropDownComponent)`
&&& {
display: flex;
justify-content: center;
align-items: center;
max-width: 150px;
min-width: 150px;
}
`;
export const TextContainer = styled.div`
&&& {
min-width: 100px;

View File

@@ -1,6 +1,6 @@
import { grey } from '@ant-design/colors';
import { Checkbox } from '@signozhq/ui/checkbox';
import { CSSProperties } from 'react';
import { Checkbox, ConfigProvider } from 'antd';
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
import { CheckBoxProps } from '../types';
@@ -11,22 +11,30 @@ function CustomCheckBox({
checkBoxOnChangeHandler,
disabled = false,
}: CheckBoxProps): JSX.Element {
const onChangeHandler = (e: CheckboxChangeEvent): void => {
checkBoxOnChangeHandler(e, index);
};
const color = data[index]?.stroke?.toString() || grey[0];
const isChecked = graphVisibilityState[index] || false;
const colorStyle = {
'--checkbox-checked-background': color,
'--checkbox-border-color': color,
} as CSSProperties;
return (
<span style={colorStyle}>
<ConfigProvider
theme={{
token: {
colorPrimary: color,
colorBorder: color,
colorBgContainer: color,
},
}}
>
<Checkbox
onChange={(checked): void => checkBoxOnChangeHandler(checked, index)}
value={isChecked}
onChange={onChangeHandler}
checked={isChecked}
disabled={disabled}
/>
</span>
</ConfigProvider>
);
}

View File

@@ -1,4 +1,5 @@
import { Dispatch, MutableRefObject, RefObject, SetStateAction } from 'react';
import type { CheckboxChangeEvent } from 'antd/es/checkbox';
import { ToggleGraphProps } from 'components/Graph/types';
import { UplotProps } from 'components/Uplot/Uplot';
import { PANEL_TYPES } from 'constants/queryBuilder';
@@ -76,10 +77,7 @@ export interface CheckBoxProps {
data: ExtendedChartDataset[];
index: number;
graphVisibilityState: boolean[];
checkBoxOnChangeHandler: (
checked: boolean | 'indeterminate',
index: number,
) => void;
checkBoxOnChangeHandler: (e: CheckboxChangeEvent, index: number) => void;
disabled?: boolean;
}

View File

@@ -1,7 +1,6 @@
// eslint-disable-next-line no-restricted-imports
import { Provider } from 'react-redux';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { fireEvent, render, screen } from '@testing-library/react';
import { PANEL_TYPES } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
import { AppProvider } from 'providers/App/App';
@@ -177,7 +176,6 @@ jest.mock('providers/Dashboard/store/useDashboardStore', () => ({
describe('WidgetGraphComponent', () => {
it('should show correct menu items when hovering over more options while loading', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
const { getByTestId, findByRole, getByText, container } = render(
<MockQueryClientProvider>
<ErrorModalProvider>
@@ -210,7 +208,7 @@ describe('WidgetGraphComponent', () => {
expect(skeleton).toBeInTheDocument();
const moreOptionsButton = getByTestId('widget-header-options');
await user.click(moreOptionsButton);
fireEvent.mouseEnter(moreOptionsButton);
const menu = await findByRole('menu');
expect(menu).toBeInTheDocument();

View File

@@ -54,17 +54,6 @@
visibility: visible;
}
// currently the width of the dropdown menu is set to 100% of the parent container,
// which is not desired. This is a workaround to unset that width and allow the dropdown menu to size based on its content.
// This is necessary because the dropdown menu can contain items with varying widths, and setting it to 100% can cause layout issues and make the menu look unbalanced.
// we should idealy fix this in the dropdown menu component itself, but for now this is a quick fix to ensure the dropdown menu looks correct in the widget header.
[data-radix-popper-content-wrapper]
[data-slot='dropdown-menu-content'].widget-header-dropdown
[data-slot='dropdown-menu-item'] {
width: unset !important;
}
.widget-api-actions {
padding-right: 0.25rem;
}

View File

@@ -467,7 +467,6 @@ describe('WidgetHeader', () => {
describe('Create Alerts Menu Item', () => {
it('renders Create Alerts menu item with external link icon when included in headerMenuList', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<WidgetHeader
title={TEST_WIDGET_TITLE}
@@ -484,7 +483,7 @@ describe('WidgetHeader', () => {
const moreOptionsIcon = await screen.findByTestId(WIDGET_HEADER_OPTIONS_ID);
expect(moreOptionsIcon).toBeInTheDocument();
await user.click(moreOptionsIcon);
await userEvent.hover(moreOptionsIcon);
await screen.findByText(CREATE_ALERTS_TEXT);
@@ -495,7 +494,6 @@ describe('WidgetHeader', () => {
});
it('Create Alerts menu item is enabled and clickable', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
const mockCreateAlertsHandler = jest.fn();
const useCreateAlerts = jest.requireMock(
'hooks/queryBuilder/useCreateAlerts',
@@ -519,12 +517,12 @@ describe('WidgetHeader', () => {
expect(useCreateAlerts).toHaveBeenCalledWith(mockWidget, 'dashboardView');
const moreOptionsIcon = await screen.findByTestId(WIDGET_HEADER_OPTIONS_ID);
await user.click(moreOptionsIcon);
await userEvent.hover(moreOptionsIcon);
const createAlertsMenuItem = await screen.findByText(CREATE_ALERTS_TEXT);
// Verify the menu item is clickable by actually clicking it
await user.click(createAlertsMenuItem);
await userEvent.click(createAlertsMenuItem);
expect(mockCreateAlertsHandler).toHaveBeenCalledTimes(1);
});
});

View File

@@ -15,8 +15,7 @@ import {
X,
} from '@signozhq/icons';
import { Color } from '@signozhq/design-tokens';
import { Button, Input, Tooltip } from 'antd';
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
import { Button, Dropdown, Input, MenuProps, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import ErrorContent from 'components/ErrorModal/components/ErrorContent';
import ErrorPopover from 'components/ErrorPopover/ErrorPopover';
@@ -129,7 +128,7 @@ function WidgetHeader({
],
);
const onMenuItemSelectHandler = useCallback(
const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
({ key }: { key: string }): void => {
if (isTWidgetOptions(key)) {
const functionToCall = keyMethodMapping[key];
@@ -189,8 +188,18 @@ function WidgetHeader({
{
key: MenuItemKeys.CreateAlerts,
icon: <Bell size="md" />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.CreateAlerts],
rightIcon: <SquareArrowOutUpRight size="lg" />,
label: (
<span
style={{
display: 'flex',
alignItems: 'baseline',
justifyContent: 'space-between',
}}
>
{MENUITEM_KEYS_VS_LABELS[MenuItemKeys.CreateAlerts]}
<SquareArrowOutUpRight size={10} />
</span>
),
isVisible: headerMenuList?.includes(MenuItemKeys.CreateAlerts) || false,
disabled: false,
},
@@ -212,10 +221,8 @@ function WidgetHeader({
const menu = useMemo(
() => ({
items: updatedMenuList.map((item) => ({
...item,
onClick: onMenuItemSelectHandler,
})),
items: updatedMenuList,
onClick: onMenuItemSelectHandler,
}),
[updatedMenuList, onMenuItemSelectHandler],
);
@@ -314,12 +321,7 @@ function WidgetHeader({
/>
)}
{menu && Array.isArray(menu.items) && menu.items.length > 0 && (
<DropdownMenuSimple
menu={menu}
side="bottom"
align="end"
className="widget-header-dropdown"
>
<Dropdown menu={menu} trigger={['hover']} placement="bottomRight">
<Button
data-testid="widget-header-options"
className={`widget-header-more-options ${
@@ -327,7 +329,7 @@ function WidgetHeader({
}`}
icon={<EllipsisVertical size="md" />}
/>
</DropdownMenuSimple>
</Dropdown>
)}
</div>
</>

View File

@@ -6,7 +6,6 @@ export interface MenuItem {
key: MenuItemKeys;
icon: ReactNode;
label: ReactNode;
rightIcon?: ReactNode;
isVisible: boolean;
disabled: boolean;
danger?: boolean;

View File

@@ -1,9 +1,9 @@
import type { MenuItem as DropdownMenuItem } from '@signozhq/ui/dropdown-menu';
import type { MenuItemType } from 'antd/es/menu/hooks/useItems';
import { MenuItemKeys } from './contants';
import { MenuItem } from './types';
export const generateMenuList = (actions: MenuItem[]): DropdownMenuItem[] =>
export const generateMenuList = (actions: MenuItem[]): MenuItemType[] =>
actions
.filter((action: MenuItem) => action.isVisible)
.map(({ key, icon: Icon, label, disabled, ...rest }) => ({

View File

@@ -8,10 +8,9 @@ import React, {
import { useQuery } from 'react-query';
// eslint-disable-next-line no-restricted-imports
import { Color, Spacing } from '@signozhq/design-tokens';
import { Button, Drawer, Tooltip } from 'antd';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { Divider } from '@signozhq/ui/divider';
import { Button, Divider, Drawer, Radio, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import type { RadioChangeEvent } from 'antd/lib';
import logEvent from 'api/common/logEvent';
import { combineInitialAndUserExpression } from 'components/QueryBuilderV2/QueryV2/QuerySearch/utils';
import { convertFiltersToExpression } from 'components/QueryBuilderV2/utils';
@@ -331,8 +330,8 @@ export default function K8sBaseDetails<T>({
}
}, [getMinMaxTime, selectedTime]);
const handleTabChange = (value: string): void => {
setSelectedView(value);
const handleTabChange = (e: RadioChangeEvent): void => {
setSelectedView(e.target.value);
setLogFiltersParam(null);
setTracesFiltersParam(null);
setEventsFiltersParam(null);
@@ -340,7 +339,7 @@ export default function K8sBaseDetails<T>({
entity: InfraMonitoringEvents.K8sEntity,
page: InfraMonitoringEvents.DetailedPage,
category: eventCategory,
view: value,
view: e.target.value,
});
};
@@ -521,101 +520,102 @@ export default function K8sBaseDetails<T>({
{!hideDetailViewTabs && (
<div className="views-tabs-container">
<ToggleGroupSimple
type="single"
<Radio.Group
className="views-tabs"
onChange={handleTabChange}
value={selectedView}
items={[
...(tabVisibility.showMetrics
? [
{
value: VIEW_TYPES.METRICS,
label: (
<div className="view-title">
<BarChart size={14} />
Metrics
</div>
),
},
]
: []),
...(tabVisibility.showLogs
? [
{
value: VIEW_TYPES.LOGS,
label: (
<div className="view-title">
<ScrollText size={14} />
Logs
</div>
),
},
]
: []),
...(tabVisibility.showTraces
? [
{
value: VIEW_TYPES.TRACES,
label: (
<div className="view-title">
<DraftingCompass size={14} />
Traces
</div>
),
},
]
: []),
...(tabVisibility.showEvents
? [
{
value: VIEW_TYPES.EVENTS,
label: (
<div className="view-title">
<ChevronsLeftRight size={14} />
Events
</div>
),
},
]
: []),
...(tabVisibility.showContainers
? [
{
value: VIEW_TYPES.CONTAINERS,
label: (
<div className="view-title">
<Package2 size={14} />
Containers
</div>
),
},
]
: []),
...(tabVisibility.showProcesses
? [
{
value: VIEW_TYPES.PROCESSES,
label: (
<div className="view-title">
<ChevronsLeftRight size={14} />
Processes
</div>
),
},
]
: []),
...(customTabs?.map((tab) => ({
value: tab.key,
label: (
<div className="view-title">
{tab.icon}
{tab.label}
</div>
),
})) ?? []),
]}
/>
>
{tabVisibility.showMetrics && (
<Radio.Button
className={
selectedView === VIEW_TYPES.METRICS ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.METRICS}
>
<div className="view-title">
<BarChart size={14} />
Metrics
</div>
</Radio.Button>
)}
{tabVisibility.showLogs && (
<Radio.Button
className={
selectedView === VIEW_TYPES.LOGS ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.LOGS}
>
<div className="view-title">
<ScrollText size={14} />
Logs
</div>
</Radio.Button>
)}
{tabVisibility.showTraces && (
<Radio.Button
className={
selectedView === VIEW_TYPES.TRACES ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.TRACES}
>
<div className="view-title">
<DraftingCompass size={14} />
Traces
</div>
</Radio.Button>
)}
{tabVisibility.showEvents && (
<Radio.Button
className={
selectedView === VIEW_TYPES.EVENTS ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.EVENTS}
>
<div className="view-title">
<ChevronsLeftRight size={14} />
Events
</div>
</Radio.Button>
)}
{tabVisibility.showContainers && (
<Radio.Button
className={
selectedView === VIEW_TYPES.CONTAINERS ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.CONTAINERS}
>
<div className="view-title">
<Package2 size={14} />
Containers
</div>
</Radio.Button>
)}
{tabVisibility.showProcesses && (
<Radio.Button
className={
selectedView === VIEW_TYPES.PROCESSES ? 'selected_view tab' : 'tab'
}
value={VIEW_TYPES.PROCESSES}
>
<div className="view-title">
<ChevronsLeftRight size={14} />
Processes
</div>
</Radio.Button>
)}
{customTabs?.map((tab) => (
<Radio.Button
key={tab.key}
className={selectedView === tab.key ? 'selected_view tab' : 'tab'}
value={tab.key}
>
<div className="view-title">
{tab.icon}
{tab.label}
</div>
</Radio.Button>
))}
</Radio.Group>
{selectedView === VIEW_TYPES.LOGS && (
<Tooltip title="Go to Logs Explorer" placement="left">

View File

@@ -152,23 +152,23 @@
font-weight: var(--font-weight-normal);
}
> button {
.tab {
border: 1px solid var(--l1-border);
width: 114px;
}
&::before {
background: var(--l1-border);
}
.tab::before {
background: var(--l1-border);
}
&[data-state='on'] {
background: var(--l1-border);
color: var(--l1-foreground);
border: 1px solid var(--l1-border);
.selected_view {
background: var(--l1-border);
color: var(--l1-foreground);
border: 1px solid var(--l1-border);
}
&::before {
background: var(--l1-border);
}
}
.selected_view::before {
background: var(--l1-border);
}
}

View File

@@ -1,5 +1,5 @@
import { Dispatch, SetStateAction } from 'react';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Checkbox } from 'antd';
import { useRegionSelection } from 'hooks/integration/aws/useRegionSelection';
import { regions } from 'utils/regions';
@@ -21,18 +21,18 @@ export function RegionSelector({
setIncludeAllRegions,
});
const allSelected =
allRegionIds.length > 0 &&
allRegionIds.every((regionId) => selectedRegions.includes(regionId));
const someSelected =
selectedRegions.length > 0 && selectedRegions.length < allRegionIds.length;
return (
<div className="region-selector">
<div className="select-all">
<Checkbox
value={allSelected ? true : someSelected ? 'indeterminate' : false}
onChange={(checked): void => handleSelectAll(checked === true)}
checked={
allRegionIds.length > 0 &&
allRegionIds.every((regionId) => selectedRegions.includes(regionId))
}
indeterminate={
selectedRegions.length > 0 && selectedRegions.length < allRegionIds.length
}
onChange={(e): void => handleSelectAll(e.target.checked)}
>
Select All Regions
</Checkbox>
@@ -45,7 +45,7 @@ export function RegionSelector({
{region.subRegions.map((subRegion) => (
<Checkbox
key={subRegion.id}
value={selectedRegions.includes(subRegion.id)}
checked={selectedRegions.includes(subRegion.id)}
onChange={(): void => handleRegionSelect(subRegion.id)}
>
{subRegion.name}

View File

@@ -1,7 +1,6 @@
import React, { useCallback, useState } from 'react';
import { Plus } from '@signozhq/icons';
import { Button, Flex } from 'antd';
import { Divider } from '@signozhq/ui/divider';
import { Button, Divider, Flex } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import ROUTES from 'constants/routes';

View File

@@ -3,8 +3,7 @@ import { useTranslation } from 'react-i18next';
import { UseQueryResult } from 'react-query';
import { Button, Flex, Input } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { Ellipsis, Plus } from '@signozhq/icons';
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
import { Plus } from '@signozhq/icons';
import type { ColumnsType } from 'antd/es/table/interface';
import logEvent from 'api/common/logEvent';
import { convertToApiError } from 'api/ErrorResponseHandlerForGeneratedAPIs';
@@ -16,6 +15,7 @@ import type {
} from 'api/generated/services/sigNoz.schemas';
import type { ErrorType } from 'api/generatedAPIInstance';
import { AxiosError } from 'axios';
import DropDown from 'components/DropDown/DropDown';
import {
DynamicColumnsKey,
TableDataSource,
@@ -323,67 +323,55 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
dataIndex: 'id',
key: 'action',
width: 10,
render: (id: RuletypesRuleDTO['id'], record): JSX.Element => {
const actionItems = [
<ToggleAlertState
key="1"
disabled={record.disabled ?? false}
setData={setData}
id={id ?? ''}
/>,
<ColumnButton
key="2"
onClick={(e: React.MouseEvent): void =>
onEditHandler(record, { newTab: isModifierKeyPressed(e) })
render: (id: RuletypesRuleDTO['id'], record): JSX.Element => (
<div data-testid="alert-actions">
<DropDown
onDropDownItemClick={(item): void =>
alertActionLogEvent(item.key, record)
}
type="link"
loading={editLoader}
>
Edit
</ColumnButton>,
<ColumnButton
key="3-new-tab"
onClick={(): void => onEditHandler(record, { newTab: true })}
type="link"
loading={editLoader}
>
Edit in New Tab
</ColumnButton>,
<ColumnButton
key="3-clone"
onClick={onCloneHandler(record)}
type="link"
loading={cloneLoader}
>
Clone
</ColumnButton>,
<DeleteAlert
key="4"
notifications={notificationsApi}
setData={setData}
id={id ?? ''}
/>,
];
return (
<div data-testid="alert-actions">
<DropdownMenuSimple
menu={{
items: actionItems.map((element, index) => ({
key: String(index),
label: element,
onClick: ({ key }): void => alertActionLogEvent(key, record),
})),
}}
>
<Button
element={[
<ToggleAlertState
key="1"
disabled={record.disabled ?? false}
setData={setData}
id={id ?? ''}
/>,
<ColumnButton
key="2"
onClick={(e: React.MouseEvent): void =>
onEditHandler(record, { newTab: isModifierKeyPressed(e) })
}
type="link"
style={{ color: 'var(--l1-foreground)' }}
icon={<Ellipsis size={16} />}
/>
</DropdownMenuSimple>
</div>
);
},
loading={editLoader}
>
Edit
</ColumnButton>,
<ColumnButton
key="3"
onClick={(): void => onEditHandler(record, { newTab: true })}
type="link"
loading={editLoader}
>
Edit in New Tab
</ColumnButton>,
<ColumnButton
key="3"
onClick={onCloneHandler(record)}
type="link"
loading={cloneLoader}
>
Clone
</ColumnButton>,
<DeleteAlert
key="4"
notifications={notificationsApi}
setData={setData}
id={id ?? ''}
/>,
]}
/>
</div>
),
});
}

View File

@@ -12,11 +12,12 @@ import { useTranslation } from 'react-i18next';
import { generatePath } from 'react-router-dom';
import { useCopyToClipboard } from 'react-use';
import { Color } from '@signozhq/design-tokens';
import { DropdownMenuSimple, type MenuItem } from '@signozhq/ui/dropdown-menu';
import {
Button,
Dropdown,
Flex,
Input,
MenuProps,
Modal,
Popover,
Skeleton,
@@ -552,7 +553,7 @@ function DashboardsList(): JSX.Element {
];
const getCreateDashboardItems = useMemo(() => {
const menuItems: MenuItem[] = [
const menuItems: MenuProps['items'] = [
{
label: (
<div
@@ -710,11 +711,11 @@ function DashboardsList(): JSX.Element {
{createNewDashboard && (
<section className="actions">
<DropdownMenuSimple
className="new-dashboard-menu"
<Dropdown
overlayClassName="new-dashboard-menu"
menu={{ items: getCreateDashboardItems }}
side="bottom"
align="end"
placement="bottomRight"
trigger={['click']}
>
<Button
type="text"
@@ -726,7 +727,7 @@ function DashboardsList(): JSX.Element {
>
New Dashboard
</Button>
</DropdownMenuSimple>
</Dropdown>
<Button
type="text"
className="learn-more"
@@ -755,11 +756,11 @@ function DashboardsList(): JSX.Element {
onChange={handleSearch}
/>
{createNewDashboard && (
<DropdownMenuSimple
className="new-dashboard-menu"
<Dropdown
overlayClassName="new-dashboard-menu"
menu={{ items: getCreateDashboardItems }}
side="bottom"
align="end"
placement="bottomRight"
trigger={['click']}
>
<Button
type="primary"
@@ -772,7 +773,7 @@ function DashboardsList(): JSX.Element {
>
New dashboard
</Button>
</DropdownMenuSimple>
</Dropdown>
)}
</div>

View File

@@ -1,8 +1,7 @@
import { memo, useMemo } from 'react';
// eslint-disable-next-line no-restricted-imports
import { useDispatch, useSelector } from 'react-redux';
import { Button, Flex } from 'antd';
import { Divider } from '@signozhq/ui/divider';
import { Button, Divider, Flex } from 'antd';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import Controls from 'container/Controls';
import Download from 'container/Download/Download';

View File

@@ -2,13 +2,7 @@ import { useCallback } from 'react';
import { useCopyToClipboard } from 'react-use';
import { orange } from '@ant-design/colors';
import { Settings } from '@signozhq/icons';
import {
type BaseMenuItem,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@signozhq/ui/dropdown-menu';
import { Dropdown, MenuProps } from 'antd';
import {
negateOperator,
OPERATORS,
@@ -141,38 +135,41 @@ function BodyTitleRenderer({
viewName,
]);
const onClickHandler = (key: string): void => {
const onClickHandler: MenuProps['onClick'] = (props): void => {
const mapper = {
[DROPDOWN_KEY.FILTER_IN]: filterHandler(true),
[DROPDOWN_KEY.FILTER_OUT]: filterHandler(false),
[DROPDOWN_KEY.GROUP_BY]: groupByHandler,
};
const handler = mapper[key];
const handler = mapper[props.key];
if (handler) {
handler();
}
};
const menuItems: BaseMenuItem[] = [
{
key: DROPDOWN_KEY.FILTER_IN,
label: `Filter for ${value}`,
},
{
key: DROPDOWN_KEY.FILTER_OUT,
label: `Filter out ${value}`,
},
...(isGroupBySupported
? [
{
key: DROPDOWN_KEY.GROUP_BY,
label: `Group by ${nodeKey}`,
},
]
: []),
];
const menu: MenuProps = {
items: [
{
key: DROPDOWN_KEY.FILTER_IN,
label: `Filter for ${value}`,
},
{
key: DROPDOWN_KEY.FILTER_OUT,
label: `Filter out ${value}`,
},
...(isGroupBySupported
? [
{
key: DROPDOWN_KEY.GROUP_BY,
label: `Group by ${nodeKey}`,
},
]
: []),
],
onClick: onClickHandler,
};
const handleNodeClick = useCallback(
(e: React.MouseEvent): void => {
@@ -221,23 +218,15 @@ function BodyTitleRenderer({
}}
onMouseDown={(e): void => e.preventDefault()}
>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Settings style={{ marginRight: 8 }} className="hover-reveal" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<div data-log-detail-ignore="true">
{menuItems.map((item) => (
<DropdownMenuItem
key={item.key}
onSelect={(): void => onClickHandler(item.key as string)}
>
{item.label}
</DropdownMenuItem>
))}
</div>
</DropdownMenuContent>
</DropdownMenu>
<Dropdown
menu={menu}
trigger={['click']}
dropdownRender={(originNode): React.ReactNode => (
<div data-log-detail-ignore="true">{originNode}</div>
)}
>
<Settings style={{ marginRight: 8 }} className="hover-reveal" />
</Dropdown>
</span>
)}
{title.toString()}{' '}

View File

@@ -1,4 +1,4 @@
import { Divider } from '@signozhq/ui/divider';
import { Divider } from 'antd';
import { TooltipSimple } from '@signozhq/ui/tooltip';
import { Typography } from '@signozhq/ui/typography';

View File

@@ -1,5 +1,6 @@
import { useMemo, useState } from 'react';
import { Empty } from 'antd';
import type { RadioChangeEvent } from 'antd/lib';
import SignozRadioGroup from 'components/SignozRadioGroup/SignozRadioGroup';
import { History, Table } from '@signozhq/icons';
import { DataSource } from 'types/common/queryBuilder';
@@ -59,8 +60,8 @@ function InfraMetrics({
return options;
}, [podName]);
const handleModeChange = (value: string): void => {
setSelectedView(value);
const handleModeChange = (e: RadioChangeEvent): void => {
setSelectedView(e.target.value);
};
if (!podName && !nodeName && !hostName) {

View File

@@ -3,8 +3,7 @@ import MEditor, { EditorProps, Monaco } from '@monaco-editor/react';
import { Color } from '@signozhq/design-tokens';
import { Button } from '@signozhq/ui/button';
import { Switch } from '@signozhq/ui/switch';
import { Collapse, Input, Tag } from 'antd';
import { Divider } from '@signozhq/ui/divider';
import { Collapse, Divider, Input, Tag } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { AddToQueryHOCProps } from 'components/Logs/AddToQueryHOC';
import { ChangeViewFunctionType } from 'container/ExplorerOptions/types';

View File

@@ -2,8 +2,9 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import { useHistory } from 'react-router-dom';
import { Check, ChevronDown, Plus } from '@signozhq/icons';
import { Button } from '@signozhq/ui/button';
import { DropdownMenuSimple, type MenuItem } from '@signozhq/ui/dropdown-menu';
import { Input } from '@signozhq/ui/input';
import type { MenuProps } from 'antd';
import { Dropdown } from 'antd';
import { useListUsers } from 'api/generated/services/users';
import EditMemberDrawer from 'components/EditMemberDrawer/EditMemberDrawer';
import InviteMembersModal from 'components/InviteMembersModal/InviteMembersModal';
@@ -94,7 +95,7 @@ function MembersSettings(): JSX.Element {
).length;
const totalCount = allMembers.length;
const filterMenuItems: MenuItem[] = [
const filterMenuItems: MenuProps['items'] = [
{
key: FilterMode.All,
label: (
@@ -170,9 +171,10 @@ function MembersSettings(): JSX.Element {
</div>
<div className="members-settings__controls">
<DropdownMenuSimple
<Dropdown
menu={{ items: filterMenuItems }}
className="members-filter-dropdown"
trigger={['click']}
overlayClassName="members-filter-dropdown"
>
<Button
variant="solid"
@@ -182,7 +184,7 @@ function MembersSettings(): JSX.Element {
<span>{filterLabel}</span>
<ChevronDown size={12} className="members-filter-trigger__chevron" />
</Button>
</DropdownMenuSimple>
</Dropdown>
<div className="members-settings__search">
<Input

View File

@@ -1,5 +1,4 @@
import type { TypesUserDTO } from 'api/generated/services/sigNoz.schemas';
import userEvent from '@testing-library/user-event';
import { rest, server } from 'mocks-server/server';
import { fireEvent, render, screen } from 'tests/test-utils';
@@ -77,15 +76,14 @@ describe('MembersSettings (integration)', () => {
});
it('filters to pending invites via the filter dropdown', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(<MembersSettings />);
await screen.findByText('Alice Smith');
await user.click(screen.getByRole('button', { name: /all members/i }));
fireEvent.click(screen.getByRole('button', { name: /all members/i }));
const pendingOption = await screen.findByText(/pending invites/i);
await user.click(pendingOption);
fireEvent.click(pendingOption);
await screen.findByText('charlie@signoz.io');
expect(screen.queryByText('Alice Smith')).not.toBeInTheDocument();

View File

@@ -1,8 +1,7 @@
import { useMemo } from 'react';
import { generatePath } from 'react-router-dom';
import { Color } from '@signozhq/design-tokens';
import { DropdownMenuSimple } from '@signozhq/ui/dropdown-menu';
import { Skeleton } from 'antd';
import { Dropdown, Skeleton } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import {
useGetMetricAlerts,
@@ -127,11 +126,12 @@ function DashboardsAndAlertsPopover({
return (
<div className="dashboards-and-alerts-popover-container">
{dashboardsPopoverContent && (
<DropdownMenuSimple
<Dropdown
menu={{
items: dashboardsPopoverContent,
}}
align="start"
placement="bottomLeft"
trigger={['click']}
>
<div
className="dashboards-and-alerts-popover dashboards-popover"
@@ -142,14 +142,15 @@ function DashboardsAndAlertsPopover({
{pluralize(dashboards.length, 'dashboard')}
</Typography.Text>
</div>
</DropdownMenuSimple>
</Dropdown>
)}
{alertsPopoverContent && (
<DropdownMenuSimple
<Dropdown
menu={{
items: alertsPopoverContent,
}}
align="start"
placement="bottomLeft"
trigger={['click']}
>
<div
className="dashboards-and-alerts-popover alerts-popover"
@@ -160,7 +161,7 @@ function DashboardsAndAlertsPopover({
{pluralize(alerts.length, 'alert rule')}
</Typography.Text>
</div>
</DropdownMenuSimple>
</Dropdown>
)}
</div>
);

View File

@@ -2,8 +2,7 @@ import { useCallback, useEffect, useMemo } from 'react';
// eslint-disable-next-line no-restricted-imports
import { useSelector } from 'react-redux';
import { Color } from '@signozhq/design-tokens';
import { Button, Drawer } from 'antd';
import { Divider } from '@signozhq/ui/divider';
import { Button, Divider, Drawer } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import logEvent from 'api/common/logEvent';
import { useGetMetricMetadata } from 'api/generated/services/metrics';

View File

@@ -119,17 +119,17 @@ describe('MySettings Flows', () => {
it('Should have Dark theme selected by default', async () => {
const themeSelector = screen.getByTestId(THEME_SELECTOR_TEST_ID);
const darkOption = within(themeSelector).getByRole('radio', {
name: /Dark/,
});
const darkOption = themeSelector.querySelector(
'input[value="dark"]',
) as HTMLInputElement;
expect(darkOption).toBeChecked();
});
it('Should switch theme and log event when Light theme is selected', async () => {
const themeSelector = screen.getByTestId(THEME_SELECTOR_TEST_ID);
const lightOption = within(themeSelector).getByRole('radio', {
name: /Light/,
});
const lightOption = themeSelector.querySelector(
'input[value="light"]',
) as HTMLInputElement;
fireEvent.click(lightOption);

View File

@@ -1,8 +1,7 @@
import { useEffect, useState } from 'react';
import { useMutation } from 'react-query';
import { Tag } from 'antd';
import { Radio, RadioChangeEvent, Tag } from 'antd';
import { Switch } from '@signozhq/ui/switch';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import setLocalStorageApi from 'api/browser/localstorage/set';
import logEvent from 'api/common/logEvent';
import updateUserPreference from 'api/v1/user/preferences/name/update';
@@ -89,7 +88,7 @@ function MySettings(): JSX.Element {
return isDarkMode ? 'dark' : 'light';
});
const handleThemeChange = (value: string): void => {
const handleThemeChange = ({ target: { value } }: RadioChangeEvent): void => {
logEvent('Account Settings: Theme Changed', {
theme: value,
});
@@ -188,12 +187,14 @@ function MySettings(): JSX.Element {
<div className="user-preference-section-content-item theme-selector">
<div className="user-preference-section-content-item-title-action">
Select your theme
<ToggleGroupSimple
type="single"
<Radio.Group
options={themeOptions}
onChange={handleThemeChange}
value={theme}
optionType="button"
buttonStyle="solid"
data-testid="theme-selector"
items={themeOptions}
size="middle"
/>
</div>

View File

@@ -1,5 +1,4 @@
import { Empty } from 'antd';
import { Checkbox } from '@signozhq/ui/checkbox';
import { Checkbox, Empty } from 'antd';
import { AxiosResponse } from 'axios';
import Spinner from 'components/Spinner';
import { EXCLUDED_COLUMNS } from 'container/OptionsMenu/constants';
@@ -51,8 +50,9 @@ function ExplorerAttributeColumns({
<div className="attribute-columns">
{filteredAttributeKeys.map((attributeKey: any) => (
<Checkbox
value={isAttributeKeySelected(attributeKey.name)}
checked={isAttributeKeySelected(attributeKey.name)}
onChange={(): void => handleCheckboxChange(attributeKey.name)}
style={{ padding: 0 }}
key={attributeKey.name}
>
{attributeKey.name}

View File

@@ -104,33 +104,38 @@
height: 200px;
background-color: var(--l1-border);
overflow: hidden !important;
.ant-dropdown-menu {
padding: 0;
padding: 4px;
.ant-checkbox-wrapper {
padding: 2px 8px !important;
}
.ant-dropdown-menu-item {
padding: 4px;
.ant-checkbox-wrapper {
padding: 2px 8px !important;
}
.attribute-columns {
display: flex;
flex-direction: column;
height: 160px;
overflow: scroll;
}
.attribute-columns {
display: flex;
flex-direction: column;
height: 160px;
overflow: scroll;
}
.attribute-columns::-webkit-scrollbar {
width: 3px; /* Width of the scrollbar */
}
.attribute-columns::-webkit-scrollbar {
width: 3px; /* Width of the scrollbar */
}
.attribute-columns::-webkit-scrollbar-track {
background: var(--l1-border); /* Color of the track */
}
.attribute-columns::-webkit-scrollbar-track {
background: var(--l1-border); /* Color of the track */
}
.attribute-columns::-webkit-scrollbar-thumb {
background: var(--l2-foreground); /* Color of the thumb */
border-radius: 4px; /* Roundness of the thumb */
}
.attribute-columns::-webkit-scrollbar-thumb {
background: var(--l2-foreground); /* Color of the thumb */
border-radius: 4px; /* Roundness of the thumb */
}
.attribute-columns::-webkit-scrollbar-thumb:hover {
background: var(--l1-border); /* Color of the thumb on hover */
.attribute-columns::-webkit-scrollbar-thumb:hover {
background: var(--l1-border); /* Color of the thumb on hover */
}
}
}
}

View File

@@ -7,13 +7,7 @@ import {
DropResult,
} from 'react-beautiful-dnd';
import { Color } from '@signozhq/design-tokens';
import { Button, Input, Tooltip } from 'antd';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuTrigger,
} from '@signozhq/ui/dropdown-menu';
import { Divider } from '@signozhq/ui/divider';
import { Button, Divider, Dropdown, Input, MenuProps, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { FieldDataType } from 'api/v5/v5';
import { SOMETHING_WENT_WRONG } from 'constants/api';
@@ -165,12 +159,34 @@ function ExplorerColumnsRenderer({
debouncedSetQuerySearchText(e.target.value);
};
const handleOpenChange = (nextOpen: boolean): void => {
setOpen(nextOpen);
if (nextOpen) {
setSearchText('');
}
};
const items: MenuProps['items'] = [
{
key: 'search',
label: (
<Input
type="text"
placeholder="Search"
className="explorer-columns-search"
value={searchText}
onChange={handleSearchChange}
prefix={<Search size={16} style={{ padding: '6px' }} />}
/>
),
},
{
key: 'columns',
label: (
<ExplorerAttributeColumns
isLoading={isLoading}
data={data}
searchText={searchText}
isAttributeKeySelected={isAttributeKeySelected}
handleCheckboxChange={handleCheckboxChange}
dataSource={initialDataSource}
/>
),
},
];
const removeSelectedLogField = (name: string): void => {
if (
@@ -222,6 +238,13 @@ function ExplorerColumnsRenderer({
}
};
const toggleDropdown = (): void => {
setOpen(!open);
if (!open) {
setSearchText('');
}
};
const isDarkMode = useIsDarkMode();
return (
@@ -304,38 +327,25 @@ function ExplorerColumnsRenderer({
</Droppable>
</DragDropContext>
<div>
<DropdownMenu open={open} onOpenChange={handleOpenChange}>
<DropdownMenuTrigger asChild>
<Button
className="action-btn"
data-testid="add-columns-button"
icon={
<CirclePlus
size={16}
color={isDarkMode ? Color.BG_INK_400 : Color.BG_VANILLA_100}
/>
}
/>
</DropdownMenuTrigger>
<DropdownMenuContent side="top" className="explorer-columns-dropdown">
<Input
type="text"
placeholder="Search"
className="explorer-columns-search"
value={searchText}
onChange={handleSearchChange}
prefix={<Search size={16} style={{ padding: '6px' }} />}
/>
<ExplorerAttributeColumns
isLoading={isLoading}
data={data}
searchText={searchText}
isAttributeKeySelected={isAttributeKeySelected}
handleCheckboxChange={handleCheckboxChange}
dataSource={initialDataSource}
/>
</DropdownMenuContent>
</DropdownMenu>
<Dropdown
menu={{ items }}
arrow
placement="top"
open={open}
overlayClassName="explorer-columns-dropdown"
>
<Button
className="action-btn"
data-testid="add-columns-button"
icon={
<CirclePlus
size={16}
color={isDarkMode ? Color.BG_INK_400 : Color.BG_VANILLA_100}
/>
}
onClick={toggleDropdown}
/>
</Dropdown>
</div>
</div>
)}

View File

@@ -146,7 +146,6 @@ describe('ExplorerColumnsRenderer', () => {
});
it('opens and closes the dropdown', async () => {
const user = userEvent.setup({ pointerEventsCheck: 0 });
render(
<Wrapper>
<ExplorerColumnsRenderer
@@ -159,12 +158,12 @@ describe('ExplorerColumnsRenderer', () => {
);
const addButton = screen.getByTestId('add-columns-button');
await user.click(addButton);
await userEvent.click(addButton);
expect(screen.getByPlaceholderText('Search')).toBeInTheDocument();
expect(screen.getByText('attribute1')).toBeInTheDocument();
await user.click(addButton);
await userEvent.click(addButton);
await waitFor(() => {
expect(screen.queryByRole('menu')).not.toBeInTheDocument();
});

View File

@@ -13,7 +13,7 @@ import { Plus, Trash2 } from '@signozhq/icons';
import { ContextLinkProps, Widgets } from 'types/api/dashboard/getAll';
import { getBaseUrl } from 'utils/basePath';
import VariablesPopover from './VariablesPopover';
import VariablesDropdown from './VariablesDropdown';
import './UpdateContextLinks.styles.scss';
@@ -71,7 +71,7 @@ function UpdateContextLinks({
customVariables: fieldVariables,
});
// Transform variables into the format expected by VariablesPopover
// Transform variables into the format expected by VariablesDropdown
const transformedVariables = useMemo(
() => transformContextVariables(variables),
[variables],
@@ -224,9 +224,7 @@ function UpdateContextLinks({
},
]}
>
{/* TODO: replace with AutoComplete with options for variables and
previously used URLs for better UX */}
<VariablesPopover
<VariablesDropdown
onVariableSelect={handleVariableSelect}
variables={transformedVariables}
>
@@ -254,7 +252,7 @@ function UpdateContextLinks({
/>
</div>
)}
</VariablesPopover>
</VariablesDropdown>
</Form.Item>
{/* Remove the separate variables section */}
@@ -284,7 +282,7 @@ function UpdateContextLinks({
/>
</Col>
<Col span={16}>
<VariablesPopover
<VariablesDropdown
onVariableSelect={(variableName, cursorPosition): void =>
handleParamVariableSelect(index, variableName, cursorPosition)
}
@@ -313,7 +311,7 @@ function UpdateContextLinks({
}
/>
)}
</VariablesPopover>
</VariablesDropdown>
</Col>
<Col span={2}>
<Button

View File

@@ -0,0 +1,26 @@
.variables-dropdown-container {
.url-input-trigger {
width: 100%;
.url-input-field {
width: 100%;
}
}
// Override Ant Design dropdown styles
.ant-dropdown-menu {
max-height: 300px;
overflow-y: auto;
padding: 8px 0;
}
}
.variable-row {
display: flex;
justify-content: space-between;
.variable-source {
color: #666;
font-size: 12px;
}
}

View File

@@ -0,0 +1,93 @@
import { ReactNode, useEffect, useMemo, useRef, useState } from 'react';
import { Dropdown } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import './VariablesDropdown.styles.scss';
interface VariablesDropdownProps {
onVariableSelect: (variableName: string, cursorPosition?: number) => void;
variables: VariableItem[];
children: (props: {
onVariableSelect: (variableName: string, cursorPosition?: number) => void;
isOpen: boolean;
setIsOpen: (open: boolean) => void;
cursorPosition: number | null;
setCursorPosition: (position: number | null) => void;
}) => ReactNode;
}
interface VariableItem {
name: string;
source: string;
}
function VariablesDropdown({
onVariableSelect,
variables,
children,
}: VariablesDropdownProps): JSX.Element {
const [isOpen, setIsOpen] = useState(false);
const [cursorPosition, setCursorPosition] = useState<number | null>(null);
const wrapperRef = useRef<HTMLDivElement>(null);
// Click outside handler
useEffect(() => {
function handleClickOutside(event: MouseEvent): void {
if (
wrapperRef.current &&
!wrapperRef.current.contains(event.target as Node)
) {
setIsOpen(false);
}
}
if (isOpen) {
document.addEventListener('mousedown', handleClickOutside);
}
return (): void => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isOpen]);
const dropdownItems = useMemo(
() =>
variables.map((v) => ({
key: v.name,
label: (
<div className="variable-row">
<Typography.Text className="variable-name">{`{{${v.name}}}`}</Typography.Text>
<Typography.Text className="variable-source">{v.source}</Typography.Text>
</div>
),
})),
[variables],
);
return (
<div className="variables-dropdown-container" ref={wrapperRef}>
<Dropdown
menu={{
items: dropdownItems,
onClick: ({ key }): void => {
const variableName = key as string;
onVariableSelect(`{{${variableName}}}`, cursorPosition || undefined);
setIsOpen(false);
},
}}
open={isOpen}
placement="bottomLeft"
trigger={['click']}
getPopupContainer={(): HTMLElement => wrapperRef.current || document.body}
>
{children({
onVariableSelect,
isOpen,
setIsOpen,
cursorPosition,
setCursorPosition,
})}
</Dropdown>
</div>
);
}
export default VariablesDropdown;

View File

@@ -1,74 +0,0 @@
.variables-popover-container {
.url-input-trigger {
width: 100%;
.url-input-field {
width: 100%;
}
}
.variables-popover-anchor-wrap {
width: 100%;
}
}
.variables-popover-content {
// antd Modal uses z-index ~1000; popover must sit above it.
z-index: 1100;
padding: 4px 0;
max-height: 300px;
overflow-y: auto;
overflow-x: hidden;
min-width: var(--radix-popover-trigger-width);
}
.variables-popover-empty {
padding: 8px 12px;
color: var(--l3-foreground, #999);
font-size: 12px;
font-style: italic;
}
.variables-popover-item {
all: unset;
display: block;
box-sizing: border-box;
width: 100%;
padding: 8px 12px;
cursor: pointer;
color: var(--l1-foreground);
font-size: 13px;
line-height: 1.4;
overflow: hidden;
&:hover,
&:focus {
background-color: var(--l1-background-hover);
}
}
.variable-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
min-width: 0;
.variable-name,
.variable-source {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.variable-name {
flex: 1 1 auto;
}
.variable-source {
color: #666;
font-size: 12px;
flex: 0 1 auto;
}
}

View File

@@ -1,111 +0,0 @@
// Uses Popover (not DropdownMenu like the rest of the antd-dropdown migration):
// DropdownMenuTrigger preventDefaults pointerdown, breaking input focus and
// dismissing on every keystroke. PopoverAnchor is a passive positioning element.
import { ReactNode, useRef, useState } from 'react';
import { Popover, PopoverAnchor, PopoverContent } from '@signozhq/ui/popover';
import { Typography } from '@signozhq/ui/typography';
import './VariablesPopover.styles.scss';
interface VariablesPopoverProps {
onVariableSelect: (variableName: string, cursorPosition?: number) => void;
variables: VariableItem[];
children: (props: {
onVariableSelect: (variableName: string, cursorPosition?: number) => void;
isOpen: boolean;
setIsOpen: (open: boolean) => void;
cursorPosition: number | null;
setCursorPosition: (position: number | null) => void;
}) => ReactNode;
}
interface VariableItem {
name: string;
source: string;
}
function VariablesPopover({
onVariableSelect,
variables,
children,
}: VariablesPopoverProps): JSX.Element {
const [isOpen, setIsOpen] = useState(false);
const [cursorPosition, setCursorPosition] = useState<number | null>(null);
const anchorRef = useRef<HTMLDivElement>(null);
const handleOpenChange = (open: boolean): void => {
// Accept "close" events from the popover (outside-click, Esc) but ignore
// opens — opening is driven by the input's onFocus in the consumer.
if (!open) {
setIsOpen(false);
}
};
return (
<div className="variables-popover-container">
<Popover open={isOpen} onOpenChange={handleOpenChange} modal={false}>
<PopoverAnchor asChild>
<div className="variables-popover-anchor-wrap" ref={anchorRef}>
{children({
onVariableSelect,
isOpen,
setIsOpen,
cursorPosition,
setCursorPosition,
})}
</div>
</PopoverAnchor>
<PopoverContent
align="start"
sideOffset={4}
className="variables-popover-content"
onOpenAutoFocus={(e): void => e.preventDefault()}
onCloseAutoFocus={(e): void => e.preventDefault()}
onInteractOutside={(e): void => {
// Keep the popover open while interacting with the anchor (the input),
// otherwise typing/clicking the input would close it immediately.
const target = e.target as Node | null;
if (target && anchorRef.current?.contains(target)) {
e.preventDefault();
}
}}
onFocusOutside={(e): void => {
const target = e.target as Node | null;
if (target && anchorRef.current?.contains(target)) {
e.preventDefault();
}
}}
>
{variables.length === 0 ? (
<div className="variables-popover-empty">No variables available</div>
) : (
variables.map((v) => (
<button
key={v.name}
type="button"
className="variables-popover-item"
onMouseDown={(e): void => {
// Prevent the input from losing focus when clicking an item.
e.preventDefault();
}}
onClick={(): void => {
onVariableSelect(`{{${v.name}}}`, cursorPosition || undefined);
setIsOpen(false);
}}
>
<div className="variable-row">
<Typography.Text className="variable-name">{`{{${v.name}}}`}</Typography.Text>
<Typography.Text className="variable-source">
{v.source}
</Typography.Text>
</div>
</button>
))
)}
</PopoverContent>
</Popover>
</div>
);
}
export default VariablesPopover;

View File

@@ -204,7 +204,7 @@ const processContextLinks = (
};
/**
* Transforms context variables into the format expected by VariablesPopover
* Transforms context variables into the format expected by VariablesDropdown
* @param variables - Array of context variables from useContextVariables
* @returns Array of transformed variables with proper source descriptions
*/

View File

@@ -1,7 +1,6 @@
import { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { ChevronDown } from '@signozhq/icons';
import { DropdownMenuSimple, type MenuItem } from '@signozhq/ui/dropdown-menu';
import { Button, ColorPicker, Space } from 'antd';
import { Button, ColorPicker, Dropdown, MenuProps, Space } from 'antd';
import type { Color } from 'antd/es/color-picker';
import useDebounce from 'hooks/useDebounce';
@@ -27,7 +26,7 @@ function ColorSelector({
setColorFromPicker(hex);
};
const items: MenuItem[] = [
const items: MenuProps['items'] = [
{
key: 'Red',
label: <CustomColor color="Red" />,
@@ -63,7 +62,7 @@ function ColorSelector({
];
return (
<DropdownMenuSimple menu={{ items }}>
<Dropdown menu={{ items }} trigger={['click']}>
<Button
onClick={(e): void => e.preventDefault()}
className="color-selector-button"
@@ -73,7 +72,7 @@ function ColorSelector({
<ChevronDown size="md" />
</Space>
</Button>
</DropdownMenuSimple>
</Dropdown>
);
}

View File

@@ -1,4 +1,4 @@
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/ui/toggle-group';
import { Typography } from '@signozhq/ui/typography';
import { DisconnectedValuesMode } from 'lib/uPlotV2/config/types';
@@ -12,33 +12,27 @@ export default function DisconnectValuesModeToggle({
onChange,
}: DisconnectValuesModeToggleProps): JSX.Element {
return (
<ToggleGroupSimple
<ToggleGroup
type="single"
value={value}
size="lg"
onChange={(newValue: string): void => {
onChange={(newValue): void => {
if (newValue) {
onChange(newValue as DisconnectedValuesMode);
}
}}
items={[
{
value: DisconnectedValuesMode.Never,
'aria-label': 'Never',
label: (
<Typography.Text className="section-heading-small">Never</Typography.Text>
),
},
{
value: DisconnectedValuesMode.Threshold,
'aria-label': 'Threshold',
label: (
<Typography.Text className="section-heading-small">
Threshold
</Typography.Text>
),
},
]}
/>
>
<ToggleGroupItem value={DisconnectedValuesMode.Never} aria-label="Never">
<Typography.Text className="section-heading-small">Never</Typography.Text>
</ToggleGroupItem>
<ToggleGroupItem
value={DisconnectedValuesMode.Threshold}
aria-label="Threshold"
>
<Typography.Text className="section-heading-small">
Threshold
</Typography.Text>
</ToggleGroupItem>
</ToggleGroup>
);
}

View File

@@ -1,4 +1,4 @@
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/ui/toggle-group';
import { Typography } from '@signozhq/ui/typography';
import { FillMode } from 'lib/uPlotV2/config/types';
@@ -16,97 +16,74 @@ export default function FillModeSelector({
return (
<section className="fill-mode-selector control-container">
<Typography.Text className="section-heading">Fill mode</Typography.Text>
<ToggleGroupSimple
<ToggleGroup
type="single"
value={value}
size="lg"
onChange={(newValue: string): void => {
onChange={(newValue): void => {
if (newValue) {
onChange(newValue as FillMode);
}
}}
items={[
{
value: FillMode.None,
'aria-label': 'None',
label: (
<>
<svg
className="fill-mode-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<rect x="8" y="16" width="32" height="16" stroke="#888" fill="none" />
</svg>
<Typography.Text className="section-heading-small">
None
</Typography.Text>
</>
),
},
{
value: FillMode.Solid,
'aria-label': 'Solid',
label: (
<>
<svg
className="fill-mode-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<rect x="8" y="16" width="32" height="16" fill="#888" />
</svg>
<Typography.Text className="section-heading-small">
Solid
</Typography.Text>
</>
),
},
{
value: FillMode.Gradient,
'aria-label': 'Gradient',
label: (
<>
<svg
className="fill-mode-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<defs>
<linearGradient id="fill-gradient" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="#888" stopOpacity="0.2" />
<stop offset="100%" stopColor="#888" stopOpacity="0.8" />
</linearGradient>
</defs>
<rect
x="8"
y="16"
width="32"
height="16"
fill="url(#fill-gradient)"
stroke="#888"
/>
</svg>
<Typography.Text className="section-heading-small">
Gradient
</Typography.Text>
</>
),
},
]}
/>
>
<ToggleGroupItem value={FillMode.None} aria-label="None">
<svg
className="fill-mode-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<rect x="8" y="16" width="32" height="16" stroke="#888" fill="none" />
</svg>
<Typography.Text className="section-heading-small">None</Typography.Text>
</ToggleGroupItem>
<ToggleGroupItem value={FillMode.Solid} aria-label="Solid">
<svg
className="fill-mode-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<rect x="8" y="16" width="32" height="16" fill="#888" />
</svg>
<Typography.Text className="section-heading-small">Solid</Typography.Text>
</ToggleGroupItem>
<ToggleGroupItem value={FillMode.Gradient} aria-label="Gradient">
<svg
className="fill-mode-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<defs>
<linearGradient id="fill-gradient" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="#888" stopOpacity="0.2" />
<stop offset="100%" stopColor="#888" stopOpacity="0.8" />
</linearGradient>
</defs>
<rect
x="8"
y="16"
width="32"
height="16"
fill="url(#fill-gradient)"
stroke="#888"
/>
</svg>
<Typography.Text className="section-heading-small">
Gradient
</Typography.Text>
</ToggleGroupItem>
</ToggleGroup>
</section>
);
}

View File

@@ -1,4 +1,4 @@
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/ui/toggle-group';
import { Typography } from '@signozhq/ui/typography';
import { LineInterpolation } from 'lib/uPlotV2/config/types';
@@ -18,98 +18,88 @@ export default function LineInterpolationSelector({
<Typography.Text className="section-heading">
Line interpolation
</Typography.Text>
<ToggleGroupSimple
<ToggleGroup
type="single"
value={value}
size="lg"
onChange={(newValue: string): void => {
onChange={(newValue): void => {
if (newValue) {
onChange(newValue as LineInterpolation);
}
}}
items={[
{
value: LineInterpolation.Linear,
'aria-label': 'Linear',
label: (
<svg
className="line-interpolation-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="8" cy="32" r="3" fill="#888" />
<circle cx="24" cy="16" r="3" fill="#888" />
<circle cx="40" cy="32" r="3" fill="#888" />
<path d="M8 32 L24 16 L40 32" stroke="#888" />
</svg>
),
},
{
value: LineInterpolation.Spline,
'aria-label': 'Spline',
label: (
<svg
className="line-interpolation-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="8" cy="32" r="3" fill="#888" />
<circle cx="24" cy="16" r="3" fill="#888" />
<circle cx="40" cy="32" r="3" fill="#888" />
<path d="M8 32 C16 8, 32 8, 40 32" />
</svg>
),
},
{
value: LineInterpolation.StepAfter,
'aria-label': 'Step After',
label: (
<svg
className="line-interpolation-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="8" cy="32" r="3" fill="#888" />
<circle cx="24" cy="16" r="3" fill="#888" />
<circle cx="40" cy="32" r="3" fill="#888" />
<path d="M8 32 V16 H24 V32 H40" />
</svg>
),
},
{
value: LineInterpolation.StepBefore,
'aria-label': 'Step Before',
label: (
<svg
className="line-interpolation-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="8" cy="32" r="3" fill="#888" />
<circle cx="24" cy="16" r="3" fill="#888" />
<circle cx="40" cy="32" r="3" fill="#888" />
<path d="M8 32 H24 V16 H40 V32" />
</svg>
),
},
]}
/>
>
<ToggleGroupItem value={LineInterpolation.Linear} aria-label="Linear">
<svg
className="line-interpolation-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="8" cy="32" r="3" fill="#888" />
<circle cx="24" cy="16" r="3" fill="#888" />
<circle cx="40" cy="32" r="3" fill="#888" />
<path d="M8 32 L24 16 L40 32" stroke="#888" />
</svg>
</ToggleGroupItem>
<ToggleGroupItem value={LineInterpolation.Spline} aria-label="Spline">
<svg
className="line-interpolation-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="8" cy="32" r="3" fill="#888" />
<circle cx="24" cy="16" r="3" fill="#888" />
<circle cx="40" cy="32" r="3" fill="#888" />
<path d="M8 32 C16 8, 32 8, 40 32" />
</svg>
</ToggleGroupItem>
<ToggleGroupItem
value={LineInterpolation.StepAfter}
aria-label="Step After"
>
<svg
className="line-interpolation-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="8" cy="32" r="3" fill="#888" />
<circle cx="24" cy="16" r="3" fill="#888" />
<circle cx="40" cy="32" r="3" fill="#888" />
<path d="M8 32 V16 H24 V32 H40" />
</svg>
</ToggleGroupItem>
<ToggleGroupItem
value={LineInterpolation.StepBefore}
aria-label="Step Before"
>
<svg
className="line-interpolation-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<circle cx="8" cy="32" r="3" fill="#888" />
<circle cx="24" cy="16" r="3" fill="#888" />
<circle cx="40" cy="32" r="3" fill="#888" />
<path d="M8 32 H24 V16 H40 V32" />
</svg>
</ToggleGroupItem>
</ToggleGroup>
</section>
);
}

View File

@@ -1,4 +1,4 @@
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { ToggleGroup, ToggleGroupItem } from '@signozhq/ui/toggle-group';
import { Typography } from '@signozhq/ui/typography';
import { LineStyle } from 'lib/uPlotV2/config/types';
@@ -16,63 +16,46 @@ export default function LineStyleSelector({
return (
<section className="line-style-selector control-container">
<Typography.Text className="section-heading">Line style</Typography.Text>
<ToggleGroupSimple
<ToggleGroup
type="single"
value={value}
size="lg"
onChange={(newValue: string): void => {
onChange={(newValue): void => {
if (newValue) {
onChange(newValue as LineStyle);
}
}}
items={[
{
value: LineStyle.Solid,
'aria-label': 'Solid',
label: (
<>
<svg
className="line-style-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M8 24 L40 24" />
</svg>
<Typography.Text className="section-heading-small">
Solid
</Typography.Text>
</>
),
},
{
value: LineStyle.Dashed,
'aria-label': 'Dashed',
label: (
<>
<svg
className="line-style-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6 4"
>
<path d="M8 24 L40 24" />
</svg>
<Typography.Text className="section-heading-small">
Dashed
</Typography.Text>
</>
),
},
]}
/>
>
<ToggleGroupItem value={LineStyle.Solid} aria-label="Solid">
<svg
className="line-style-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M8 24 L40 24" />
</svg>
<Typography.Text className="section-heading-small">Solid</Typography.Text>
</ToggleGroupItem>
<ToggleGroupItem value={LineStyle.Dashed} aria-label="Dashed">
<svg
className="line-style-icon"
viewBox="0 0 48 48"
fill="none"
stroke="#888"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
strokeDasharray="6 4"
>
<path d="M8 24 L40 24" />
</svg>
<Typography.Text className="section-heading-small">Dashed</Typography.Text>
</ToggleGroupItem>
</ToggleGroup>
</section>
);
}

View File

@@ -1,6 +1,5 @@
import { useState } from 'react';
import { Space } from 'antd';
import { RadioGroup, RadioGroupItem } from '@signozhq/ui/radio-group';
import { Radio, RadioChangeEvent, Space } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import {
OnboardingMethods,
@@ -11,29 +10,29 @@ export default function SelectMethod(): JSX.Element {
const { selectedMethod, updateSelectedMethod } = useOnboardingContext();
const [value, setValue] = useState(selectedMethod);
const onChange = (next: string): void => {
setValue(next);
updateSelectedMethod(next);
const onChange = (e: RadioChangeEvent): void => {
setValue(e.target.value);
updateSelectedMethod(e.target.value);
};
return (
<div>
<RadioGroup onChange={onChange} value={value}>
<Radio.Group onChange={onChange} value={value}>
<Space direction="vertical">
<RadioGroupItem value={OnboardingMethods.QUICK_START}>
<Radio value={OnboardingMethods.QUICK_START}>
<Typography.Text> Quick Start </Typography.Text> <br />
<small>Send data to SigNoz directly from OpenTelemetry SDK.</small>
</RadioGroupItem>
</Radio>
<RadioGroupItem value={OnboardingMethods.RECOMMENDED_STEPS}>
<Radio value={OnboardingMethods.RECOMMENDED_STEPS}>
<Typography.Text> Use Recommended Steps </Typography.Text> <br />
<small>
Send data to SigNoz via OpenTelemetry Collector (better control on data
you send to SigNoz, collect host metrics & logs).
</small>
</RadioGroupItem>
</Radio>
</Space>
</RadioGroup>
</Radio.Group>
</div>
);
}

View File

@@ -1,9 +0,0 @@
.format-field-toggle-group {
display: flex;
text-align: center;
> button {
font-size: 0.75rem;
flex: 1;
}
}

View File

@@ -1,24 +1,21 @@
import './FormatField.styles.scss';
import { useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { ToggleGroupSimple } from '@signozhq/ui/toggle-group';
import { LogViewMode } from 'container/LogsTable';
import { RadioChangeEvent } from 'antd';
import { FieldTitle } from '../styles';
import { OptionsMenuConfig } from '../types';
import { FormatFieldWrapper } from './styles';
import { FormatFieldWrapper, RadioButton, RadioGroup } from './styles';
function FormatField({ config }: FormatFieldProps): JSX.Element | null {
const { t } = useTranslation(['trace']);
const onChange = useCallback(
(value: string) => {
(event: RadioChangeEvent) => {
if (!config) {
return;
}
config.onChange(value as LogViewMode);
config.onChange(event.target.value);
},
[config],
);
@@ -30,18 +27,16 @@ function FormatField({ config }: FormatFieldProps): JSX.Element | null {
return (
<FormatFieldWrapper direction="vertical">
<FieldTitle>{t('options_menu.format')}</FieldTitle>
<ToggleGroupSimple
type="single"
size="sm"
<RadioGroup
size="small"
buttonStyle="solid"
value={config.value}
onChange={onChange}
className="format-field-toggle-group"
items={[
{ value: 'raw', label: t('options_menu.raw') },
{ value: 'list', label: t('options_menu.default') },
{ value: 'table', label: t('options_menu.column') },
]}
/>
>
<RadioButton value="raw">{t('options_menu.raw')}</RadioButton>
<RadioButton value="list">{t('options_menu.default')}</RadioButton>
<RadioButton value="table">{t('options_menu.column')}</RadioButton>
</RadioGroup>
</FormatFieldWrapper>
);
}

View File

@@ -1,7 +1,17 @@
import { Space } from 'antd';
import { Radio, Space } from 'antd';
import styled from 'styled-components';
export const FormatFieldWrapper = styled(Space)`
width: 100%;
margin-bottom: 1.125rem;
`;
export const RadioGroup = styled(Radio.Group)`
display: flex;
text-align: center;
`;
export const RadioButton = styled(Radio.Button)`
font-size: 0.75rem;
flex: 1;
`;

View File

@@ -1,7 +1,6 @@
import { useState } from 'react';
import { EyeOpen } from '@signozhq/icons';
import { Modal } from 'antd';
import { Divider } from '@signozhq/ui/divider';
import { Divider, Modal } from 'antd';
import logEvent from 'api/common/logEvent';
import PipelineProcessingPreview from 'container/PipelinePage/PipelineListsView/Preview/PipelineProcessingPreview';
import { PipelineData } from 'types/api/pipeline/def';

View File

@@ -8,6 +8,7 @@ import {
FormInstance,
Input,
Modal,
Radio,
Select,
SelectProps,
Spin,
@@ -54,8 +55,6 @@ import {
} from './PlannedDowntimeutils';
import './PlannedDowntime.styles.scss';
import { RadioGroupItem } from '@signozhq/ui/radio-group';
import { RadioGroup } from '@signozhq/ui/radio-group';
dayjs.locale('en');
dayjs.extend(utc);
@@ -471,10 +470,10 @@ export function PlannedDowntimeForm(
initialValue="specific"
className="alert-rule-scope"
>
<RadioGroup>
<RadioGroupItem value="all">All alert rules</RadioGroupItem>
<RadioGroupItem value="specific">Specific alert rules</RadioGroupItem>
</RadioGroup>
<Radio.Group>
<Radio value="all">All alert rules</Radio>
<Radio value="specific">Specific alert rules</Radio>
</Radio.Group>
</Form.Item>
{alertRuleScope === 'specific' && (
<>

View File

@@ -0,0 +1,18 @@
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
export type QueryProps = {
index: number;
isAvailableToDisable: boolean;
query: IBuilderQuery;
queryVariant?: 'static' | 'dropdown';
isListViewPanel?: boolean;
showFunctions?: boolean;
version: string;
showSpanScopeSelector?: boolean;
showOnlyWhereClause?: boolean;
showTraceOperator?: boolean;
hasTraceOperator?: boolean;
signalSource?: string;
isMultiQueryAllowed?: boolean;
} & Pick<QueryBuilderProps, 'filterConfigs' | 'queryComponents'>;

View File

@@ -0,0 +1,8 @@
.qb-search-container {
display: block;
position: relative;
}
.qb-container {
padding: 0 24px;
}

View File

@@ -0,0 +1,628 @@
/* eslint-disable sonarjs/cognitive-complexity */
// ** Hooks
import {
ChangeEvent,
memo,
ReactNode,
useCallback,
useMemo,
useState,
} from 'react';
import { useLocation } from 'react-use';
import { Col, Input, Row, Tooltip } from 'antd';
import { Typography } from '@signozhq/ui/typography';
import { ENTITY_VERSION_V4 } from 'constants/app';
// ** Constants
import { ATTRIBUTE_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
import ROUTES from 'constants/routes';
// ** Components
import {
AdditionalFiltersToggler,
DataSourceDropdown,
FilterLabel,
} from 'container/QueryBuilder/components';
import {
AggregatorFilter,
GroupByFilter,
HavingFilter,
MetricNameSelector,
OperatorsSelect,
OrderByFilter,
ReduceToFilter,
} from 'container/QueryBuilder/filters';
import AggregateEveryFilter from 'container/QueryBuilder/filters/AggregateEveryFilter';
import LimitFilter from 'container/QueryBuilder/filters/LimitFilter/LimitFilter';
import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch';
import QueryBuilderSearchV2 from 'container/QueryBuilder/filters/QueryBuilderSearchV2/QueryBuilderSearchV2';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
import { DataSource } from 'types/common/queryBuilder';
import { transformToUpperCase } from 'utils/transformToUpperCase';
import QBEntityOptions from '../QBEntityOptions/QBEntityOptions';
import SpaceAggregationOptions from '../SpaceAggregationOptions/SpaceAggregationOptions';
// ** Types
import { QueryProps } from './Query.interfaces';
import './Query.styles.scss';
export const Query = memo(function Query({
index,
queryVariant,
query,
filterConfigs,
queryComponents,
isListViewPanel = false,
showFunctions = false,
version,
}: QueryProps): JSX.Element {
const { panelType, currentQuery, cloneQuery } = useQueryBuilder();
const { pathname } = useLocation();
const [isCollapse, setIsCollapsed] = useState(false);
const {
operators,
spaceAggregationOptions,
isMetricsDataSource,
isTracePanelType,
listOfAdditionalFilters,
handleChangeAggregatorAttribute,
handleChangeQueryData,
handleChangeDataSource,
handleChangeOperator,
handleSpaceAggregationChange,
handleDeleteQuery,
handleQueryFunctionsUpdates,
} = useQueryOperations({
index,
query,
filterConfigs,
isListViewPanel,
entityVersion: version,
});
const handleChangeAggregateEvery = useCallback(
(value: IBuilderQuery['stepInterval']) => {
handleChangeQueryData('stepInterval', value);
},
[handleChangeQueryData],
);
const handleChangeLimit = useCallback(
(value: IBuilderQuery['limit']) => {
handleChangeQueryData('limit', value);
},
[handleChangeQueryData],
);
const handleChangeHavingFilter = useCallback(
(value: IBuilderQuery['having']) => {
handleChangeQueryData('having', value);
},
[handleChangeQueryData],
);
const handleChangeOrderByKeys = useCallback(
(value: IBuilderQuery['orderBy']) => {
handleChangeQueryData('orderBy', value);
},
[handleChangeQueryData],
);
const handleToggleDisableQuery = useCallback(() => {
handleChangeQueryData('disabled', !query.disabled);
}, [handleChangeQueryData, query]);
const handleChangeTagFilters = useCallback(
(value: IBuilderQuery['filters']) => {
handleChangeQueryData('filters', value);
},
[handleChangeQueryData],
);
const handleChangeReduceTo = useCallback(
(value: IBuilderQuery['reduceTo']) => {
handleChangeQueryData('reduceTo', value);
},
[handleChangeQueryData],
);
const handleChangeGroupByKeys = useCallback(
(value: IBuilderQuery['groupBy']) => {
handleChangeQueryData('groupBy', value);
},
[handleChangeQueryData],
);
const handleChangeQueryLegend = useCallback(
(event: ChangeEvent<HTMLInputElement>) => {
handleChangeQueryData('legend', event.target.value);
},
[handleChangeQueryData],
);
const handleToggleCollapsQuery = (): void => {
setIsCollapsed(!isCollapse);
};
const renderOrderByFilter = useCallback((): ReactNode => {
if (queryComponents?.renderOrderBy) {
return queryComponents.renderOrderBy({
query,
onChange: handleChangeOrderByKeys,
});
}
return (
<OrderByFilter
entityVersion={version}
query={query}
onChange={handleChangeOrderByKeys}
isListViewPanel={isListViewPanel}
/>
);
}, [
queryComponents,
query,
version,
handleChangeOrderByKeys,
isListViewPanel,
]);
const renderAggregateEveryFilter = useCallback(
(): JSX.Element | null =>
!filterConfigs?.stepInterval?.isHidden ? (
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="Aggregate Every" />
</Col>
<Col flex="1 1 6rem">
<AggregateEveryFilter
query={query}
disabled={filterConfigs?.stepInterval?.isDisabled || false}
onChange={handleChangeAggregateEvery}
/>
</Col>
</Row>
) : null,
[
filterConfigs?.stepInterval?.isHidden,
filterConfigs?.stepInterval?.isDisabled,
query,
handleChangeAggregateEvery,
],
);
const isExplorerPage = useMemo(
() =>
pathname === ROUTES.LOGS_EXPLORER || pathname === ROUTES.TRACES_EXPLORER,
[pathname],
);
const renderAdditionalFilters = useCallback((): ReactNode => {
switch (panelType) {
case PANEL_TYPES.TIME_SERIES: {
return (
<>
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="Limit" />
</Col>
<Col flex="1 1 12.5rem">
<LimitFilter query={query} onChange={handleChangeLimit} />
</Col>
</Row>
</Col>
<Col span={24}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="HAVING" />
</Col>
<Col flex="1 1 12.5rem">
<HavingFilter
entityVersion={version}
onChange={handleChangeHavingFilter}
query={query}
/>
</Col>
</Row>
</Col>
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="Order by" />
</Col>
<Col flex="1 1 12.5rem">{renderOrderByFilter()}</Col>
</Row>
</Col>
<Col span={11}>{renderAggregateEveryFilter()}</Col>
</>
);
}
case PANEL_TYPES.VALUE: {
return (
<>
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="HAVING" />
</Col>
<Col flex="1 1 12.5rem">
<HavingFilter
onChange={handleChangeHavingFilter}
entityVersion={version}
query={query}
/>
</Col>
</Row>
</Col>
<Col span={11}>{renderAggregateEveryFilter()}</Col>
</>
);
}
default: {
return (
<>
{!filterConfigs?.limit?.isHidden && (
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="Limit" />
</Col>
<Col flex="1 1 12.5rem">
<LimitFilter query={query} onChange={handleChangeLimit} />
</Col>
</Row>
</Col>
)}
{!filterConfigs?.having?.isHidden && (
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="HAVING" />
</Col>
<Col flex="1 1 12.5rem">
<HavingFilter
entityVersion={version}
onChange={handleChangeHavingFilter}
query={query}
/>
</Col>
</Row>
</Col>
)}
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<FilterLabel label="Order by" />
</Col>
<Col flex="1 1 12.5rem">{renderOrderByFilter()}</Col>
</Row>
</Col>
<Col span={11}>{renderAggregateEveryFilter()}</Col>
</>
);
}
}
}, [
panelType,
query,
handleChangeLimit,
version,
handleChangeHavingFilter,
renderOrderByFilter,
renderAggregateEveryFilter,
filterConfigs?.limit?.isHidden,
filterConfigs?.having?.isHidden,
]);
const disableOperatorSelector =
!query?.aggregateAttribute?.key || query?.aggregateAttribute?.key === '';
const isVersionV4 = version && version === ENTITY_VERSION_V4;
return (
<Row gutter={[0, 12]} className={`query-builder-${version}`}>
<QBEntityOptions
isMetricsDataSource={isMetricsDataSource}
showFunctions={
(version && version === ENTITY_VERSION_V4) ||
query.dataSource === DataSource.LOGS ||
showFunctions ||
false
}
isCollapsed={isCollapse}
entityType="query"
entityData={query}
onToggleVisibility={handleToggleDisableQuery}
onDelete={handleDeleteQuery}
onCloneQuery={cloneQuery}
onCollapseEntity={handleToggleCollapsQuery}
query={query}
onQueryFunctionsUpdates={handleQueryFunctionsUpdates}
showDeleteButton={currentQuery.builder.queryData.length > 1}
isListViewPanel={isListViewPanel}
index={index}
queryVariant={queryVariant}
/>
{!isCollapse && (
<Row gutter={[0, 12]} className="qb-container">
<Col span={24}>
<Row align="middle" gutter={[5, 11]}>
{!isExplorerPage && (
<Col>
{queryVariant === 'dropdown' ? (
<DataSourceDropdown
onChange={handleChangeDataSource}
value={query.dataSource}
style={{ minWidth: '5.625rem' }}
isListViewPanel={isListViewPanel}
/>
) : (
<FilterLabel label={transformToUpperCase(query.dataSource)} />
)}
</Col>
)}
{isMetricsDataSource && (
<Col span={12}>
<Row gutter={[11, 5]}>
{version && version === 'v3' && (
<Col flex="5.93rem">
<Tooltip
title={
<div style={{ textAlign: 'center' }}>
Select Aggregate Operator
<Typography.Link
className="learn-more"
href="https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=query-builder#aggregation"
target="_blank"
style={{ textDecoration: 'underline' }}
>
{' '}
<br />
Learn more
</Typography.Link>
</div>
}
>
<OperatorsSelect
value={query.aggregateOperator || ''}
onChange={handleChangeOperator}
operators={operators}
/>
</Tooltip>
</Col>
)}
<Col flex="auto">
<MetricNameSelector
onChange={handleChangeAggregatorAttribute}
query={query}
/>
</Col>
{version &&
version === ENTITY_VERSION_V4 &&
operators &&
Array.isArray(operators) &&
operators.length > 0 && (
<Col flex="5.93rem">
<Tooltip
title={
<div style={{ textAlign: 'center' }}>
Select Aggregate Operator
<Typography.Link
className="learn-more"
href="https://signoz.io/docs/metrics-management/types-and-aggregation/?utm_source=product&utm_medium=query-builder#aggregation"
target="_blank"
style={{ textDecoration: 'underline' }}
>
{' '}
<br />
Learn more
</Typography.Link>
</div>
}
>
<OperatorsSelect
value={query.aggregateOperator || ''}
onChange={handleChangeOperator}
operators={operators}
disabled={disableOperatorSelector}
/>
</Tooltip>
</Col>
)}
</Row>
</Col>
)}
<Col flex="1 1 40rem">
<Row gutter={[11, 5]}>
{isMetricsDataSource && (
<Col>
<FilterLabel label="WHERE" />
</Col>
)}
<Col flex="1" className="qb-search-container">
{[DataSource.LOGS, DataSource.TRACES].includes(query.dataSource) ? (
<QueryBuilderSearchV2
query={query}
onChange={handleChangeTagFilters}
whereClauseConfig={filterConfigs?.filters}
hideSpanScopeSelector={query.dataSource !== DataSource.TRACES}
/>
) : (
<QueryBuilderSearch
query={query}
onChange={handleChangeTagFilters}
whereClauseConfig={filterConfigs?.filters}
/>
)}
</Col>
</Row>
</Col>
</Row>
</Col>
{!isMetricsDataSource && !isListViewPanel && (
<Col span={11}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
<Tooltip
title={
<div style={{ textAlign: 'center' }}>
Select Aggregate Operator
<Typography.Link
href="https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=query-builder#aggregation"
target="_blank"
style={{ textDecoration: 'underline' }}
>
{' '}
<br />
Learn more
</Typography.Link>
</div>
}
>
<OperatorsSelect
value={query.aggregateOperator || ''}
onChange={handleChangeOperator}
operators={operators}
/>
</Tooltip>
</Col>
<Col flex="1 1 12.5rem">
<AggregatorFilter
query={query}
onChange={handleChangeAggregatorAttribute}
disabled={
panelType === PANEL_TYPES.LIST || panelType === PANEL_TYPES.TRACE
}
/>
</Col>
</Row>
</Col>
)}
{!isListViewPanel && (
<Col span={24}>
<Row gutter={[11, 5]}>
<Col flex="5.93rem">
{isVersionV4 && isMetricsDataSource ? (
<SpaceAggregationOptions
panelType={panelType}
key={`${panelType}${query.spaceAggregation}${query.timeAggregation}`}
aggregatorAttributeType={
query?.aggregateAttribute?.type as ATTRIBUTE_TYPES
}
selectedValue={query.spaceAggregation}
disabled={disableOperatorSelector}
onSelect={handleSpaceAggregationChange}
operators={spaceAggregationOptions}
/>
) : (
<FilterLabel
label={panelType === PANEL_TYPES.VALUE ? 'Reduce to' : 'Group by'}
/>
)}
</Col>
<Col flex="1 1 12.5rem">
{panelType === PANEL_TYPES.VALUE ? (
<Row>
{isVersionV4 && isMetricsDataSource && (
<Col span={4}>
<FilterLabel label="Reduce to" />
</Col>
)}
<Col span={isVersionV4 && isMetricsDataSource ? 20 : 24}>
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
</Col>
</Row>
) : (
<GroupByFilter
disabled={isMetricsDataSource && !query.aggregateAttribute?.key}
query={query}
onChange={handleChangeGroupByKeys}
/>
)}
</Col>
{isVersionV4 &&
isMetricsDataSource &&
(panelType === PANEL_TYPES.TABLE || panelType === PANEL_TYPES.PIE) && (
<Col flex="1 1 12.5rem">
<Row>
<Col span={6}>
<FilterLabel label="Reduce to" />
</Col>
<Col span={18}>
<ReduceToFilter query={query} onChange={handleChangeReduceTo} />
</Col>
</Row>
</Col>
)}
</Row>
</Col>
)}
{!isTracePanelType && !isListViewPanel && (
<Col span={24}>
<AdditionalFiltersToggler
listOfAdditionalFilter={listOfAdditionalFilters}
>
<Row gutter={[0, 11]} justify="space-between">
{renderAdditionalFilters()}
</Row>
</AdditionalFiltersToggler>
</Col>
)}
{isListViewPanel && (
<Col span={24}>
<Row gutter={[0, 11]} justify="space-between">
{renderAdditionalFilters()}
</Row>
</Col>
)}
{panelType !== PANEL_TYPES.LIST && panelType !== PANEL_TYPES.TRACE && (
<Row style={{ width: '100%' }}>
<Tooltip
placement="right"
title={
<div style={{ textAlign: 'center' }}>
Name of legend
<Typography.Link
style={{ textDecoration: 'underline' }}
href="https://signoz.io/docs/userguide/query-builder/?utm_source=product&utm_medium=query-builder#legend-format"
target="_blank"
>
{' '}
<br />
Learn more
</Typography.Link>
</div>
}
>
<Input
onChange={handleChangeQueryLegend}
size="middle"
value={query.legend}
addonBefore="Legend Format"
/>
</Tooltip>
</Row>
)}
</Row>
)}
</Row>
);
});

View File

@@ -0,0 +1 @@
export { Query } from './Query';

View File

@@ -5,3 +5,4 @@ export { Formula } from './Formula';
export { HavingFilterTag } from './HavingFilterTag';
export { ListItemWrapper } from './ListItemWrapper';
export { ListMarker } from './ListMarker';
export { Query } from './Query';

View File

@@ -1,6 +1,4 @@
import { IQueryBuilderState } from 'constants/queryBuilder';
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
export interface InitialStateI {
search: string;
@@ -20,19 +18,3 @@ export type Option = {
isIndexed?: boolean;
type?: string;
};
export type QueryProps = {
index: number;
isAvailableToDisable: boolean;
query: IBuilderQuery;
queryVariant?: 'static' | 'dropdown';
isListViewPanel?: boolean;
showFunctions?: boolean;
version: string;
showSpanScopeSelector?: boolean;
showOnlyWhereClause?: boolean;
showTraceOperator?: boolean;
hasTraceOperator?: boolean;
signalSource?: string;
isMultiQueryAllowed?: boolean;
} & Pick<QueryBuilderProps, 'filterConfigs' | 'queryComponents'>;

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