Compare commits

..

116 Commits

Author SHA1 Message Date
Srikanth Chekuri
403e41d460 Merge branch 'develop' into alert-link-filter 2024-05-31 19:36:40 +05:30
Srikanth Chekuri
4a8101b4be chore: fix the formula alert link 2024-05-31 19:33:24 +05:30
Srikanth Chekuri
67779d6c2c chore: scheduled maintenance query-service impl (#4834) 2024-05-31 17:43:13 +05:30
Srikanth Chekuri
f927969c7d fix: do not consider Inf and NaN for formula (#5124) 2024-05-31 16:32:22 +05:30
Vikrant Gupta
ba0f63ad1e fix: random sizing issue with alerts query builder (#5116)
* fix: alerts qb rendering

* fix: picker z-index

* fix: intercom z-index

* fix: add antd z-index chart

* fix: add antd z-index chart
2024-05-31 13:52:52 +05:30
Vikrant Gupta
0760917a4b feat: enale temporary sorting on table panel type (#5114) 2024-05-31 13:52:07 +05:30
Vikrant Gupta
6aded04b7f fix: page jumping for alerts (#5119) 2024-05-31 13:37:27 +05:30
Srikanth Chekuri
70bc72b52f chore: add annotation for alert link 2024-05-31 08:18:55 +05:30
Srikanth Chekuri
63effdc2ee feat: alert page link with filter applied for notification 2024-05-30 23:28:05 +05:30
Vikrant Gupta
b849705710 chore: added theme analytics for the users (#5097)
* chore: added theme analytics for the users

* chore: added error handling

* fix: refactor code
2024-05-29 19:29:53 +05:30
Vikrant Gupta
c913c8bf20 fix: clone dashboard panel intermittent issue (#5096) 2024-05-29 18:59:15 +05:30
Vikrant Gupta
1328f05d78 fix: dashboard design feedback (#5104)
* fix: add createdby and createdAt by default and show not be configurable

* fix: make the checkbox always checked for createdby and createdat

* fix: decrease the margins for the graph and make it compact

* fix: decrease font size for metadata
2024-05-29 18:58:02 +05:30
Vikrant Gupta
1db1f76a72 fix: dashboard editor width fixes on large screens (#5101)
* fix: dashboard editor width fixes on large screens

* fix: label fix
2024-05-29 12:28:37 +05:30
Yunus M
932d892d9e feat: dashboard revamp according to the latest designs (#4868)
* feat: dashboard list view

* feat: update sort menu items

* feat: wire up add / import dashboard functionss

* feat: update import json styles

* feat: new dashboard templates modal

* feat: add template filter logic

* feat: revamp the overview settings modal (#4894)

* feat: revamp the overview settings modal

* feat: dashboard settings variable landing page

* feat: dashboard add variable button settings

* feat: add variable modal changes

* feat: handle the unsaved changes for general settings

* feat: follow ups for side panel section for dashboards  (#4906)

* feat: changes for tags input

* feat: side panel header styles

* feat: changes for textbox variable

* feat: handle changes for custom type variable

* feat: overflow preview vales

* feat: overflow preview vales

* feat: setup for new dashboard landing page (#4921)

* feat: setup for new dashboard landing page

* feat: added empty state widgets

* feat: added functionality to the configure and the add panel button

* feat: tag variables changes

* feat: dashboard revamp changes follow ups (#4929)

* feat: changes for new panel type modal

* fix: added missing / in the breadcrumbs

* feat: added dashboard settings menu items

* feat: added dashboard rename modal

* feat: move full screen handle a few components up

* feat: handle rename and copy export changes

* feat: minor cleanup

* feat: delete button changes

* feat: dashboard widget edit page design revamp (#4946)

* feat: dashboard edit page base setup

* feat: right container design revamp

* feat: alerts and thresholds changes right container

* feat: right container

* feat: fix graph styles

* fix: some edits for dashboard edit page

* feat: threshold preview changes (#4990)

* feat: threshold preview changes

* feat: threshold preview changes

* feat: threshold discard handler

* fix: remove the horizontal scroll from the dashboards landing page

* fix: added margin to dashboard widgets (#4991)

* fix: rebase conflicts

* feat: dashboard panel grouping change for new designs (#4992)

* feat: dashboard panel groping base cleanups

* feat: move add panel code from inner component to parent component

* feat: new dashboard section panel naming modal

* feat: dashboard panel groping base cleanups

* feat: grip changes

* feat: dashboard list page revamp and functionality additions (#4994)

* feat: fix types and code structure for list page

* feat: dashboard actions

* feat: design changes for tags

* feat: design changes for tags

* feat: update import json styles

* feat: added all dashboards row

* feat: added configure metadata linking

* feat: added local storage changes for dynamic columns

* feat: added user metadata display for metadata modal

* feat: configure metadata final changes

* feat: handle dashboard list loading state

* feat: sort and pagination changes for dashboard list view designs (#4996)

* feat: minor list view css changes

* feat: added sort functionality to the dashboards list

* feat: added sort functionality to the dashboards list

* feat: added name dropdown in the settings drawer and image as base64 format (#5000)

* feat: added name input in settings drawer

* feat: discard handler

* feat: implemented the name dropdown

* feat: added dashboard list page header

* fix: margin of dashboard list container

* feat: dashboard empty state (#5005)

* feat: light mode changes for new dashboard revamp (#5006)

* feat: light mode changes for dahsboards list page

* feat: dashboard description landing page changes

* feat: variable panels landing page light theme changes

* feat: dashboard edit panel light mode

* feat: added dashboard list error state (#5011)

* feat: added missing light mode designs

* fix: usability / customer issues  (#5014)

* fix: [GH-4986]: preview values not getting updated when the query result is empty

* fix: [GH-4985]: fix the usability of dahsboards variables drawer

* fix: light mode design for component slider

* fix: code cleanup

* fix: 0 being added in case of no tags

* fix: minor styling fixes

* fix: handle silent error for dashboard edit mutation (#5022)

* fix: handle silent error for dashboard edit mutation

* fix: handle silent error for dashboard edit mutation

* fix: rbac changes

* fix: grip icon color

* fix: new dashboards feedback from testing (#5030)

* fix: hide create new dashboards from viewer roles

* fix: move the elipsis button right of date time picker and make it a button

* fix: remove duplicate button from actions for now

* fix: last updated by and created by difference

* fix: hide intercom for modals

* fix: actions popover not closing

* fix: temp remove templates modal from normal flow

* fix: delete button event propagation

* fix: minor UI fixes (#5032)

* fix: update dashboards landing page icons

* fix: added lock icon for locked dashboards

* fix: updated dashboards list page styles

* fix: comment out filters code for phase 2 (#5034)

* fix: dashboard revamp ui fixes  (#5037)

* fix: increase the width of the graph section

* fix: remove select and next from component slider

* Dashboard vqa 1 (#5090)

* fix: dashboard VQA pt 1

* fix: dashboard VQA pt 2

* fix: dashboard VQA pt 3

* fix: dashboard VQA pt 4

* fix: dashboard VQA pt 5

* fix: dashboard VQA pt 6

* fix: dashboard VQA pt 7

* fix: added dashboard locked footer and base64 icons (#5091)

* fix: added dashboard locked footer

* fix: update base64 images

* fix: dashboard delete hover and row actions refactor

* fix: dashboard vqa pt 2 (#5098)

* fix: review comments

* fix: alerts plot tag spacing

* fix: css variables update

---------

Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
2024-05-28 19:09:04 +05:30
Vikrant Gupta
3538815331 fix: query range api triggering on every keystroke (#5093) 2024-05-27 22:09:40 +05:30
Vibhu Pandey
956a4d081d fix(github): expire ssh keys after 15m (#5094)
* fix(github): expire ssh keys after 5m

* fix(github): bump expiry to 15m
2024-05-27 20:31:29 +05:30
Srikanth Chekuri
10b543dff1 chore: make time macros available for all query types (#5092) 2024-05-27 19:22:18 +05:30
Vishal Sharma
96162d7949 fix: handle-large-traces (#4903)
* fix: handle-large-traces

* feat: add isSubTree key to identify subTrees
Show user a loom video explaining how to navigate large spans

* chore: update icon to warning

* chore: fire telemetry events for all trace detail API calls, large traces

* chore: update MAX_SPANS_IN_TRACE to 250k
2024-05-27 17:20:45 +05:30
Prashant Shahi
3085093130 Merge pull request #5084 from SigNoz/chore/post-release-v0.46
post-release: sync main changes in develop
2024-05-27 15:24:06 +05:30
Vikrant Gupta
83d0ddeec0 fix: do not route back to overview tab when changing the resource attributes (#5058)
* fix: do not route back to overview tab when changing the resource attributes

* fix: retain the resource attributes in query params on tab change

* feat: added jest test case for the same

---------

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2024-05-27 15:23:06 +05:30
Nityananda Gohain
ab444af8e6 fix: escape string for contains and ncontains (#5083)
* fix: escape string for contains and ncontains

* fix: add changes to json and traces builder

---------

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2024-05-27 15:18:49 +05:30
Vikrant Gupta
749fba67cb fix: respect the query labels array for ordering than labels object (#5041)
* fix: respect the query labels array for ordering than labels object

* fix: remove console log

* fix: type check

---------

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2024-05-27 15:06:54 +05:30
Prashant Shahi
fe96a78ee8 Merge branch 'develop' into chore/post-release-v0.46 2024-05-27 13:35:06 +05:30
Prashant Shahi
f77089da55 chore: remove intermediate golang versions file
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-05-27 13:34:26 +05:30
Srikanth Chekuri
1d1d85efa3 chore: default zero only for counters and label normalization (#5085) 2024-05-27 13:19:28 +05:30
Vishal Sharma
c1c5c4dfa8 chore: deprecate old columns in traces (#5016)
Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2024-05-27 11:40:34 +05:30
Prashant Shahi
9eab315f76 Merge branch 'main' into chore/post-release-v0.46 2024-05-27 11:16:42 +05:30
Vikrant Gupta
1d86e5eb50 fix: page breaking when all queries are hidden (#5080)
* fix: page breaking when all queries are hidden

* fix: page breaking when all queries are hidden
2024-05-24 22:48:53 +05:30
Srikanth Chekuri
2f7495c6e4 fix: add back the templating for query (#5079) 2024-05-24 21:29:13 +05:30
Srikanth Chekuri
1369fe1912 fix: remove series with no points (#5077) 2024-05-24 18:56:30 +05:30
Vikrant Gupta
76b1e40cbc feat: added download as csv support for table panel type (#5067)
* feat: added download as csv support for table panel type

* feat: update the position of download

* fix: build issues

* fix: address review comments
2024-05-24 15:54:36 +05:30
SagarRajput-7
52e4c2d8ff chore: added pipeline list view, edit action and expanded views test cases (#4938)
* chore: added pipeline list view, edit action and expanded views

* chore: added tests for pipeline list view action, expand content delete and toggle action

* chore: added tests for pipeline delete and toggle action

* chore: added tests for pipeline delete and toggle action
2024-05-24 14:59:47 +05:30
SagarRajput-7
7e79900973 feat: [SIG-582]: added planned maintenance create dialog (#4863)
* feat: [SIG-582]: added planned maintenance create dialog

* feat: [SIG-582]: added planned maintenance - listing

* feat: [SIG-582]: added alert rule select

* feat: [SIG-582]: added - planned maintenance list and createform ui fixes

* feat: [SIG-582]: added - alert-rule tag styles

* feat: [SIG-582]: added - style changes

* feat: [SIG-582]: added - crud API integration and delete modal

* feat: [SIG-582]: added - reccurrence form details

* feat: [SIG-582]: added - duration and timezone

* feat: [SIG-582]: removed console logs

* feat: [SIG-582]: added - form validation for duration and endTime

* feat: [SIG-582]: code refactor

* feat: [SIG-582]: code refactor

* feat: [SIG-582]: code refactor

* feat: [SIG-582]: code refactor

* feat: [SIG-582]: light mode styles

* feat: [SIG-582]: code refactor

* feat: [SIG-582]: code refactor and comment resolve

* feat: [SIG-582]: code refactor and removed filters

* feat: [SIG-582]: changed coming up on to start time

* feat: [SIG-582]: added planned downtime behind FF - PLANNED_MAINTENANCE
2024-05-24 14:02:37 +05:30
Vikrant Gupta
f818a86720 fix: update the metrics datasource version v4 inproduct link (#5073) 2024-05-24 13:42:41 +05:30
Srikanth Chekuri
0cf8817f3f chore: use querier in alerts (#5045) 2024-05-24 12:11:34 +05:30
Vikrant Gupta
0d043bf380 fix: retain the alerts table sorting when editing or discarding the opened alert (#5066) 2024-05-23 22:46:17 +05:30
Vikrant Gupta
3b599ea41a fix: step interval not getting updated on time range change (#5040) 2024-05-23 22:44:28 +05:30
Vikrant Gupta
da74619a46 fix: cursor resetting in dropdown causing wrong query selection (#5063) 2024-05-23 22:43:11 +05:30
Vikrant Gupta
93babc3019 fix: clickhouse builder cursor issue (#5065) 2024-05-23 22:42:50 +05:30
Vikrant Gupta
f8b8080853 feat: added padding bottom to increase the visibility of logs in explorer page (#5069) 2024-05-23 22:39:38 +05:30
Vikrant Gupta
3c2bc06e6a feat: added drag select in time series chart for logs explorer page (#5068)
* feat: added drag select in time series chart for logs explorer page

* fix: handle back navigation properly
2024-05-23 22:39:29 +05:30
Prashant Shahi
a0d866c2ff Merge pull request #5061 from SigNoz/release/v0.46.x
Release/v0.46.x
2024-05-23 09:37:46 +05:30
Prashant Shahi
52c8584e63 chore(signoz): 📌 pin versions: SigNoz 0.46.0, SigNoz OtelCollector 0.88.24
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-05-22 21:45:38 +05:30
Prashant Shahi
9a908c3f76 Merge branch 'main' into release/v0.46.x 2024-05-22 21:42:34 +05:30
SagarRajput-7
4887a1d8dd feat: added helper text, learn-more and doc-link (#5024)
* feat: added helper text, learn-more and doc-link

* feat: added collapse and uncollapse text on hover

* feat: added learn-more doc link for trace & logs explorer

* feat: added learn-more doc link

* feat: added learn-more doc link

* chore: added alert links and minor updates

* feat: added learn-more doc link

---------

Co-authored-by: makeavish <makeavish786@gmail.com>
2024-05-22 16:00:49 +05:30
hulk
f2b0387a1b feat: allow using the consistent naming with the signoz collector (#4865)
Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2024-05-22 12:42:13 +05:30
dependabot[bot]
cbb9fd51f8 --- (#5056)
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 12:15:47 +05:30
Yunus M
10e44ce440 feat: update packages (#5054)
* feat: update packages

* feat: remove jest preview
2024-05-22 11:38:51 +05:30
dependabot[bot]
6827d66ae9 --- (#5048)
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 10:46:03 +05:30
dependabot[bot]
611ec3e08d chore(deps): bump axios from 0.21.4 to 1.6.2 in /frontend (#4839)
Bumps [axios](https://github.com/axios/axios) from 0.21.4 to 1.6.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.21.4...v1.6.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 10:33:36 +05:30
dependabot[bot]
4ab350e721 chore(deps): bump follow-redirects from 1.15.2 to 1.15.6 in /frontend (#4714)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.6.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.6)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 10:33:28 +05:30
Vikrant Gupta
631c12259f Fix promql light theme (#5044)
* fix: promQL icon not visible in light mode

* fix: promql icon

* fix: promQL icon not visible in light mode
2024-05-21 22:54:18 +05:30
Srikanth Chekuri
de497bf5b6 chore: query range v3 metrics use v4 tables (#5021) 2024-05-21 12:01:21 +05:30
Vikrant Gupta
12be6ce020 feat: restrict the rendering of img and form tags in the logs content (#4905)
* feat: restrict the rendering of img tags in the logs content

* fix: forbidden tags code cleanup
2024-05-20 19:41:44 +05:30
Vikrant Gupta
2dbe598b2c fix: retain the query name,expression and disabled property on changing panel type (#4976)
* fix: retain the query name,expression and disabled property on changing panel type

* fix: missing reset and init query for superset query
2024-05-20 18:42:39 +05:30
Srikanth Chekuri
cf64da2631 fix: metrics order by avg (#5029) 2024-05-20 14:22:44 +05:30
Srikanth Chekuri
9ff0e34038 chore: migrate alerts to v4 for supported operators (#5010) 2024-05-17 07:45:03 +05:30
Vikrant Gupta
d313f44556 fix: multiple widgets getting created and hence blocking the delete (#5015)
* fix: multiple widgets getting created and hence blocking the delete

* fix: allow multiple deletes when multiple widgets present with same id

* chore: use the avg for limit

---------

Co-authored-by: Srikanth Chekuri <srikanth.chekuri92@gmail.com>
2024-05-17 07:44:33 +05:30
Raj Kamal Singh
5a778dcb18 Chore: integrations: populate updatedAt for integration dashboards (#5019)
* chore: add test for updatedAt value being populated in integration dashboards and get it passing

* chore: also populate createdAt, createBy and updateBy for instaled integration dashboards

* chore: update clickhouse integration config instructions
2024-05-16 21:44:46 +05:30
Srikanth Chekuri
7e31b4ca01 fix: several issues (#5001) 2024-05-15 18:52:01 +05:30
Raj Kamal Singh
3efd9801a1 Chore: restrict logs connection test for integrations to use log attributes for identifying logs (#4977)
* chore: change logs connection test spec to be based on an attrib value

* chore: disallow unknown fields while unmarshalling JSON for an integration

* chore: add description field to collected metric spec

* chore: update logs connection test for builtin integrations

* chore: update logic for calculating logs connection status
2024-05-15 14:36:52 +05:30
Vishal Sharma
0cbaa17d9f chore: allow unlimited dashboards and alerts in community version (#4989)
* chore: allow unlimited dashboards and alerts in community version

* chore: update ee plan
2024-05-14 18:05:59 +05:30
Nityananda Gohain
30bfad527f chore: enable limits for trace queries (#4997) 2024-05-14 17:03:29 +05:30
Srikanth Chekuri
9f1c45bc32 chore: add toUnixTimestamp to supported functions (#4877) 2024-05-14 10:34:43 +05:30
Vikrant Gupta
51becf7cfb fix: added right padding to the notifications bar to show cancel button (#4969) 2024-05-12 16:45:16 +05:30
Vibhu Pandey
7460e650af feat(workflow): integrate with workflow identity pool (#4945)
* feat(workflows): add wif workflow
* feat(workflows): add name of compute instance
* feat(workflows): fix permissions
* feat(workflows):  add an OR true since github runs with -e
* ci(testing-deployment): include GITHUB envs
* ci(testing-deployment): move GCP information to secrets
* ci(staging-deployment): wif workflow

---------

Co-authored-by: Prashant Shahi <prashant@signoz.io>
2024-05-10 23:23:31 +05:30
Prashant Shahi
a544723bb8 Merge pull request #4973 from SigNoz/release/v0.45
Release/v0.45
2024-05-09 08:46:58 +05:30
Prashant Shahi
eb6f038db5 Merge branch 'main' into release/v0.45 2024-05-08 21:21:45 +05:30
Prashant Shahi
47dcd994f0 chore(signoz): 📌 pin versions: SigNoz 0.45.0, SigNoz OtelCollector 0.88.22
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-05-08 20:03:12 +05:30
Vikrant Gupta
211fe4fdd5 fix: prevent page from crashing in case items in filters is null (#4964)
* fix: prevent page from crashing in case items in filters is null

* fix: added null check for filters as well
2024-05-06 19:18:27 +05:30
Vikrant Gupta
e2992b42c1 fix: make integrations available for the ee cloud user (#4963) 2024-05-06 19:17:50 +05:30
Nityananda Gohain
3957d91a9b fix: add read-in-order config (#4918) 2024-05-06 15:01:53 +05:30
Vishal Sharma
967aa16f21 feat: sort tags and events in trace detail (#4962) 2024-05-05 09:03:31 +05:30
Vikrant Gupta
08b1a87cb5 Revert "fix: step interval not getting updated on time range change (#4944)" (#4955)
This reverts commit 5c1c09c790.
2024-05-01 22:46:32 +05:30
SagarRajput-7
03ddcdd20e feat: added test cases for pipeline pages (#4872)
* feat: added test cases for pipeline pages

* feat: added test cases for changeHistory

* chore: change history table test case added

* chore: added create pipeline button test cases

* chore: updated useAnalytics mocking
2024-05-01 18:49:04 +05:30
SagarRajput-7
1aec7f3ca6 feat: added tooltips for facing issue btn (#4948) 2024-05-01 18:36:56 +05:30
Vikrant Gupta
241edcb88a fix: text change for saved views in traces (#4953) 2024-05-01 18:28:05 +05:30
Srikanth Chekuri
27d12871af chore: disallow small step intervals for large durations (#4950) 2024-05-01 17:03:46 +05:30
Yunus M
e78e1d4b63 fix: add safety checks to handle null response from query range API (#4939) 2024-05-01 15:49:30 +05:30
Yunus M
64bf580323 feat: show milliseconds in timestamp in logs views (#4949)
* feat: show milliseconds in timestamp in logs views

* fix: remove console log

---------

Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
2024-05-01 15:27:48 +05:30
SagarRajput-7
152aa4b518 fix: fixed facing issue btn alignment issue (#4936)
* fix: fixed facing issue btn alignment issue

* fix: fixed facing issue btn alignment issue

* fix: moved intercom help messages to util file
2024-05-01 14:49:42 +05:30
Vikrant Gupta
b3d5831574 fix: ch queries sending builder as query type in query range api for exceptions alerts (#4941)
* fix: ch queries sending builder as query type in query range api for exceptions alerts

* fix: ch queries sending builder as query type in query range api for exceptions alerts

* fix: alerts routing from logs explorer and dashboards
2024-05-01 14:39:39 +05:30
Vikrant Gupta
b85b9f42ed fix: time interval not getting updated in case of edit dashboard (#4940) 2024-05-01 13:00:18 +05:30
Vikrant Gupta
5c1c09c790 fix: step interval not getting updated on time range change (#4944) 2024-05-01 12:47:33 +05:30
Vishal Sharma
33960b05fd chore: update facing issues text (#4942) 2024-04-30 23:38:15 +05:30
Vikrant Gupta
191d9b0648 feat: introducing collapsable rows for dashboards (#4806)
* feat: dashboard panel grouping initial setup

* feat: added panel map to the dashboard response and subsequent types for the same

* feat: added panel map to the dashboard response and subsequent types for the same

* feat: added settings modal

* feat: handle panel collapse and open changes

* feat: handle creating panel map when dashboard layout changes

* feat: handle creating panel map when dashboard layout changes

* feat: refactor code

* feat: handle multiple collapsable rows

* fix: type issues

* feat: handle row collapse button and scroll

* feat: handle y axis movement for rows

* feat: handle delete row

* feat: handle settings name change

* feat: disable collapse/uncollapse when dashboard loading to avoid async states

* feat: decrease the height of the grouping row

* fix: row height management

* fix: handle empty row case

* feat: remove resize handle from the row

* feat: handle re-arrangement of panels

* feat: increase height of default new widget

* feat: added safety checks
2024-04-30 14:36:47 +05:30
Srikanth Chekuri
7d81bc3417 fix: value panel restriction should be on enabled queries (#4934) 2024-04-30 09:53:03 +05:30
Srikanth Chekuri
506916661d fix: metric limit works with cache (#4935) 2024-04-30 01:25:50 +05:30
Nityananda Gohain
5326f2d23b fix: dont enrich if non empty keys are not same (#4930)
* fix: dont enrich if non empty keys are not same

* fix: update if any of the type and dataType is empty but other is matching
2024-04-29 22:40:40 +05:30
dependabot[bot]
dfaa344dce chore(deps): bump express from 4.18.2 to 4.19.2 in /frontend (#4840)
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-29 21:29:27 +05:30
SagarRajput-7
882b540a0b chore: [SIG-583]: Jest coverage collection config (#4920)
* chore: [SIG-583]: Jest coverage collection config

* fix: added missing attribute
2024-04-27 11:31:37 +05:30
Ankit Nayan
1c4b579c3d fix: frontend/package.json & frontend/yarn.lock to reduce vulnerabilities (#4341)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-AXIOS-6144788

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
Co-authored-by: Prashant Shahi <prashant@signoz.io>
2024-04-26 15:00:06 +05:30
Yunus M
706f25cc5d fix: frontend/Dockerfile to reduce vulnerabilities (#4913)
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-ALPINE318-EXPAT-6241039
- https://snyk.io/vuln/SNYK-ALPINE318-LIBX11-6042398
- https://snyk.io/vuln/SNYK-ALPINE318-LIBXML2-6245694
- https://snyk.io/vuln/SNYK-ALPINE318-OPENSSL-6032386
- https://snyk.io/vuln/SNYK-ALPINE318-OPENSSL-6032386

Co-authored-by: snyk-bot <snyk-bot@snyk.io>
Co-authored-by: Prashant Shahi <prashant@signoz.io>
2024-04-26 11:41:53 +05:30
Raj Kamal Singh
e6e0a59f5f Feat: integrations: clickhouse (#4879)
* chore: get built-in clickhouse integration started

* chore: update config pre-requisites for clickhouse integration

* chore: add details of metrics data collected for clickhouse integration

* chore: clickhouse integration: move list of data-collected to its own file

* chore: clickhouse integration: get overview dashboard started

* chore: start with logs collection instructions for clickhouse

* chore: regex parsing for clickhouse text logs

* chore: timestamp parsing for clickhouse logs

* chore: severity parsing for clickhouse logs

* chore: clickhouse logs parsing: move parsed message to body if available

* chore: update pre-reqs for collecting from system.query_log table

* feat: add instructions for collecting from system.query_log table

* feat: add logs attribs collected

* chore: some cleanup of clickhouse overview dashboard

* feat: finish up with clickhouse overview dashboard for clickhouse integration
2024-04-26 09:45:57 +05:30
Prashant Shahi
b2c170c752 Merge pull request #4919 from SigNoz/release/v0.44
post-release: sync release changes in develop
2024-04-25 22:13:44 +05:30
Prashant Shahi
ee421af95c Merge pull request #4917 from SigNoz/release/v0.44
Release/v0.44
2024-04-25 19:26:25 +05:30
Prashant Shahi
453be9074d Merge branch 'main' into release/v0.44 2024-04-25 18:07:31 +05:30
Prashant Shahi
3272444e13 chore(signoz): 📌 pin versions: SigNoz 0.44.0
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-04-25 18:01:10 +05:30
Vishal Sharma
71b3e6d522 fix: rate in table panel (#4916)
* fix: rate in table panel

* test: added test cases for rate operation in table panel
2024-04-25 14:15:33 +05:30
Prashant Shahi
6cf7cc9f4f chore: bump SigNoz/prometheus from v1.10.1 to v1.11.0 (#4912)
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-04-25 13:12:30 +05:30
Prashant Shahi
5ec2f17d09 chore: pin SigNoz OtelCollector 0.88.21 and update ClickHouse dsn (#4909)
* chore: 📌 pin versions: SigNoz OtelCollector 0.88.21
* chore(clickhouse): update dsn as per the new parsing logic

---------

Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-04-24 21:15:07 +05:30
Prashant Shahi
a45fb8ec0c fix(clickhouse): update endpoint of the healthcheck in deployment files (#4908)
Signed-off-by: Prashant Shahi <prashant@signoz.io>
2024-04-24 19:06:05 +05:30
SagarRajput-7
bd148bbd5a fix: restrict visibility of facing-issue button to only cloud users with intercom setup (#4907)
* fix: restrict visibilty of facing-issue button to only cloud users with intercom setup

* fix: restrict visibilty of facing-issue button to only cloud users with intercom setup

* fix: added a comment

* fix: added chat support feature flag condition

* fix: added a comment

* fix: changed folder structure
2024-04-24 18:56:19 +05:30
SagarRajput-7
1306e99ca8 fix: alert threshold form is resetting to default query option on stage & run (#4876)
* fix: alert threshold form is resetting to default query option on stage & run

* fix: alert threshold - added safety check when the queryOption is deleted
2024-04-24 15:58:59 +05:30
SagarRajput-7
1a8f063b4b feat: [SIG-585]: Added facingIssueBtn at Dashboard list, detail and alert list, detail etc. pages (#4899)
* feat: [SIG-585]: Added facingIssueBtn at Dashboard list, detail and alert list, detail etc. pages

* feat: [SIG-585]: Added facingIssueBtn for dashboard & alert listing

* feat: [SIG-585]: Added facingIssueBtn for dashboard & alert detail and dashboard panel edit

* feat: [SIG-585]: Code cleanup

* feat: [SIG-585]: Changed logEvent attribute and event content

* feat: [SIG-585]: Changed alignment of button and button text

* feat: [SIG-585]: Changed button color to amber

* feat: [SIG-585]: Code cleanup
2024-04-24 15:48:48 +05:30
SagarRajput-7
c7668b9a78 chore: added jest coverage setup (#4871)
* chore: added jest coverage setup

* chore: added sample test changes

* chore: revert sample code change

* chore: changed script to add yarn install handle and removed checkouts to develop

* chore: added fix for checkout issue

* chore: added fix for checkout issue

* chore: added fix for checkout issue

* chore: added fix for checkout issue

* chore: added sample test changes

* chore: revert sample test cases

* chore: adding coverage threshold

* chore: added coverage threshold and sample test code

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: testing fetch and checkout

* chore: code cleanup and threshold adjustment

* chore: testing fetch and checkout
2024-04-23 20:06:02 +05:30
Vikrant Gupta
5e3dba2587 fix: do not save dashboard panel on creating a new panel if discard is pressed (#4884)
* fix: do not save dashboard panel on creating a new panel if discard is pressed

* fix: remove console log
2024-04-23 19:39:41 +05:30
Vikrant Gupta
374f30e0cd fix: billing container scroll issue when trial banner present (#4893) 2024-04-22 19:31:54 +05:30
Vikrant Gupta
38d2833931 fix: handle the old variables by name instead of id (#4890) 2024-04-20 17:54:29 +05:30
SagarRajput-7
731eacbbca feat: [SIG-584]: moved facing issue btn tracking from trackEvent to logEvent (#4888)
* feat: [SIG-584]: moved facing issue btn tracking from trackEvent to logEvent

* feat: [SIG-584]: removed logEvent from useAnalytic hook
2024-04-20 16:40:47 +05:30
dependabot[bot]
a63bb139bf chore(deps): bump golang.org/x/net from 0.21.0 to 0.23.0 (#4889)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.21.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.21.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-20 07:52:44 +05:30
Vikrant Gupta
a140bef0e6 fix: handle the case where the functions are recieved as undefined in the query response (#4880) 2024-04-18 11:17:28 +05:30
Vikrant Gupta
48e5436167 fix: handle the edge cases for alerts create form (#4875) 2024-04-17 16:40:21 +05:30
Yunus M
0fc664a387 feat: show timestamp in list view of trace explorer (#4860)
Co-authored-by: Vishal Sharma <makeavish786@gmail.com>
2024-04-16 12:41:13 +05:30
Prashant Shahi
5817d50652 Merge pull request #4853 from SigNoz/release/v0.43.x
Release/v0.43.x
2024-04-15 20:07:57 +05:45
Prashant Shahi
bb318cf52a Merge pull request #4852 from SigNoz/release/v0.43.x
chore(pre-release): 📌 pin versions: SigNoz 0.43.0, OtelCollector 0.88.20
2024-04-15 19:50:20 +05:45
332 changed files with 37526 additions and 4749 deletions

View File

@@ -0,0 +1,31 @@
name: Jest Coverage - changed files
on:
pull_request:
branches: develop
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: "refs/heads/develop"
token: ${{ secrets.GITHUB_TOKEN }} # Provide the GitHub token for authentication
- name: Fetch branch
run: git fetch origin ${{ github.event.pull_request.head.ref }}
- run: |
git checkout ${{ github.event.pull_request.head.sha }}
- uses: actions/setup-node@v4
with:
node-version: lts/*
- name: Install dependencies
run: cd frontend && npm install -g yarn && yarn
- name: npm run test:changedsince
run: cd frontend && npm run i18n:generate-hash && npm run test:changedsince

View File

@@ -9,34 +9,46 @@ jobs:
name: Deploy latest develop branch to staging
runs-on: ubuntu-latest
environment: staging
permissions:
contents: 'read'
id-token: 'write'
steps:
- name: Executing remote ssh commands using ssh key
uses: appleboy/ssh-action@v1.0.3
env:
GITHUB_BRANCH: develop
GITHUB_SHA: ${{ github.sha }}
- id: 'auth'
uses: 'google-github-actions/auth@v2'
with:
host: ${{ secrets.HOST_DNS }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
envs: GITHUB_BRANCH,GITHUB_SHA
command_timeout: 60m
script: |
echo "GITHUB_BRANCH: ${GITHUB_BRANCH}"
echo "GITHUB_SHA: ${GITHUB_SHA}"
export DOCKER_TAG="${GITHUB_SHA:0:7}" # needed for child process to access it
export OTELCOL_TAG="main"
export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work
docker system prune --force
docker pull signoz/signoz-otel-collector:main
docker pull signoz/signoz-schema-migrator:main
cd ~/signoz
git status
git add .
git stash push -m "stashed on $(date --iso-8601=seconds)"
git fetch origin
git checkout ${GITHUB_BRANCH}
git pull
make build-ee-query-service-amd64
make build-frontend-amd64
make run-signoz
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
- name: 'sdk'
uses: 'google-github-actions/setup-gcloud@v2'
- name: 'ssh'
shell: bash
env:
GITHUB_BRANCH: ${{ github.head_ref || github.ref_name }}
GITHUB_SHA: ${{ github.sha }}
GCP_PROJECT: ${{ secrets.GCP_PROJECT }}
GCP_ZONE: ${{ secrets.GCP_ZONE }}
GCP_INSTANCE: ${{ secrets.GCP_INSTANCE }}
run: |
read -r -d '' COMMAND <<EOF || true
echo "GITHUB_BRANCH: ${GITHUB_BRANCH}"
echo "GITHUB_SHA: ${GITHUB_SHA}"
export DOCKER_TAG="${GITHUB_SHA:0:7}" # needed for child process to access it
export OTELCOL_TAG="main"
export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work
docker system prune --force
docker pull signoz/signoz-otel-collector:main
docker pull signoz/signoz-schema-migrator:main
cd ~/signoz
git status
git add .
git stash push -m "stashed on $(date --iso-8601=seconds)"
git fetch origin
git checkout ${GITHUB_BRANCH}
git pull
make build-ee-query-service-amd64
make build-frontend-amd64
make run-signoz
EOF
gcloud compute ssh ${GCP_INSTANCE} --zone ${GCP_ZONE} --ssh-key-expire-after=15m --tunnel-through-iap --project ${GCP_PROJECT} --command "${COMMAND}"

View File

@@ -9,35 +9,47 @@ jobs:
runs-on: ubuntu-latest
environment: testing
if: ${{ github.event.label.name == 'testing-deploy' }}
permissions:
contents: 'read'
id-token: 'write'
steps:
- name: Executing remote ssh commands using ssh key
uses: appleboy/ssh-action@v1.0.3
- id: 'auth'
uses: 'google-github-actions/auth@v2'
with:
workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
- name: 'sdk'
uses: 'google-github-actions/setup-gcloud@v2'
- name: 'ssh'
shell: bash
env:
GITHUB_BRANCH: ${{ github.head_ref || github.ref_name }}
GITHUB_SHA: ${{ github.sha }}
with:
host: ${{ secrets.HOST_DNS }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
envs: GITHUB_BRANCH,GITHUB_SHA
command_timeout: 60m
script: |
echo "GITHUB_BRANCH: ${GITHUB_BRANCH}"
echo "GITHUB_SHA: ${GITHUB_SHA}"
export DOCKER_TAG="${GITHUB_SHA:0:7}" # needed for child process to access it
export DEV_BUILD="1"
export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work
docker system prune --force
cd ~/signoz
git status
git add .
git stash push -m "stashed on $(date --iso-8601=seconds)"
git fetch origin
git checkout develop
git pull
# This is added to include the scenerio when new commit in PR is force-pushed
git branch -D ${GITHUB_BRANCH}
git checkout --track origin/${GITHUB_BRANCH}
make build-ee-query-service-amd64
make build-frontend-amd64
make run-signoz
GCP_PROJECT: ${{ secrets.GCP_PROJECT }}
GCP_ZONE: ${{ secrets.GCP_ZONE }}
GCP_INSTANCE: ${{ secrets.GCP_INSTANCE }}
run: |
read -r -d '' COMMAND <<EOF || true
echo "GITHUB_BRANCH: ${GITHUB_BRANCH}"
echo "GITHUB_SHA: ${GITHUB_SHA}"
export DOCKER_TAG="${GITHUB_SHA:0:7}" # needed for child process to access it
export DEV_BUILD="1"
export PATH="/usr/local/go/bin/:$PATH" # needed for Golang to work
docker system prune --force
cd ~/signoz
git status
git add .
git stash push -m "stashed on $(date --iso-8601=seconds)"
git fetch origin
git checkout develop
git pull
# This is added to include the scenerio when new commit in PR is force-pushed
git branch -D ${GITHUB_BRANCH}
git checkout --track origin/${GITHUB_BRANCH}
make build-ee-query-service-amd64
make build-frontend-amd64
make run-signoz
EOF
gcloud compute ssh ${GCP_INSTANCE} --zone ${GCP_ZONE} --ssh-key-expire-after=15m --tunnel-through-iap --project ${GCP_PROJECT} --command "${COMMAND}"

1
.gitignore vendored
View File

@@ -47,6 +47,7 @@ ee/query-service/signoz.db
ee/query-service/tests/test-deploy/data/
# local data
*.backup
*.db
/deploy/docker/clickhouse-setup/data/
/deploy/docker-swarm/clickhouse-setup/data/

View File

@@ -22,7 +22,7 @@ x-clickhouse-defaults: &clickhouse-defaults
"wget",
"--spider",
"-q",
"localhost:8123/ping"
"0.0.0.0:8123/ping"
]
interval: 30s
timeout: 5s
@@ -146,7 +146,7 @@ services:
condition: on-failure
query-service:
image: signoz/query-service:0.43.0
image: signoz/query-service:0.46.0
command:
[
"-config=/root/config/prometheus.yml",
@@ -186,7 +186,7 @@ services:
<<: *db-depend
frontend:
image: signoz/frontend:0.43.0
image: signoz/frontend:0.46.0
deploy:
restart_policy:
condition: on-failure
@@ -199,7 +199,7 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector:
image: signoz/signoz-otel-collector:0.88.20
image: signoz/signoz-otel-collector:0.88.24
command:
[
"--config=/etc/otel-collector-config.yaml",
@@ -237,7 +237,7 @@ services:
- query-service
otel-collector-migrator:
image: signoz/signoz-schema-migrator:0.88.20
image: signoz/signoz-schema-migrator:0.88.24
deploy:
restart_policy:
condition: on-failure

View File

@@ -111,18 +111,18 @@ processors:
exporters:
clickhousetraces:
datasource: tcp://clickhouse:9000/?database=signoz_traces
datasource: tcp://clickhouse:9000/signoz_traces
docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER}
low_cardinal_exception_grouping: ${LOW_CARDINAL_EXCEPTION_GROUPING}
clickhousemetricswrite:
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
endpoint: tcp://clickhouse:9000/signoz_metrics
resource_to_telemetry_conversion:
enabled: true
clickhousemetricswrite/prometheus:
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
endpoint: tcp://clickhouse:9000/signoz_metrics
# logging: {}
clickhouselogsexporter:
dsn: tcp://clickhouse:9000/
dsn: tcp://clickhouse:9000/signoz_logs
docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER}
timeout: 10s
extensions:

View File

@@ -22,4 +22,4 @@ rule_files:
scrape_configs: []
remote_read:
- url: tcp://clickhouse:9000/?database=signoz_metrics
- url: tcp://clickhouse:9000/signoz_metrics

View File

@@ -46,7 +46,7 @@ services:
"wget",
"--spider",
"-q",
"localhost:8123/ping"
"0.0.0.0:8123/ping"
]
interval: 30s
timeout: 5s
@@ -66,7 +66,7 @@ services:
- --storage.path=/data
otel-collector-migrator:
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.20}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24}
container_name: otel-migrator
command:
- "--dsn=tcp://clickhouse:9000"
@@ -81,7 +81,7 @@ services:
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
otel-collector:
container_name: signoz-otel-collector
image: signoz/signoz-otel-collector:0.88.20
image: signoz/signoz-otel-collector:0.88.24
command:
[
"--config=/etc/otel-collector-config.yaml",

View File

@@ -21,7 +21,7 @@ x-clickhouse-defaults: &clickhouse-defaults
"wget",
"--spider",
"-q",
"localhost:8123/ping"
"0.0.0.0:8123/ping"
]
interval: 30s
timeout: 5s
@@ -164,7 +164,7 @@ services:
# Notes for Maintainers/Contributors who will change Line Numbers of Frontend & Query-Section. Please Update Line Numbers in `./scripts/commentLinesForSetup.sh` & `./CONTRIBUTING.md`
query-service:
image: signoz/query-service:${DOCKER_TAG:-0.43.0}
image: signoz/query-service:${DOCKER_TAG:-0.46.0}
container_name: signoz-query-service
command:
[
@@ -203,7 +203,7 @@ services:
<<: *db-depend
frontend:
image: signoz/frontend:${DOCKER_TAG:-0.43.0}
image: signoz/frontend:${DOCKER_TAG:-0.46.0}
container_name: signoz-frontend
restart: on-failure
depends_on:
@@ -215,7 +215,7 @@ services:
- ../common/nginx-config.conf:/etc/nginx/conf.d/default.conf
otel-collector-migrator:
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.20}
image: signoz/signoz-schema-migrator:${OTELCOL_TAG:-0.88.24}
container_name: otel-migrator
command:
- "--dsn=tcp://clickhouse:9000"
@@ -229,7 +229,7 @@ services:
otel-collector:
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.20}
image: signoz/signoz-otel-collector:${OTELCOL_TAG:-0.88.24}
container_name: signoz-otel-collector
command:
[

View File

@@ -0,0 +1,64 @@
<clickhouse>
<logger>
<!-- Possible levels [1]:
- none (turns off logging)
- fatal
- critical
- error
- warning
- notice
- information
- debug
- trace
[1]: https://github.com/pocoproject/poco/blob/poco-1.9.4-release/Foundation/include/Poco/Logger.h#L105-L114
-->
<level>information</level>
<log>/var/log/clickhouse-keeper/clickhouse-keeper.log</log>
<errorlog>/var/log/clickhouse-keeper/clickhouse-keeper.err.log</errorlog>
<!-- Rotation policy
See https://github.com/pocoproject/poco/blob/poco-1.9.4-release/Foundation/include/Poco/FileChannel.h#L54-L85
-->
<size>1000M</size>
<count>10</count>
<!-- <console>1</console> --> <!-- Default behavior is autodetection (log to console if not daemon mode and is tty) -->
</logger>
<listen_host>0.0.0.0</listen_host>
<max_connections>4096</max_connections>
<keeper_server>
<tcp_port>9181</tcp_port>
<!-- Must be unique among all keeper serves -->
<server_id>1</server_id>
<log_storage_path>/var/lib/clickhouse/coordination/logs</log_storage_path>
<snapshot_storage_path>/var/lib/clickhouse/coordination/snapshots</snapshot_storage_path>
<coordination_settings>
<operation_timeout_ms>10000</operation_timeout_ms>
<min_session_timeout_ms>10000</min_session_timeout_ms>
<session_timeout_ms>100000</session_timeout_ms>
<raft_logs_level>information</raft_logs_level>
<compress_logs>false</compress_logs>
<!-- All settings listed in https://github.com/ClickHouse/ClickHouse/blob/master/src/Coordination/CoordinationSettings.h -->
</coordination_settings>
<!-- enable sanity hostname checks for cluster configuration (e.g. if localhost is used with remote endpoints) -->
<hostname_checks_enabled>true</hostname_checks_enabled>
<raft_configuration>
<server>
<id>1</id>
<!-- Internal port and hostname -->
<hostname>clickhouses-keeper-1</hostname>
<port>9234</port>
</server>
<!-- Add more servers here -->
</raft_configuration>
</keeper_server>
</clickhouse>

View File

@@ -122,21 +122,20 @@ extensions:
exporters:
clickhousetraces:
datasource: tcp://clickhouse:9000/?database=signoz_traces
datasource: tcp://clickhouse:9000/signoz_traces
docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER}
low_cardinal_exception_grouping: ${LOW_CARDINAL_EXCEPTION_GROUPING}
clickhousemetricswrite:
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
endpoint: tcp://clickhouse:9000/signoz_metrics
resource_to_telemetry_conversion:
enabled: true
clickhousemetricswrite/prometheus:
endpoint: tcp://clickhouse:9000/?database=signoz_metrics
# logging: {}
endpoint: tcp://clickhouse:9000/signoz_metrics
clickhouselogsexporter:
dsn: tcp://clickhouse:9000/
dsn: tcp://clickhouse:9000/signoz_logs
docker_multi_node_cluster: ${DOCKER_MULTI_NODE_CLUSTER}
timeout: 10s
# logging: {}
service:
telemetry:

View File

@@ -22,4 +22,4 @@ rule_files:
scrape_configs: []
remote_read:
- url: tcp://clickhouse:9000/?database=signoz_metrics
- url: tcp://clickhouse:9000/signoz_metrics

View File

@@ -1,12 +1,13 @@
package api
import (
"net/http"
"github.com/gorilla/mux"
"go.signoz.io/signoz/pkg/query-service/app/dashboards"
"go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/query-service/common"
"go.signoz.io/signoz/pkg/query-service/model"
"net/http"
)
func (ah *APIHandler) lockDashboard(w http.ResponseWriter, r *http.Request) {

View File

@@ -2,10 +2,8 @@ package api
import (
"net/http"
"strconv"
"go.signoz.io/signoz/ee/query-service/app/db"
"go.signoz.io/signoz/ee/query-service/constants"
"go.signoz.io/signoz/ee/query-service/model"
baseapp "go.signoz.io/signoz/pkg/query-service/app"
basemodel "go.signoz.io/signoz/pkg/query-service/model"
@@ -19,17 +17,13 @@ func (ah *APIHandler) searchTraces(w http.ResponseWriter, r *http.Request) {
ah.APIHandler.SearchTraces(w, r)
return
}
traceId, spanId, levelUpInt, levelDownInt, err := baseapp.ParseSearchTracesParams(r)
searchTracesParams, err := baseapp.ParseSearchTracesParams(r)
if err != nil {
RespondError(w, &model.ApiError{Typ: model.ErrorBadData, Err: err}, "Error reading params")
return
}
spanLimit, err := strconv.Atoi(constants.SpanLimitStr)
if err != nil {
zap.L().Error("Error during strconv.Atoi() on SPAN_LIMIT env variable", zap.Error(err))
return
}
result, err := ah.opts.DataConnector.SearchTraces(r.Context(), traceId, spanId, levelUpInt, levelDownInt, spanLimit, db.SmartTraceAlgorithm)
result, err := ah.opts.DataConnector.SearchTraces(r.Context(), searchTracesParams, db.SmartTraceAlgorithm)
if ah.HandleError(w, err, http.StatusBadRequest) {
return
}

View File

@@ -13,6 +13,11 @@ import (
func SmartTraceAlgorithm(payload []basemodel.SearchSpanResponseItem, targetSpanId string, levelUp int, levelDown int, spanLimit int) ([]basemodel.SearchSpansResult, error) {
var spans []*model.SpanForTraceDetails
// if targetSpanId is null or not present then randomly select a span as targetSpanId
if (targetSpanId == "" || targetSpanId == "null") && len(payload) > 0 {
targetSpanId = payload[0].SpanID
}
// Build a slice of spans from the payload
for _, spanItem := range payload {
var parentID string
@@ -115,6 +120,7 @@ func SmartTraceAlgorithm(payload []basemodel.SearchSpanResponseItem, targetSpanI
searchSpansResult := []basemodel.SearchSpansResult{{
Columns: []string{"__time", "SpanId", "TraceId", "ServiceName", "Name", "Kind", "DurationNano", "TagsKeys", "TagsValues", "References", "Events", "HasError"},
Events: make([][]interface{}, len(resultSpansSet)),
IsSubTree: true,
},
}

View File

@@ -710,6 +710,7 @@ func makeRulesManager(
Logger: nil,
DisableRules: disableRules,
FeatureFlags: fm,
Reader: ch,
}
// create Manager

View File

@@ -11,7 +11,8 @@ const (
var LicenseSignozIo = "https://license.signoz.io/api/v1"
var LicenseAPIKey = GetOrDefaultEnv("SIGNOZ_LICENSE_API_KEY", "")
var SaasSegmentKey = GetOrDefaultEnv("SIGNOZ_SAAS_SEGMENT_KEY", "")
var SpanLimitStr = GetOrDefaultEnv("SPAN_LIMIT", "5000")
var SpanRenderLimitStr = GetOrDefaultEnv("SPAN_RENDER_LIMIT", "2500")
var MaxSpansInTraceStr = GetOrDefaultEnv("MAX_SPANS_IN_TRACE", "250000")
func GetOrDefaultEnv(key string, fallback string) string {
v := os.Getenv(key)

View File

@@ -14,7 +14,9 @@ import (
semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
"go.signoz.io/signoz/ee/query-service/app"
"go.signoz.io/signoz/pkg/query-service/auth"
"go.signoz.io/signoz/pkg/query-service/constants"
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
"go.signoz.io/signoz/pkg/query-service/migrate"
"go.signoz.io/signoz/pkg/query-service/version"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
@@ -143,6 +145,12 @@ func main() {
zap.L().Info("JWT secret key set successfully.")
}
if err := migrate.Migrate(constants.RELATIONAL_DATASOURCE_PATH); err != nil {
zap.L().Error("Failed to migrate", zap.Error(err))
} else {
zap.L().Info("Migration successful")
}
server, err := app.NewServer(serverOptions)
if err != nil {
zap.L().Fatal("Failed to create server", zap.Error(err))

View File

@@ -52,14 +52,14 @@ var BasicPlan = basemodel.FeatureSet{
Name: basemodel.QueryBuilderPanels,
Active: true,
Usage: 0,
UsageLimit: 20,
UsageLimit: -1,
Route: "",
},
basemodel.Feature{
Name: basemodel.QueryBuilderAlerts,
Active: true,
Usage: 0,
UsageLimit: 10,
UsageLimit: -1,
Route: "",
},
basemodel.Feature{

View File

@@ -1,4 +1,4 @@
FROM nginx:1.25.2-alpine
FROM nginx:1.26-alpine
# Add Maintainer Info
LABEL maintainer="signoz"

View File

@@ -4,6 +4,7 @@ const config: Config.InitialOptions = {
clearMocks: true,
coverageDirectory: 'coverage',
coverageReporters: ['text', 'cobertura', 'html', 'json-summary'],
collectCoverageFrom: ['src/**/*.{ts,tsx}'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'json'],
modulePathIgnorePatterns: ['dist'],
moduleNameMapper: {
@@ -20,8 +21,6 @@ const config: Config.InitialOptions = {
transform: {
'^.+\\.(ts|tsx)?$': 'ts-jest',
'^.+\\.(js|jsx)$': 'babel-jest',
'^.+\\.(css|scss|sass|less)$': 'jest-preview/transforms/css',
'^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': 'jest-preview/transforms/file',
},
transformIgnorePatterns: [
'node_modules/(?!(lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@signozhq/design-tokens|d3-interpolate|d3-color)/)',
@@ -35,6 +34,14 @@ const config: Config.InitialOptions = {
browsers: ['chromium', 'firefox', 'webkit'],
},
},
coverageThreshold: {
global: {
statements: 80,
branches: 65,
functions: 80,
lines: 80,
},
},
};
export default config;

View File

@@ -13,15 +13,15 @@
"jest": "jest",
"jest:coverage": "jest --coverage",
"jest:watch": "jest --watch",
"jest-preview": "jest-preview",
"test:debug": "npm-run-all -p test jest-preview",
"postinstall": "is-ci || yarn husky:configure",
"playwright": "npm run i18n:generate-hash && NODE_ENV=testing playwright test --config=./playwright.config.ts",
"playwright:local:debug": "PWDEBUG=console yarn playwright --headed --browser=chromium",
"playwright:codegen:local": "playwright codegen http://localhost:3301",
"playwright:codegen:local:auth": "yarn playwright:codegen:local --load-storage=tests/auth.json",
"husky:configure": "cd .. && husky install frontend/.husky && cd frontend && chmod ug+x .husky/*",
"commitlint": "commitlint --edit $1"
"commitlint": "commitlint --edit $1",
"test": "jest --coverage",
"test:changedsince": "jest --changedSince=develop --coverage --silent"
},
"engines": {
"node": ">=16.15.0"
@@ -51,7 +51,7 @@
"ansi-to-html": "0.7.2",
"antd": "5.11.0",
"antd-table-saveas-excel": "2.2.1",
"axios": "1.6.2",
"axios": "1.6.4",
"babel-eslint": "^10.1.0",
"babel-jest": "^29.6.4",
"babel-loader": "9.1.3",
@@ -85,7 +85,7 @@
"less": "^4.1.2",
"less-loader": "^10.2.0",
"lodash-es": "^4.17.21",
"lucide-react": "0.321.0",
"lucide-react": "0.379.0",
"mini-css-extract-plugin": "2.4.5",
"papaparse": "5.4.1",
"react": "18.2.0",
@@ -181,7 +181,7 @@
"@types/webpack-dev-server": "^4.7.2",
"@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0",
"autoprefixer": "^9.0.0",
"autoprefixer": "10.4.19",
"babel-plugin-styled-components": "^1.12.0",
"compression-webpack-plugin": "9.0.0",
"copy-webpack-plugin": "^8.1.0",
@@ -204,12 +204,12 @@
"husky": "^7.0.4",
"is-ci": "^3.0.1",
"jest-playwright-preset": "^1.7.2",
"jest-preview": "0.3.1",
"jest-styled-components": "^7.0.8",
"lint-staged": "^12.5.0",
"msw": "1.3.2",
"npm-run-all": "latest",
"portfinder-sync": "^0.0.2",
"postcss": "8.4.38",
"prettier": "2.2.1",
"raw-loader": "4.0.2",
"react-hooks-testing-library": "0.6.0",

View File

@@ -0,0 +1 @@
<svg width="32" height="32" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M13.72 12.839l-9.054.92s.05.649.236.798c.178.142 5.617.066 11.048.088 5.433.023 10.82.125 10.944.072.249-.107.249-.992.249-.992l-13.424-.886zM16.55 7.787l-12.623-.32s.275.61.637.813c.523.29 3.71.889 11.518.918 7.808.028 10.635-.4 11.317-.678.58-.238 1.215-1.576 1.215-1.576l-12.064.843z" fill="#8A1E0C"/><path d="M21.95 8.658v1.335l2.176-.087V8.542l-2.176.116z" fill="#8A1E0C"/><path d="M21.948 9.566h2.177v16.797l-2.206.294.029-17.09z" fill="#EB2901"/><path d="M21.355 26.19c-.111.193-.111 2.297-.007 2.444.105.147 3.242.104 3.326 0 .085-.104.063-2.38 0-2.464-.062-.085-3.235-.125-3.32.02z" fill="#474C4F"/><path d="M8.462 9.85V8.488l2.042.125v1.22l-2.042.017z" fill="#8A1E0C"/><path d="M8.462 9.55l-.038 17.051 2.08-.207V9.566l-2.042-.015z" fill="#EB2901"/><path d="M7.804 25.919c-.073.073-.147 2.36-.02 2.464.125.104 3.14.129 3.244.024.105-.104.085-2.304.023-2.43-.063-.127-3.142-.163-3.247-.058z" fill="#474C4F"/><path d="M14.788 8.107v4.876l2.393-.33V8.108h-2.393z" fill="#EB2901"/><path d="M27.067 11.978c-.115-.16-.482-.138-.482-.138l-1.137-.013c.002-.398-.01-.913-.078-.996-.116-.137-4.542-.09-4.702.047-.091.078-.11.527-.107.898-2.738-.027-5.99-.058-8.83-.076 0-.384-.012-.849-.078-.915-.116-.116-4.22-.185-4.38-.07-.113.083-.136.647-.138.97-1.384.002-2.275.013-2.34.04-.322.137-.137 2.042-.137 2.042l22.476.16c.002.002.049-1.787-.067-1.95z" fill="#EB2901"/><path d="M3.93 6.942s-.646-.34-1.377-1.573c-.509-.858-.595-1.658-.387-1.778.21-.12 2.154 1.08 5.745 1.616a60.81 60.81 0 008.173.644c2.884.027 5.717-.135 8.397-.644 3.62-.689 4.906-1.436 5.264-1.316.36.12-.109 1.227-.369 1.78-.178.376-.944 1.77-1.515 1.87-.411.072-19.953-.09-19.953-.09l-3.977-.509z" fill="#474C4F"/><path d="M3.31 5.724c-.108.137-.057.457.212 1.06.107.237.415.782.529.917 0 0 2.982.756 11.977.7 8.995-.055 12.108-.62 12.108-.62s.911-1.277.745-1.32c-.096-.024-4.847.98-12.909.898C7.911 7.277 3.311 5.724 3.311 5.724z" fill="#EB2901"/></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@@ -1 +0,0 @@
<svg width="24" height="24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M12 2c1 2.538 2.5 2.962 3.5 3.808.942.78 1.481 1.845 1.5 2.961 0 1.122-.527 2.198-1.464 2.992C14.598 12.554 13.326 13 12 13s-2.598-.446-3.536-1.24C7.527 10.968 7 9.892 7 8.77c0-.255 0-.508.1-.762.085.25.236.48.443.673.207.193.463.342.75.437a2.334 2.334 0 001.767-.128c.263-.135.485-.32.65-.539.166-.22.269-.468.301-.727a1.452 1.452 0 00-.11-.765 1.699 1.699 0 00-.501-.644C8 4.115 11 2 12 2zM17 16l-5 6-5-6h10z" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>

Before

Width:  |  Height:  |  Size: 581 B

View File

@@ -0,0 +1 @@
<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1.305 13.063c.74.739 1.637.482 2.156-.109.53-.604.813-.956.813-.956.66-.973 3.392-4.227 5.724-6.568a2.638 2.638 0 002.74-.434 2.648 2.648 0 00.922-2.041.155.155 0 00-.23-.132l-1.607.927a1.64 1.64 0 01-1.076-1.864l1.6-.923a.153.153 0 00.077-.134.153.153 0 00-.077-.133 2.65 2.65 0 00-3.66 3.563C6.11 6.826 2.966 9.604 2.15 10.223c0 0-.492.356-.962.84-.464.476-.636 1.245.117 1.999zm.542-1.137a.592.592 0 111.184 0 .592.592 0 01-1.184 0z" fill="#82AEC0"/><path d="M8.334 4.61l.353-.35a2.63 2.63 0 01-.212-2.039c.073-.12.189-.249.262-.171-.03.946.245 1.931.902 2.611.327.338.752.582 1.207.696.224.057.458.082.69.069.137-.008.519-.149.596-.044v.004a2.656 2.656 0 01-2.135.043 38.176 38.176 0 00-1.903 2.05c.262-.495 1.034-1.408 1.241-1.757a.412.412 0 00-.036-.464c-.207-.255-.633-.493-.965-.649z" fill="#2F7889"/><path d="M5.186 8.529c.06-.062.004-.167-.08-.148-.158.035-.386.125-.657.345-.531.43-1.934 1.595-2.107 1.825-.173.23.522-.003.767-.047.2-.036 1.602-1.48 2.077-1.975zM10.048 1.104c-.296.212-.563.465-.84.701-.072.061-.177.122-.25.065-.08-.064-.03-.191.03-.274C9.512.874 10.493.358 11.442.563c-.5.161-.95.223-1.395.541z" fill="#B9E4EA"/><path d="M12.408 3.583a2.1 2.1 0 01-.371.19c-.112.031-.43-.092-.522-.166l1.183-.772c.043-.028.087-.056.137-.072a.546.546 0 01.185-.014c.087.004.51-.01.56.064.05.075-.126.149-.183.183-.33.197-.66.391-.99.587zM7.867 7.687L6.624 6.254c-.45.423-.895.835-1.321 1.225l.362-.078a.482.482 0 01.439.13l.58.65c.122.122.142.334.096.5l-.065.308c.367-.423.755-.862 1.152-1.302z" fill="#2F7889"/><g><path d="M13.378 12.86l-.744.643a.686.686 0 01-.968-.072L2.84 2.779l1.135-.853 9.459 9.976a.668.668 0 01-.057.957z" fill="#A06841"/><path d="M3.648 3.752l2.1 2.535c.328-.493.494-1.084.629-1.83l-2.028-2.14a1.838 1.838 0 00-.414.48 2.17 2.17 0 00-.287.955z" fill="#7D5133"/><path d="M7.81.438C5.885.416 5.17.588 4.098 1.515l-.966.835c-.35.302-.815.566-.742 1.089.027.19.086.384.05.573-.034.179-.242.268-.39.166-.139-.096-.292-.214-.463-.234a.588.588 0 00-.45.14l-.747.664s-.107.434.729 1.38c.835.946 1.373.878 1.373.878l.702-.618a.53.53 0 00.176-.412c-.003-.184-.11-.326-.174-.49-.013-.031-.083-.143.04-.244.109-.094.333-.062.46-.027.129.034.25.088.38.122.25.065.369-.051.543-.201L6.013 3.93c.619-.536-.325-1.474-.325-1.474C5.244 1.953 7.941.687 7.941.687c.198-.069.138-.246-.13-.249z" fill="#82AEC0"/><path d="M4.076 5.338a.504.504 0 00.14.016v-.02c-.011-.12-.077-.23-.144-.33A7.18 7.18 0 002.545 3.33a1.683 1.683 0 00-.154-.111.726.726 0 00-.002.22c.027.19.086.384.05.573-.038.196-.242.25-.399.177a3.27 3.27 0 011.011 1.027c.035.056.07.115.11.168a.2.2 0 01.075-.14c.109-.095.333-.063.46-.029.13.034.25.088.38.123zM1.778 5.573c.585.613.914 1.247.734 1.42-.179.17-.799-.186-1.384-.797C.542 5.584.21 4.92.388 4.748c.18-.171.804.213 1.39.825z" fill="#2F7889"/><path d="M4.057 2.41c.465-.198.88-.623 1.422-1.09A2.53 2.53 0 016.03.964c.076-.035.048-.149-.036-.148-.278.005-.527.09-.772.196-.342.149-.644.374-.935.608-.2.16-.67.555-.965.805-.055.047-.012.12.06.12.208.002.325.014.674-.135zM1.124 4.352c-.196.221.055.281.496.646.311.257.642.018.645-.223.003-.216-.052-.333-.366-.53-.315-.199-.597-.093-.775.107z" fill="#B9E4EA"/></g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 204 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

@@ -1,6 +1,6 @@
{
"create_dashboard": "Create Dashboard",
"import_json": "Import JSON",
"import_json": "Import Dashboard JSON",
"import_grafana_json": "Import Grafana JSON",
"copy_to_clipboard": "Copy To ClipBoard",
"download_json": "Download JSON",
@@ -9,13 +9,14 @@
"upload_json_file": "Upload JSON file",
"paste_json_below": "Paste JSON below",
"error_upload_json": "Invalid JSON",
"load_json": "Load JSON",
"import_and_next": "Import and Next",
"import_dashboard_by_pasting": "Import dashboard by pasting JSON or importing JSON file",
"error_loading_json": "Error loading JSON file",
"empty_json_not_allowed": "Empty JSON is not allowed",
"new_dashboard_title": "Sample Title",
"layout_saved_successfully": "Layout saved successfully",
"add_panel": "Add Panel",
"add_row": "Add Row",
"save_layout": "Save Layout",
"variable_updated_successfully": "Variable updated successfully",
"error_while_updating_variable": "Error while updating variable",

View File

@@ -1,6 +1,6 @@
{
"create_dashboard": "Create Dashboard",
"import_json": "Import JSON",
"import_json": "Import Dashboard JSON",
"import_grafana_json": "Import Grafana JSON",
"copy_to_clipboard": "Copy To ClipBoard",
"download_json": "Download JSON",
@@ -9,13 +9,14 @@
"upload_json_file": "Upload JSON file",
"paste_json_below": "Paste JSON below",
"error_upload_json": "Invalid JSON",
"load_json": "Load JSON",
"import_and_next": "Import and Next",
"import_dashboard_by_pasting": "Import dashboard by pasting JSON or importing JSON file",
"error_loading_json": "Error loading JSON file",
"empty_json_not_allowed": "Empty JSON is not allowed",
"new_dashboard_title": "Sample Title",
"layout_saved_successfully": "Layout saved successfully",
"add_panel": "Add Panel",
"add_row": "Add Row",
"save_layout": "Save Layout",
"full_view": "Full Screen View",
"variable_updated_successfully": "Variable updated successfully",

View File

@@ -9,13 +9,14 @@ import ROUTES from 'constants/routes';
import AppLayout from 'container/AppLayout';
import useAnalytics from 'hooks/analytics/useAnalytics';
import { KeyboardHotkeysProvider } from 'hooks/hotkeys/useKeyboardHotkeys';
import { useThemeConfig } from 'hooks/useDarkMode';
import { useIsDarkMode, useThemeConfig } from 'hooks/useDarkMode';
import { THEME_MODE } from 'hooks/useDarkMode/constant';
import useGetFeatureFlag from 'hooks/useGetFeatureFlag';
import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense';
import { NotificationProvider } from 'hooks/useNotifications';
import { ResourceProvider } from 'hooks/useResourceAttribute';
import history from 'lib/history';
import { identity, pickBy } from 'lodash-es';
import { identity, pick, pickBy } from 'lodash-es';
import { DashboardProvider } from 'providers/Dashboard/Dashboard';
import { QueryBuilderProvider } from 'providers/QueryBuilder';
import { Suspense, useEffect, useState } from 'react';
@@ -46,12 +47,14 @@ function App(): JSX.Element {
const dispatch = useDispatch<Dispatch<AppActions>>();
const { trackPageView } = useAnalytics();
const { trackPageView, trackEvent } = useAnalytics();
const { hostname, pathname } = window.location;
const isCloudUserVal = isCloudUser();
const isDarkMode = useIsDarkMode();
const featureResponse = useGetFeatureFlag((allFlags) => {
const isOnboardingEnabled =
allFlags.find((flag) => flag.name === FeatureKeys.ONBOARDING)?.active ||
@@ -174,6 +177,25 @@ function App(): JSX.Element {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pathname]);
useEffect(() => {
try {
const isThemeAnalyticsSent = getLocalStorageApi(
LOCALSTORAGE.THEME_ANALYTICS,
);
if (!isThemeAnalyticsSent) {
trackEvent('Theme Analytics', {
theme: isDarkMode ? THEME_MODE.DARK : THEME_MODE.LIGHT,
user: pick(user, ['email', 'userId', 'name']),
org,
});
setLocalStorageApi(LOCALSTORAGE.THEME_ANALYTICS, 'true');
}
} catch {
console.error('Failed to parse local storage theme analytics event');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<ConfigProvider theme={themeConfig}>
<Router history={history}>

View File

@@ -7,7 +7,7 @@ export const ServicesTablePage = Loadable(
export const ServiceMetricsPage = Loadable(
() =>
import(
/* webpackChunkName: "ServiceMetricsPage" */ 'pages/MetricsApplication'
/* webpackChunkName: "ServiceMetricsPage" */ 'pages/MetricsApplication/MetricsApplication'
),
);

View File

@@ -1,26 +1,20 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps, Props } from 'types/api/dashboard/update';
const updateDashboard = async (
props: Props,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.put(`/dashboards/${props.uuid}`, {
...props.data,
});
const response = await axios.put(`/dashboards/${props.uuid}`, {
...props.data,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
};
export default updateDashboard;

View File

@@ -0,0 +1,44 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { Recurrence } from './getAllDowntimeSchedules';
export interface DowntimeSchedulePayload {
name: string;
description?: string;
alertIds: string[];
schedule: {
timezone?: string;
startTime?: string;
endTime?: string;
recurrence?: Recurrence;
};
}
export interface PayloadProps {
status: string;
data: string;
}
const createDowntimeSchedule = async (
props: DowntimeSchedulePayload,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.post('/downtime_schedules', {
...props,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default createDowntimeSchedule;

View File

@@ -0,0 +1,19 @@
import axios from 'api';
import { useMutation, UseMutationResult } from 'react-query';
export interface DeleteDowntimeScheduleProps {
id?: number;
}
export interface DeleteSchedulePayloadProps {
status: string;
data: string;
}
export const useDeleteDowntimeSchedule = (
props: DeleteDowntimeScheduleProps,
): UseMutationResult<DeleteSchedulePayloadProps, Error, number> =>
useMutation({
mutationKey: [props.id],
mutationFn: () => axios.delete(`/downtime_schedules/${props.id}`),
});

View File

@@ -0,0 +1,50 @@
import axios from 'api';
import { AxiosError, AxiosResponse } from 'axios';
import { Option } from 'container/PlannedDowntime/DropdownWithSubMenu/DropdownWithSubMenu';
import { useQuery, UseQueryResult } from 'react-query';
export type Recurrence = {
startTime?: string | null;
endTime?: string | null;
duration?: number | string | null;
repeatType?: string | Option | null;
repeatOn?: string[] | null;
};
type Schedule = {
timezone: string | null;
startTime: string | null;
endTime: string | null;
recurrence: Recurrence | null;
};
export interface DowntimeSchedules {
id: number;
name: string | null;
description: string | null;
schedule: Schedule | null;
alertIds: string[] | null;
createdAt: string | null;
createdBy: string | null;
updatedAt: string | null;
updatedBy: string | null;
}
export type PayloadProps = { data: DowntimeSchedules[] };
export const getAllDowntimeSchedules = async (
props?: GetAllDowntimeSchedulesPayloadProps,
): Promise<AxiosResponse<PayloadProps>> =>
axios.get('/downtime_schedules', { params: props });
export interface GetAllDowntimeSchedulesPayloadProps {
active?: boolean;
recurrence?: boolean;
}
export const useGetAllDowntimeSchedules = (
props?: GetAllDowntimeSchedulesPayloadProps,
): UseQueryResult<AxiosResponse<PayloadProps>, AxiosError> =>
useQuery<AxiosResponse<PayloadProps>, AxiosError>({
queryKey: ['getAllDowntimeSchedules', props],
queryFn: () => getAllDowntimeSchedules(props),
});

View File

@@ -0,0 +1,37 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { DowntimeSchedulePayload } from './createDowntimeSchedule';
export interface DowntimeScheduleUpdatePayload {
data: DowntimeSchedulePayload;
id?: number;
}
export interface PayloadProps {
status: string;
data: string;
}
const updateDowntimeSchedule = async (
props: DowntimeScheduleUpdatePayload,
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.put(`/downtime_schedules/${props.id}`, {
...props.data,
});
return {
statusCode: 200,
error: null,
message: response.data.status,
payload: response.data.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default updateDowntimeSchedule;

View File

@@ -0,0 +1,176 @@
export default function ApacheIcon(): JSX.Element {
return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M8.7112 1.05739C8.3796 1.30831 8.08524 1.60498 7.83691 1.93853L8.17977 2.58567C8.40218 2.26279 8.64677 1.95576 8.91177 1.66681C8.93063 1.64581 8.94091 1.63596 8.94091 1.63596L8.91177 1.66681C8.66003 1.95965 8.43081 2.27111 8.22606 2.59853C8.67305 2.56672 9.11808 2.51165 9.55934 2.43353C9.60936 2.25525 9.62374 2.06886 9.60168 1.88502C9.57962 1.70118 9.52154 1.52349 9.43077 1.3621C9.43077 1.3621 9.09991 0.828957 8.7112 1.05739Z"
fill="url(#paint0_linear_2061_4195)"
/>
<path
d="M7.55932 6.49365L7.42432 6.51722L7.49332 6.50651C7.51432 6.50265 7.53703 6.49837 7.55932 6.49365Z"
fill="#BE202E"
/>
<path
opacity="0.35"
d="M7.55932 6.49365L7.42432 6.51722L7.49332 6.50651C7.51432 6.50265 7.53703 6.49837 7.55932 6.49365Z"
fill="#BE202E"
/>
<path
d="M7.67407 5.92858L7.6955 5.92558C7.72464 5.9213 7.75336 5.91616 7.78122 5.91016L7.67493 5.92816L7.67407 5.92858Z"
fill="#BE202E"
/>
<path
opacity="0.35"
d="M7.67407 5.92858L7.6955 5.92558C7.72464 5.9213 7.75336 5.91616 7.78122 5.91016L7.67493 5.92816L7.67407 5.92858Z"
fill="#BE202E"
/>
<path
d="M7.16872 4.25736C7.273 4.0625 7.37858 3.87221 7.48543 3.6865C7.59629 3.49393 7.70829 3.3075 7.82143 3.12721L7.84115 3.09507C7.95315 2.91793 8.066 2.74779 8.17972 2.58464L7.83686 1.9375L7.75886 2.03393C7.65986 2.15736 7.55743 2.29107 7.452 2.43036C7.33329 2.58893 7.21115 2.75779 7.08729 2.93564C6.97286 3.09979 6.85672 3.27164 6.74058 3.44993C6.64158 3.60121 6.54258 3.75721 6.444 3.91707L6.43286 3.93507L6.879 4.8145C6.97443 4.62707 7.071 4.44136 7.16872 4.25736Z"
fill="url(#paint1_linear_2061_4195)"
/>
<path
d="M5.13606 9.22519C5.07692 9.38748 5.01763 9.55305 4.95821 9.72191L4.95563 9.72919L4.93035 9.80076C4.89049 9.91476 4.85535 10.015 4.77563 10.2503C4.92444 10.3467 5.0416 10.4847 5.11249 10.6472C5.10283 10.4581 5.01907 10.2804 4.87935 10.1526C5.16043 10.1981 5.44862 10.1654 5.71236 10.0581C5.97609 9.95074 6.20521 9.77291 6.37463 9.54405C6.40102 9.50088 6.42464 9.45607 6.44535 9.40991C6.37459 9.49741 6.28141 9.56408 6.17575 9.6028C6.07008 9.64152 5.95589 9.65084 5.84535 9.62976C6.20937 9.49539 6.51802 9.24316 6.72221 8.91319C6.76978 8.83691 6.81563 8.75376 6.86278 8.66162C6.6974 8.84328 6.48756 8.97872 6.25393 9.05463C6.02029 9.13053 5.77091 9.14426 5.53035 9.09448L5.16949 9.13391C5.15706 9.16434 5.14721 9.19476 5.13606 9.22519Z"
fill="url(#paint2_linear_2061_4195)"
/>
<path
d="M5.30448 8.417C5.38248 8.21528 5.46276 8.01157 5.54533 7.80586C5.62419 7.60871 5.70519 7.41057 5.78833 7.21143C5.87148 7.01228 5.95633 6.81228 6.0429 6.61143C6.1309 6.40828 6.22076 6.20557 6.31248 6.00328C6.40419 5.801 6.49633 5.60257 6.5889 5.408C6.62262 5.33714 6.65662 5.26643 6.6909 5.19586C6.75005 5.07414 6.80962 4.95343 6.86962 4.83371C6.87262 4.82728 6.87605 4.82086 6.87948 4.81443L6.4329 3.93457L6.41105 3.97014C6.30733 4.14157 6.20362 4.313 6.10205 4.49128C6.00048 4.66957 5.89848 4.853 5.80205 5.03814C5.7189 5.19443 5.63776 5.35157 5.55862 5.50957L5.51148 5.606C5.41419 5.80614 5.32633 5.99943 5.24705 6.18543C5.15705 6.396 5.07762 6.59686 5.00876 6.788C4.9629 6.91357 4.92305 7.03486 4.88362 7.151C4.85233 7.25043 4.82276 7.34986 4.79448 7.451C4.72762 7.68471 4.67048 7.91771 4.62305 8.15L5.07133 9.035C5.13076 8.87671 5.19162 8.71614 5.2539 8.55328L5.30448 8.417Z"
fill="url(#paint3_linear_2061_4195)"
/>
<path
d="M4.615 8.18082C4.55868 8.46014 4.51975 8.74268 4.49843 9.02682C4.49843 9.03668 4.49843 9.04653 4.49629 9.05639C4.3547 8.8778 4.1801 8.72808 3.982 8.61539C4.24526 8.95089 4.41819 9.34823 4.48429 9.76953C4.28982 9.78674 4.0942 9.75338 3.91643 9.67268C4.05057 9.80984 4.21714 9.91096 4.40072 9.96668C4.14974 10.0146 3.9168 10.1307 3.72743 10.3022C3.97375 10.178 4.25059 10.1271 4.525 10.1557C4.21858 11.024 3.91129 11.9827 3.604 13.0001C3.64664 12.988 3.6856 12.9655 3.71739 12.9346C3.74918 12.9037 3.7728 12.8654 3.78615 12.8231C3.841 12.6388 4.20443 11.4298 4.77443 9.84068L4.82372 9.70439L4.83743 9.66625C4.89772 9.49968 4.96015 9.32968 5.02472 9.15625L5.06758 9.03753V9.03539L4.62143 8.15039C4.61929 8.16025 4.61672 8.17053 4.615 8.18082Z"
fill="url(#paint4_linear_2061_4195)"
/>
<path
d="M6.94843 4.89059L6.90986 4.96987C6.87129 5.04959 6.83214 5.13102 6.79243 5.21416C6.74957 5.30416 6.70671 5.3963 6.66386 5.49059C6.64157 5.53816 6.621 5.58573 6.59743 5.63416C6.53057 5.7793 6.46286 5.92916 6.39429 6.08373C6.30857 6.27402 6.22286 6.47173 6.13714 6.67687C6.054 6.87259 5.96971 7.07516 5.88429 7.28459C5.80314 7.48459 5.721 7.68973 5.63786 7.90002C5.56386 8.08888 5.48914 8.28273 5.41371 8.48159C5.40986 8.49145 5.40643 8.50087 5.403 8.51073C5.32814 8.70873 5.25257 8.91187 5.17629 9.12016L5.17114 9.1343L5.532 9.09487L5.51057 9.09102C6.039 8.98351 6.52026 8.7126 6.88629 8.31659C7.0686 8.1182 7.22683 7.89896 7.35771 7.66345C7.47147 7.46033 7.57252 7.25036 7.66029 7.03473C7.74386 6.83245 7.824 6.61387 7.90114 6.37645C7.79448 6.43086 7.68084 6.47037 7.56343 6.49388C7.54157 6.49859 7.52057 6.50287 7.49657 6.50673C7.47257 6.51059 7.45071 6.51445 7.42757 6.51745C7.80325 6.36305 8.10458 6.06924 8.26843 5.69759C8.12155 5.79773 7.95733 5.86966 7.78414 5.90973C7.75586 5.91616 7.72757 5.92087 7.69843 5.92516L7.677 5.92816C7.80484 5.87656 7.92565 5.80903 8.03657 5.72716C8.05843 5.71087 8.07943 5.69373 8.1 5.67573C8.13129 5.64873 8.16086 5.62045 8.18914 5.59002C8.20714 5.57116 8.22471 5.55145 8.24186 5.53087C8.28293 5.48179 8.32059 5.42996 8.35457 5.37573C8.36529 5.35859 8.376 5.34145 8.38629 5.32345C8.39957 5.29773 8.41243 5.27245 8.42486 5.24716C8.481 5.13402 8.526 5.03288 8.56157 4.94459C8.57957 4.90173 8.595 4.85888 8.60829 4.82116C8.61386 4.80616 8.619 4.79116 8.62371 4.7783C8.63786 4.73545 8.64943 4.69816 8.65843 4.66473C8.66941 4.62595 8.67828 4.58661 8.685 4.54688C8.67015 4.5586 8.65454 4.56934 8.63829 4.57902C8.4822 4.66169 8.31419 4.71953 8.14029 4.75045H8.13257L8.08157 4.75859L8.09057 4.75473L6.957 4.87902L6.94843 4.89059Z"
fill="url(#paint5_linear_2061_4195)"
/>
<path
d="M8.22475 2.59871C8.12404 2.75343 8.01389 2.92914 7.89561 3.12843L7.87675 3.16014C7.77446 3.33157 7.66604 3.52114 7.55147 3.72886C7.45261 3.90771 7.34989 4.10014 7.24332 4.30614C7.15018 4.48586 7.05418 4.67671 6.95532 4.87871L8.08889 4.75443C8.33914 4.65567 8.55501 4.48583 8.70989 4.26586C8.74804 4.211 8.78618 4.15357 8.82432 4.09443C8.94089 3.91271 9.05489 3.71257 9.15689 3.51371C9.25018 3.33322 9.33429 3.14813 9.40889 2.95914C9.44758 2.86102 9.48092 2.76087 9.50875 2.65914C9.52932 2.58028 9.54561 2.50571 9.55804 2.43457C9.11675 2.51236 8.67172 2.56715 8.22475 2.59871Z"
fill="url(#paint6_linear_2061_4195)"
/>
<path
d="M7.49234 6.50635C7.46963 6.5102 7.44648 6.51406 7.42334 6.51706C7.44648 6.51406 7.47134 6.5102 7.49234 6.50635Z"
fill="#BE202E"
/>
<path
opacity="0.35"
d="M7.49234 6.50635C7.46963 6.5102 7.44648 6.51406 7.42334 6.51706C7.44648 6.51406 7.47134 6.5102 7.49234 6.50635Z"
fill="#BE202E"
/>
<path
d="M7.49234 6.50635C7.46963 6.5102 7.44648 6.51406 7.42334 6.51706C7.44648 6.51406 7.47134 6.5102 7.49234 6.50635Z"
fill="url(#paint7_linear_2061_4195)"
/>
<defs>
<linearGradient
id="paint0_linear_2061_4195"
x1="7.26579"
y1="1.16584"
x2="9.7782"
y2="0.46843"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#F69923" />
<stop offset="0.312" stopColor="#F79A23" />
<stop offset="0.838" stopColor="#E97826" />
</linearGradient>
<linearGradient
id="paint1_linear_2061_4195"
x1="1.76109"
y1="12.4382"
x2="6.87606"
y2="1.48267"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.323" stopColor="#9E2064" />
<stop offset="0.63" stopColor="#C92037" />
<stop offset="0.751" stopColor="#CD2335" />
<stop offset="1" stopColor="#E97826" />
</linearGradient>
<linearGradient
id="paint2_linear_2061_4195"
x1="3.47784"
y1="11.6287"
x2="6.5258"
y2="5.10046"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#282662" />
<stop offset="0.095" stopColor="#662E8D" />
<stop offset="0.788" stopColor="#9F2064" />
<stop offset="0.949" stopColor="#CD2032" />
</linearGradient>
<linearGradient
id="paint3_linear_2061_4195"
x1="1.94596"
y1="11.7748"
x2="7.06094"
y2="0.819304"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.323" stopColor="#9E2064" />
<stop offset="0.63" stopColor="#C92037" />
<stop offset="0.751" stopColor="#CD2335" />
<stop offset="1" stopColor="#E97826" />
</linearGradient>
<linearGradient
id="paint4_linear_2061_4195"
x1="2.46768"
y1="11.0455"
x2="5.15578"
y2="5.28798"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#282662" />
<stop offset="0.095" stopColor="#662E8D" />
<stop offset="0.788" stopColor="#9F2064" />
<stop offset="0.949" stopColor="#CD2032" />
</linearGradient>
<linearGradient
id="paint5_linear_2061_4195"
x1="3.08048"
y1="12.3044"
x2="8.19546"
y2="1.3489"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.323" stopColor="#9E2064" />
<stop offset="0.63" stopColor="#C92037" />
<stop offset="0.751" stopColor="#CD2335" />
<stop offset="1" stopColor="#E97826" />
</linearGradient>
<linearGradient
id="paint6_linear_2061_4195"
x1="2.70679"
y1="12.9581"
x2="7.82195"
y2="2.00223"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.323" stopColor="#9E2064" />
<stop offset="0.63" stopColor="#C92037" />
<stop offset="0.751" stopColor="#CD2335" />
<stop offset="1" stopColor="#E97826" />
</linearGradient>
<linearGradient
id="paint7_linear_2061_4195"
x1="3.41759"
y1="12.4619"
x2="8.53277"
y2="1.50629"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.323" stopColor="#9E2064" />
<stop offset="0.63" stopColor="#C92037" />
<stop offset="0.751" stopColor="#CD2335" />
<stop offset="1" stopColor="#E97826" />
</linearGradient>
</defs>
</svg>
);
}

View File

@@ -0,0 +1,28 @@
export default function DockerIcon(): JSX.Element {
return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_2061_4220)">
<path
d="M13.574 5.82107C13.2652 5.60679 12.5575 5.52644 12.0042 5.63358C11.9398 5.09788 11.6439 4.62915 11.1292 4.21399L10.8332 3.99971L10.6274 4.30774C10.37 4.70951 10.2413 5.27198 10.2799 5.80768C10.2928 5.99517 10.3571 6.32998 10.5502 6.62461C10.37 6.73175 9.99686 6.86567 9.50789 6.86567H0.204685L0.17895 6.97281C0.0888774 7.5085 0.0888774 9.18254 1.14401 10.4682C1.9418 11.4458 3.12561 11.9414 4.68258 11.9414C8.05386 11.9414 10.5502 10.3209 11.7211 7.38797C12.1843 7.40136 13.1751 7.38797 13.677 6.38354C13.6898 6.35676 13.7156 6.30319 13.8056 6.10231L13.8571 5.99517L13.574 5.82107ZM7.6421 2.04443H6.22668V3.38367H7.6421V2.04443ZM7.6421 3.65151H6.22668V4.99074H7.6421V3.65151ZM5.96933 3.65151H4.5539V4.99074H5.96933V3.65151ZM4.29655 3.65151H2.88113V4.99074H4.29655V3.65151ZM2.62378 5.25859H1.20835V6.59782H2.62378V5.25859ZM4.29655 5.25859H2.88113V6.59782H4.29655V5.25859ZM5.96933 5.25859H4.5539V6.59782H5.96933V5.25859ZM7.6421 5.25859H6.22668V6.59782H7.6421V5.25859ZM9.31488 5.25859H7.89945V6.59782H9.31488V5.25859Z"
fill="#2396ED"
/>
</g>
<defs>
<clipPath id="clip0_2061_4220">
<rect
width="13.7143"
height="13.7143"
fill="white"
transform="translate(0.142822 0.143066)"
/>
</clipPath>
</defs>
</svg>
);
}

View File

@@ -0,0 +1,36 @@
export default function ElasticSearchIcon(): JSX.Element {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.38041 6.94508L9.70241 8.45858L13.0524 5.52158C13.1019 5.27892 13.1255 5.03171 13.1229 4.78408C13.1236 3.9846 12.8681 3.20592 12.3941 2.56216C11.92 1.9184 11.2522 1.44339 10.4885 1.20675C9.72487 0.970101 8.9055 0.984259 8.15047 1.24714C7.39544 1.51003 6.74446 2.00782 6.29291 2.66758L5.73291 5.56008L6.38041 6.94508Z"
fill="#FED10A"
/>
<path
d="M2.943 10.4593C2.89356 10.7062 2.86994 10.9575 2.8725 11.2093C2.87361 12.012 3.13177 12.7932 3.60915 13.4385C4.08654 14.0838 4.75804 14.5592 5.52528 14.7952C6.29252 15.0311 7.11514 15.0151 7.87262 14.7495C8.63009 14.4839 9.28259 13.9827 9.7345 13.3193L10.2845 10.4398L9.55 9.02928L6.215 7.50928L2.943 10.4593Z"
fill="#24BBB1"
/>
<path
d="M2.92394 4.71302L5.19994 5.25002L5.69994 2.66552C5.39077 2.43015 5.01365 2.30134 4.62509 2.29839C4.23654 2.29544 3.8575 2.41852 3.54479 2.64916C3.23208 2.8798 3.00256 3.20559 2.89063 3.57768C2.77869 3.94978 2.79039 4.34813 2.92394 4.71302Z"
fill="#EF5098"
/>
<path
d="M2.72503 5.2583C2.23266 5.42016 1.80253 5.73058 1.49379 6.14687C1.18505 6.56317 1.01286 7.0649 1.00091 7.58305C0.988962 8.1012 1.13784 8.61033 1.42706 9.04041C1.71628 9.4705 2.13164 9.80042 2.61603 9.9848L5.81003 7.0998L5.22653 5.8498L2.72503 5.2583Z"
fill="#17A8E0"
/>
<path
d="M10.312 13.3197C10.6209 13.5543 10.9975 13.6825 11.3853 13.6851C11.7732 13.6877 12.1514 13.5646 12.4634 13.3342C12.7755 13.1038 13.0044 12.7785 13.116 12.407C13.2276 12.0356 13.2159 11.6379 13.0825 11.2737L10.812 10.7412L10.312 13.3197Z"
fill="#93C83E"
/>
<path
d="M10.7735 10.145L13.2735 10.7285C13.7666 10.5672 14.1976 10.257 14.507 9.84058C14.8165 9.42415 14.9891 8.922 15.0013 8.40333C15.0134 7.88467 14.8643 7.375 14.5747 6.94457C14.2851 6.51414 13.8691 6.18413 13.384 6L10.1135 8.8665L10.7735 10.145Z"
fill="#0779A1"
/>
</svg>
);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
function HerokuIcon(): JSX.Element {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_2061_4245)">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M13.6285 0H2.10787C1.31283 0 0.666748 0.644312 0.666748 1.44112V14.5602C0.666748 15.3557 1.31283 16 2.10787 16H13.6285C14.4237 16 15.0678 15.3557 15.0678 14.5602V1.44112C15.0678 0.644312 14.4237 0 13.6285 0ZM14.2677 14.5602C14.2677 14.9135 13.9815 15.1994 13.6285 15.1994H2.10787C1.75506 15.1994 1.46688 14.9135 1.46688 14.5602V1.44112C1.46688 1.08654 1.75506 0.800133 2.10787 0.800133H13.6285C13.9815 0.800133 14.2677 1.08654 14.2677 1.44112V14.5602ZM4.26699 13.6009L6.06823 12.0002L4.26699 10.3999V13.6009ZM10.7719 7.11485C10.4481 6.78927 9.8569 6.40038 8.86819 6.40038C7.78342 6.40038 6.66611 6.68302 5.86752 6.94177V2.40082H4.26704V9.33907L5.39807 8.82667C5.41688 8.81826 7.24025 8.00086 8.86819 8.00086C9.68027 8.00086 9.86022 8.44796 9.86885 8.82158V13.6009H11.4676V8.801C11.4693 8.6983 11.4592 7.81051 10.7719 7.11485ZM8.66761 5.00027H10.2681C10.9912 4.1811 11.3597 3.30903 11.4677 2.40066H9.86881C9.69063 3.30726 9.29643 4.17424 8.66761 5.00027Z"
fill="#6762A6"
/>
</g>
<defs>
<clipPath id="clip0_2061_4245">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>
);
}
export default HerokuIcon;

View File

@@ -0,0 +1,82 @@
function JuiceBoxIcon(): JSX.Element {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.15412 6.3041L3.80207 4.64419C3.80207 4.64419 3.79429 4.48531 3.94429 4.40531C4.09539 4.32532 6.47082 3.35204 6.66192 3.35204C6.85302 3.35204 7.97185 3.56758 8.78625 3.74868C9.60065 3.92978 11.7528 4.39198 11.7628 4.53308C11.7728 4.67419 9.56066 6.93629 9.56066 6.93629L7.15412 6.3041Z"
fill="#C3FECE"
/>
<path
d="M10.2117 5.2686C10.2117 5.2686 8.80626 4.85529 7.43855 4.53419C6.80192 4.3842 4.93091 3.992 4.93091 3.992C4.93091 3.992 5.12979 3.90534 5.27978 3.84201C5.43199 3.77868 5.5531 3.74535 5.5531 3.74535C5.5531 3.74535 6.77303 3.98867 7.67965 4.18199C8.90625 4.44309 10.6228 4.95528 10.6628 5.03639C10.7039 5.11639 10.2117 5.2686 10.2117 5.2686Z"
fill="white"
/>
<path
d="M7.48978 4.87973C7.19091 5.0875 7.48423 5.21305 8.03531 5.34193C8.63861 5.48303 9.09858 5.6908 9.48634 5.46526C9.83632 5.26304 8.9797 5.04306 8.58639 4.95862C8.28641 4.89418 7.72088 4.71974 7.48978 4.87973Z"
fill="#ACB1B2"
/>
<path
d="M8.58521 5.29749C8.58299 5.29749 8.59521 2.86317 8.59521 2.77317C8.59521 2.68318 8.49411 2.60207 8.60521 2.41097C8.71632 2.21987 9.00741 2.23987 9.00741 2.23987C9.00741 2.23987 10.2151 1.88878 10.6973 1.76767C11.1795 1.64657 12.2461 1.36547 12.2461 1.36547L12.3172 1.97544C12.3172 1.97544 11.1995 2.29098 10.6462 2.43208C10.0929 2.57319 9.33851 2.80428 9.33851 2.80428L9.2274 2.90539L9.25962 5.28415C9.25962 5.28415 9.1563 5.3997 8.9252 5.3997C8.70076 5.39859 8.58521 5.29749 8.58521 5.29749Z"
fill="#FFD816"
/>
<path
d="M12.0472 1.67768C12.0561 1.86767 12.1572 1.98544 12.2772 1.98544C12.3972 1.98544 12.4794 1.83211 12.4616 1.63212C12.4427 1.43325 12.3138 1.34214 12.2038 1.3788C12.0939 1.41547 12.0405 1.5388 12.0472 1.67768Z"
fill="#FEB804"
/>
<path
d="M9.22962 2.96094C9.20851 2.96094 9.1874 2.95539 9.16851 2.94206L8.59187 2.55763C8.54076 2.52319 8.52743 2.4543 8.56076 2.4032C8.59521 2.35209 8.66298 2.33764 8.7152 2.37209L9.29184 2.75651C9.34294 2.79095 9.35628 2.85984 9.32295 2.91095C9.30072 2.94317 9.26517 2.96094 9.22962 2.96094Z"
fill="#FEB804"
/>
<path
d="M9.31847 2.86761C9.28514 2.86761 9.25181 2.85206 9.22959 2.82317C9.08182 2.62651 8.91294 2.40319 8.88294 2.36875C8.84516 2.33208 8.83739 2.27209 8.86739 2.22653C8.90183 2.17542 8.9696 2.16098 9.02182 2.19542C9.04182 2.20876 9.05293 2.21653 9.40736 2.68873C9.44402 2.73762 9.41069 2.80761 9.3618 2.84428C9.34181 2.85983 9.34181 2.86761 9.31847 2.86761Z"
fill="#FEB804"
/>
<path
d="M9.18298 3.05427C9.17742 3.05427 9.17187 3.05427 9.1652 3.05316L8.60968 2.96539C8.54968 2.95539 8.50746 2.89872 8.51746 2.83873C8.52746 2.77873 8.58301 2.73651 8.64412 2.74651L9.19965 2.83428C9.25964 2.84428 9.31186 2.85539 9.30186 2.9165C9.29408 2.96983 9.23742 3.05427 9.18298 3.05427Z"
fill="#FEB804"
/>
<path
d="M3.79761 4.63197C3.97426 4.63197 5.39197 5.00417 6.12304 5.13861C6.85411 5.27304 9.06177 5.79524 9.13621 5.80968C9.21066 5.82413 9.19621 6.77963 9.19621 6.77963C9.19621 6.77963 10.2406 12.1793 10.2106 12.4338C10.1806 12.6871 9.49508 14.6414 9.49508 14.6414C9.49508 14.6414 9.09177 14.7014 8.06294 14.4026C7.0341 14.1037 4.31869 13.3582 4.15426 13.1493C3.98982 12.9404 4.09426 10.1806 4.00537 8.48065C3.91427 6.77963 3.79761 4.63197 3.79761 4.63197Z"
fill="#79DD8A"
/>
<path
d="M12.0294 13.2682C12.086 13.0804 11.7705 11.0672 11.7261 9.21728C11.6816 7.36849 11.8405 4.5553 11.7638 4.53308C11.6872 4.51197 9.13621 5.80968 9.13621 5.80968C9.13621 5.80968 9.1251 7.64514 9.15621 9.37616C9.1962 11.616 9.37508 14.5814 9.49396 14.6414C9.61285 14.7014 10.645 14.1237 10.9561 13.9548C11.5072 13.6559 11.9849 13.4182 12.0294 13.2682Z"
fill="#02AB46"
/>
<path
d="M10.204 6.65964L10.2095 5.26638L10.6662 5.04195L10.6795 6.56964L10.4684 6.93962L10.204 6.65964Z"
fill="#DBDFE1"
/>
<path
d="M6.16088 7.1485C5.17427 7.04295 4.49097 7.9229 4.5443 9.06951C4.60763 10.4239 5.4998 11.4216 6.46531 11.6205C7.43081 11.8194 8.36632 11.4005 8.38632 9.91947C8.40854 8.37621 7.05194 7.24294 6.16088 7.1485Z"
fill="#FEFEFD"
/>
<path
d="M6.81861 8.3851C6.81861 8.3851 6.61862 7.71292 6.01754 7.89179C5.41646 8.07067 5.09092 9.37838 6.11087 9.96724C7.09082 10.5328 7.99299 9.49504 7.67745 8.82286C7.3908 8.20956 6.81861 8.3851 6.81861 8.3851Z"
fill="#EF5B44"
/>
<path
d="M6.58968 7.56292C6.52191 7.58737 6.46858 7.86402 6.53968 8.12845C6.59302 8.32622 6.76301 8.59731 6.86189 8.57954C6.94077 8.56509 7.00077 8.254 6.92633 7.9929C6.83967 7.69181 6.66857 7.53404 6.58968 7.56292Z"
fill="#B8CF17"
/>
<path
d="M6.57531 9.12284C6.2931 9.03729 5.97534 10.0072 6.54642 10.6116C7.17639 11.2772 7.9119 10.6905 7.89191 10.5617C7.86302 10.3828 7.36971 10.2539 7.12639 9.9539C6.88307 9.6517 6.73974 9.17284 6.57531 9.12284Z"
fill="#FD8F01"
/>
<path
d="M5.67307 8.9584C5.67307 8.9584 5.72973 8.53398 5.33642 8.55731C4.95644 8.58065 5.04977 9.02951 5.04977 9.02951C5.04977 9.02951 4.73312 9.06729 4.78645 9.41838C4.82645 9.68392 5.10643 9.68059 5.10643 9.68059C5.10643 9.68059 4.79867 9.80169 4.93866 10.1528C5.0631 10.465 5.34975 10.3394 5.34975 10.3394C5.34975 10.3394 5.2442 10.6439 5.49308 10.805C5.70307 10.9405 5.88639 10.8261 5.88639 10.8261C5.88639 10.8261 5.87528 11.0861 6.16526 11.1338C6.51636 11.1916 6.66857 10.7639 6.41858 10.5661C6.22859 10.4161 6.05638 10.5328 6.05638 10.5328C6.05638 10.5328 6.09193 10.4494 6.0586 10.3539C6.03638 10.2917 6.00083 10.2683 6.00083 10.2683C6.00083 10.2683 6.30859 10.2394 6.25859 9.88947C6.2086 9.53837 5.90083 9.5817 5.90083 9.5817C5.90083 9.5817 6.07971 9.43838 6.00861 9.18061C5.93861 8.92174 5.67307 8.9584 5.67307 8.9584Z"
fill="#A281D0"
/>
<path
d="M10.5205 7.04739C10.265 7.04739 9.17503 5.96412 9.16503 5.95301C9.12504 5.90634 9.13059 5.83635 9.17726 5.79635C9.22392 5.75635 9.29281 5.76191 9.33391 5.80746C9.40391 5.88523 10.2372 6.65853 10.4772 6.80741C10.6327 6.5752 11.1383 5.55858 11.5727 4.64085C11.5994 4.5853 11.666 4.56197 11.7205 4.58752C11.776 4.61419 11.7993 4.67974 11.7738 4.73529C10.7027 6.99851 10.5972 7.02851 10.5405 7.04406C10.5339 7.0474 10.5272 7.04739 10.5205 7.04739Z"
fill="#2D802D"
/>
</svg>
);
}
export default JuiceBoxIcon;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,38 @@
function MagicBallIcon(): JSX.Element {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M13.2883 12.3928C14.4283 11.0451 15.4015 8.62412 14.396 5.84427C13.9061 4.49101 13.3528 3.73549 12.7084 3.25552C12.3162 2.96331 10.7496 2.2278 8.67524 2.54889C7.15865 2.78443 5.1332 3.74105 3.8166 5.29763C2.54 6.80978 1.5545 8.00416 1.50895 9.1641C1.45006 10.6562 2.57445 11.9384 2.71111 12.1462C2.95443 12.515 4.61434 14.6238 7.73306 14.7316C10.4862 14.826 12.2795 13.5861 13.2883 12.3928Z"
fill="#403D3E"
/>
<path
d="M4.04763 2.43331C2.61104 3.45881 0.996679 5.55647 1.28666 8.49854C1.42777 9.93069 1.77997 10.7995 2.2855 11.4228C2.59326 11.8028 3.92875 12.9328 6.02086 13.0994C8.12964 13.2672 9.73288 12.795 11.4072 11.6306C14.8104 9.26295 14.3093 5.68313 14.1638 5.26649C14.0182 4.84984 12.9283 2.39664 9.93176 1.52446C7.28746 0.755617 5.31867 1.52446 4.04763 2.43331Z"
fill="#5E6367"
/>
<path
d="M6.57189 2.63219C4.99308 2.57553 3.48427 3.81213 3.33872 5.33871C3.19318 6.86419 4.13757 8.02635 5.6086 8.263C7.07964 8.49855 8.78066 7.60526 9.12286 5.73425C9.47618 3.80657 8.07958 2.68663 6.57189 2.63219Z"
fill="white"
/>
<path
d="M7.05971 5.24541C7.05971 5.24541 7.43969 5.16653 7.51524 4.60655C7.58968 4.05547 7.31636 3.5855 6.67862 3.41662C5.98532 3.23329 5.51535 3.61438 5.39313 4.01547C5.22314 4.57322 5.47424 4.83876 5.47424 4.83876C5.47424 4.83876 4.79427 5.00209 4.73983 5.80427C4.68872 6.5609 5.20536 6.96754 5.72422 7.09198C6.3653 7.24642 7.09193 7.07087 7.2697 6.25313C7.41747 5.57984 7.05971 5.24541 7.05971 5.24541Z"
fill="#303030"
/>
<path
d="M5.99081 4.22544C5.92971 4.45543 6.05192 4.67764 6.29191 4.73874C6.55079 4.8043 6.78633 4.71875 6.84966 4.45431C6.90521 4.21988 6.79411 4.01323 6.52079 3.94656C6.29635 3.89101 6.05748 3.97434 5.99081 4.22544Z"
fill="white"
/>
<path
d="M6.18643 5.36871C5.89533 5.27871 5.51091 5.39093 5.4498 5.78202C5.38869 6.17311 5.62313 6.3731 5.92978 6.42865C6.23643 6.4842 6.52641 6.3231 6.58308 6.00978C6.63863 5.69758 6.47641 5.45759 6.18643 5.36871Z"
fill="white"
/>
</svg>
);
}
export default MagicBallIcon;

View File

@@ -0,0 +1,68 @@
export default function MongoDBIcon(): JSX.Element {
return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M7.26568 13.0004L6.94382 12.8937C6.94382 12.8937 6.98668 11.2651 6.39739 11.1507C6.01168 10.7015 6.45439 -8.02403 7.86439 11.0868C7.59687 11.2225 7.39217 11.4564 7.29311 11.7395C7.24001 12.1577 7.23082 12.5803 7.26568 13.0004Z"
fill="url(#paint0_linear_2061_4238)"
/>
<path
d="M7.43957 11.4272C8.29654 10.7821 8.95282 9.90701 9.33214 8.90369C9.71147 7.90037 9.79826 6.81 9.58243 5.75931C8.95243 2.98002 7.46058 2.06631 7.29986 1.71745C7.1612 1.50022 7.04284 1.27068 6.94629 1.03174L7.065 8.7756C7.065 8.7756 6.819 11.1422 7.43957 11.4272Z"
fill="url(#paint1_linear_2061_4238)"
/>
<path
d="M6.78015 11.5296C6.78015 11.5296 4.15687 9.74286 4.30858 6.58214C4.32273 5.62928 4.54121 4.69054 4.94926 3.82935C5.3573 2.96816 5.94542 2.20456 6.67387 1.59014C6.759 1.51782 6.82663 1.42715 6.87168 1.32493C6.91674 1.22272 6.93805 1.11163 6.93401 1C7.0973 1.35143 7.07072 6.247 7.08787 6.81957C7.1543 9.04686 6.96401 11.1091 6.78015 11.5296Z"
fill="url(#paint2_linear_2061_4238)"
/>
<defs>
<linearGradient
id="paint0_linear_2061_4238"
x1="5.1021"
y1="7.10853"
x2="8.80138"
y2="8.36386"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.231" stopColor="#999875" />
<stop offset="0.563" stopColor="#9B9977" />
<stop offset="0.683" stopColor="#A09F7E" />
<stop offset="0.768" stopColor="#A9A889" />
<stop offset="0.837" stopColor="#B7B69A" />
<stop offset="0.896" stopColor="#C9C7B0" />
<stop offset="0.948" stopColor="#DEDDCB" />
<stop offset="0.994" stopColor="#F8F6EB" />
<stop offset="1" stopColor="#FBF9EF" />
</linearGradient>
<linearGradient
id="paint1_linear_2061_4238"
x1="6.45855"
y1="0.976404"
x2="8.09399"
y2="11.1888"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#48A547" />
<stop offset="1" stopColor="#3F9143" />
</linearGradient>
<linearGradient
id="paint2_linear_2061_4238"
x1="4.08293"
y1="6.89501"
x2="8.47176"
y2="5.42519"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#41A247" />
<stop offset="0.352" stopColor="#4BA74B" />
<stop offset="0.956" stopColor="#67B554" />
<stop offset="1" stopColor="#69B655" />
</linearGradient>
</defs>
</svg>
);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,22 @@
function NginxIcon(): JSX.Element {
return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M6.97775 1H7.00561C7.14837 1.068 7.28743 1.14353 7.42218 1.22629C8.97332 2.11829 10.5245 3.01057 12.0756 3.90314C12.1356 3.93517 12.1846 3.9845 12.2163 4.04472C12.2479 4.10493 12.2607 4.17327 12.253 4.24086C12.2496 6.12186 12.253 8.00243 12.2509 9.88257C12.2307 9.9723 12.1759 10.0504 12.0983 10.0999C10.4489 11.0496 8.79932 11.9987 7.14961 12.9473C7.10963 12.9778 7.06144 12.9956 7.01125 12.9984C6.96106 13.0012 6.91118 12.9889 6.86803 12.9631C5.21661 12.0163 3.56675 11.0677 1.91846 10.1174C1.86489 10.0921 1.82003 10.0515 1.78952 10.0007C1.75901 9.94988 1.74423 9.89119 1.74703 9.832C1.74703 7.95143 1.74703 6.071 1.74703 4.19071C1.74299 4.13178 1.75661 4.07299 1.78616 4.02184C1.8157 3.97069 1.85983 3.92951 1.91289 3.90357C3.46203 3.01271 5.01118 2.12129 6.56032 1.22929C6.69832 1.15043 6.83375 1.06686 6.97775 1Z"
fill="#019639"
/>
<path
d="M3.90007 4.65924C3.90007 6.21039 3.90007 7.76167 3.90007 9.3131C3.89809 9.39901 3.91326 9.48446 3.94468 9.56445C3.9761 9.64444 4.02315 9.71736 4.08307 9.77896C4.19794 9.89243 4.34824 9.96309 4.5089 9.97916C4.66956 9.99522 4.83087 9.95572 4.96593 9.86724C5.05634 9.80581 5.13035 9.72321 5.18152 9.62662C5.23269 9.53003 5.25946 9.4224 5.2595 9.3131C5.2595 8.19024 5.25736 7.06739 5.2595 5.94453C6.28322 7.17024 7.30907 8.39424 8.33707 9.61653C8.4799 9.76122 8.65676 9.86772 8.85145 9.92628C9.04614 9.98484 9.25242 9.99357 9.45136 9.95167C9.59184 9.924 9.71973 9.85198 9.81624 9.74622C9.91275 9.64045 9.97278 9.50652 9.9875 9.3641C9.98979 7.78096 9.98979 6.19796 9.9875 4.6151C9.97275 4.44614 9.8952 4.28884 9.77016 4.17425C9.64512 4.05966 9.48168 3.99609 9.31207 3.99609C9.14247 3.99609 8.97902 4.05966 8.85399 4.17425C8.72895 4.28884 8.6514 4.44614 8.63665 4.6151C8.63665 5.75596 8.62979 6.89553 8.63665 8.03596C7.63122 6.85053 6.63822 5.65481 5.63665 4.4651C5.5046 4.29578 5.32979 4.16474 5.13023 4.08549C4.93067 4.00624 4.71359 3.98165 4.50136 4.01424C4.34059 4.03214 4.19154 4.10704 4.08124 4.22536C3.97093 4.34369 3.90666 4.49761 3.90007 4.65924Z"
fill="white"
/>
</svg>
);
}
export default NginxIcon;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,60 @@
export default function RedisIcon(): JSX.Element {
return (
<svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<g clipPath="url(#clip0_2061_4282)">
<path
d="M13.3198 10.158C12.5879 10.5395 8.79654 12.0984 7.98938 12.5192C7.18221 12.94 6.73382 12.936 6.09616 12.6312C5.45855 12.3263 1.42388 10.6966 0.697072 10.3492C0.333858 10.1756 0.142822 10.029 0.142822 9.8906V8.50438C0.142822 8.50438 5.3955 7.3609 6.24348 7.05667C7.09141 6.75244 7.38563 6.74145 8.10723 7.00578C8.82895 7.2702 13.1439 8.0487 13.8571 8.30992L13.8568 9.67653C13.8569 9.81356 13.6923 9.96388 13.3198 10.158Z"
fill="#912626"
/>
<path
d="M13.3195 8.77972C12.5877 9.16104 8.79642 10.72 7.98926 11.1407C7.18215 11.5616 6.73376 11.5575 6.09615 11.2527C5.45849 10.9481 1.42397 9.31805 0.697221 8.97086C-0.029529 8.62345 -0.0447433 8.38436 0.66915 8.10482C1.38304 7.82518 5.39544 6.25098 6.24353 5.94675C7.09145 5.64263 7.38561 5.63154 8.10722 5.89597C8.82888 6.16029 12.5975 7.66034 13.3106 7.9215C14.024 8.18298 14.0513 8.39829 13.3195 8.77972Z"
fill="#C6302B"
/>
<path
d="M13.3198 7.91483C12.5879 8.29637 8.79654 9.85519 7.98938 10.2762C7.18221 10.6968 6.73382 10.6928 6.09616 10.388C5.4585 10.0833 1.42388 8.45338 0.697072 8.10597C0.333858 7.9324 0.142822 7.78604 0.142822 7.64756V6.26119C0.142822 6.26119 5.3955 5.11776 6.24348 4.81353C7.09141 4.50929 7.38563 4.49826 8.10723 4.76263C8.829 5.02701 13.144 5.80535 13.8571 6.06661L13.8568 7.43338C13.8569 7.57036 13.6923 7.72069 13.3198 7.91483Z"
fill="#912626"
/>
<path
d="M13.3195 6.53701C12.5877 6.91844 8.79642 8.47726 7.98926 8.89817C7.18215 9.31892 6.73376 9.3148 6.09615 9.00998C5.45849 8.70537 1.42397 7.07541 0.697221 6.72816C-0.029529 6.38085 -0.0447433 6.14171 0.66915 5.86207C1.38304 5.58258 5.39549 4.00828 6.24353 3.7041C7.09145 3.39992 7.38561 3.38889 8.10722 3.65326C8.82888 3.91758 12.5975 5.41753 13.3106 5.6788C14.024 5.94023 14.0513 6.15558 13.3195 6.53701Z"
fill="#C6302B"
/>
<path
d="M13.3198 5.58855C12.5879 5.96998 8.79654 7.5289 7.98938 7.94987C7.18221 8.37062 6.73382 8.36649 6.09616 8.06167C5.4585 7.75701 1.42388 6.12705 0.697072 5.7798C0.333858 5.60607 0.142822 5.45965 0.142822 5.32133V3.9349C0.142822 3.9349 5.3955 2.79153 6.24348 2.48735C7.09141 2.18307 7.38563 2.17214 8.10723 2.43646C8.829 2.70083 13.144 3.47917 13.8571 3.74044L13.8568 5.10715C13.8569 5.24403 13.6923 5.39435 13.3198 5.58855Z"
fill="#912626"
/>
<path
d="M13.3195 4.21078C12.5876 4.59221 8.79639 6.15113 7.98923 6.57188C7.18212 6.99263 6.73373 6.98851 6.09612 6.68385C5.45852 6.37903 1.42394 4.74917 0.697248 4.40187C-0.0295558 4.05462 -0.0447165 3.81542 0.669123 3.53583C1.38302 3.2563 5.39546 1.68221 6.2435 1.37792C7.09143 1.07369 7.38559 1.06276 8.1072 1.32714C8.82886 1.59151 12.5975 3.09146 13.3106 3.35272C14.0239 3.61394 14.0513 3.82935 13.3195 4.21078Z"
fill="#C6302B"
/>
<path
d="M8.67572 2.86218L7.49661 2.9846L7.23267 3.61974L6.80635 2.91099L5.44483 2.78863L6.46076 2.42226L6.15594 1.85986L7.1071 2.23186L8.00378 1.93829L7.76142 2.51981L8.67572 2.86218ZM7.16228 5.94351L4.96172 5.03081L8.11494 4.54679L7.16228 5.94351ZM4.11138 3.21522C5.04219 3.21522 5.79674 3.50772 5.79674 3.86847C5.79674 4.22933 5.04219 4.52177 4.11138 4.52177C3.18058 4.52177 2.42603 4.22927 2.42603 3.86847C2.42603 3.50772 3.18058 3.21522 4.11138 3.21522Z"
fill="white"
/>
<path
d="M10.0693 3.0357L11.9355 3.77316L10.0709 4.50993L10.0693 3.0357Z"
fill="#621B1C"
/>
<path
d="M8.00464 3.85234L10.0693 3.03564L10.0709 4.50988L9.86844 4.58906L8.00464 3.85234Z"
fill="#9A2928"
/>
</g>
<defs>
<clipPath id="clip0_2061_4282">
<rect
width="13.7143"
height="13.7143"
fill="white"
transform="translate(0.142822 0.143066)"
/>
</clipPath>
</defs>
</svg>
);
}

View File

@@ -0,0 +1,110 @@
function TentIcon(): JSX.Element {
return (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.75393 9.01964L6.72187 8.90409L6.18079 14.0994C6.18079 14.0994 7.01185 14.0416 8.1118 14.0416C9.21174 14.0416 10.1395 14.1194 10.1395 14.1194L9.75393 9.01964Z"
fill="#8A2E08"
/>
<path
d="M3.65538 9.74625L2.9132 9.59182C2.9132 9.59182 2.73099 10.7684 2.56322 11.4973C2.39545 12.2261 2.11658 13.2116 2.11658 13.2116L3.88093 14.0882C3.88093 14.0882 5.47751 14.2282 5.50529 14.1727C5.53306 14.1171 6.70967 11.3851 6.70967 11.3851L6.98965 10.334L3.65538 9.74625Z"
fill="#FF6110"
/>
<path
d="M9.17395 10.3207L12.8715 9.55071L13.0393 9.99847C13.0393 9.99847 13.2826 11.0162 13.4171 11.5673C13.6337 12.4517 13.9126 13.2439 13.9126 13.2439L12.2538 14.2705L10.545 14.2149L9.17395 10.3207Z"
fill="#FF6110"
/>
<path
d="M2.90422 9.15296C2.90422 9.17852 2.92199 9.36406 2.9131 9.49961C2.90422 9.63516 2.87866 9.80182 2.87866 9.80182C2.87866 9.80182 2.97977 9.82404 3.0731 9.88403C3.16642 9.94403 3.35308 10.1729 3.35308 10.1729C3.35308 10.1729 3.71751 10.0385 3.93861 10.1218C4.27859 10.2496 4.42303 10.564 4.42303 10.564C4.42303 10.564 4.77078 10.2407 5.19632 10.3096C5.61185 10.3762 5.77407 10.7684 5.77407 10.7684C5.77407 10.7684 6.08627 10.5162 6.35959 10.5307C6.68291 10.5473 6.80179 10.7851 6.80179 10.7851L7.28621 9.80848L3.68529 9.15407H2.90422V9.15296Z"
fill="#AF0D03"
/>
<path
d="M1.93542 13.6716L2.13097 13.1539C2.13097 13.1539 2.99759 13.5783 3.62534 13.7649C4.25308 13.9516 4.99304 13.9772 4.99304 13.9772C4.99304 13.9772 4.98415 13.6838 4.71194 13.4716C4.43974 13.2594 4.00865 12.9028 4.00865 12.9028L4.59751 12.7061L4.92527 12.575L5.95299 13.7983L5.6219 14.6227C5.6219 14.6227 4.89971 14.716 3.74533 14.4194C2.59095 14.1227 1.93542 13.6716 1.93542 13.6716Z"
fill="#C9C9C9"
/>
<path
d="M9.104 9.63738C9.12067 9.70516 9.11289 10.2407 9.11289 10.2407L9.42732 10.5973C9.42732 10.5973 9.59731 10.3762 9.9884 10.394C10.3795 10.4107 10.5573 10.5896 10.5573 10.5896C10.5573 10.5896 10.795 10.3007 11.1183 10.2418C11.4417 10.1818 11.755 10.3518 11.755 10.3518C11.755 10.3518 11.9761 9.94403 12.2983 9.85071C12.6216 9.75738 13.0394 9.99959 13.0394 9.99959L12.8671 8.95075L9.69953 9.17852L9.104 9.63738Z"
fill="#AF0D03"
/>
<path
d="M13.8782 13.1372L14.0815 13.6549C14.0815 13.6549 13.4615 14.1394 12.6627 14.3605C11.8638 14.5816 10.7528 14.6704 10.7528 14.6704L10.4283 12.8494L11.5238 12.5183L12.1249 12.775C12.1249 12.775 11.5927 13.2227 11.4472 13.4283C11.1705 13.8172 11.2694 14.0638 11.2694 14.0638C11.2694 14.0638 12.1183 13.9872 12.7727 13.7072C13.4282 13.425 13.8782 13.1372 13.8782 13.1372Z"
fill="#C9C9C9"
/>
<path
d="M4.42432 12.8905C4.42432 12.8905 4.80096 13.0594 4.99318 13.1961C5.23094 13.3661 5.46871 13.6205 5.54537 14.0283C5.5987 14.3094 5.62203 14.6227 5.62203 14.6227C5.62203 14.6227 5.88535 14.826 6.4031 13.9605C6.92085 13.095 7.18417 11.8962 7.2275 11.0473C7.26972 10.1985 7.25306 10.0029 7.25306 10.0029L6.48865 11.2595L5.35871 12.2872L4.42432 12.8905Z"
fill="#D92F0A"
/>
<path
d="M7.25966 10.0074C7.25966 10.0074 7.34855 10.7784 6.30971 11.735C5.66419 12.3295 5.03422 12.7383 4.60314 12.9072C4.45537 12.965 4.32315 12.9417 4.22982 12.9405C3.92539 12.9361 3.98206 12.8839 4.15205 12.7972C4.35204 12.695 4.93312 12.4828 6.02084 11.4806C6.89079 10.6784 6.95079 9.87626 6.95079 9.87626L7.25855 9.7096V10.0074H7.25966Z"
fill="#FFFEFF"
/>
<path
d="M10.7949 14.666C10.7949 14.666 10.4127 14.8015 9.835 14.0205C9.25725 13.2394 9.07948 11.855 9.05393 11.4128C9.02837 10.9706 9.03726 10.1385 9.03726 10.1385L10.0994 11.4717C10.0994 11.4717 11.7471 12.7117 11.7216 12.7205C11.696 12.7294 11.1838 13.0516 10.9916 13.375C10.5994 14.0283 10.7949 14.666 10.7949 14.666Z"
fill="#D92F0A"
/>
<path
d="M8.97839 9.77071C8.97839 9.77071 8.96284 10.0618 9.0195 10.2829C9.13839 10.7495 9.54836 11.254 10.0817 11.7273C10.7694 12.3384 11.5683 12.7639 11.6271 12.7806C11.6871 12.7972 12.1116 12.815 12.1193 12.7717C12.1227 12.7572 12.1482 12.6939 12.0093 12.5928C11.7238 12.3872 11.0916 12.025 10.5739 11.6162C10.2083 11.3273 9.73947 10.9684 9.33171 10.2285C9.25727 10.094 9.28393 9.75405 9.28393 9.75405L8.97839 9.77071Z"
fill="white"
/>
<path
d="M3.32974 7.25306C3.32974 7.25306 2.44534 7.36861 2.41756 7.49416C2.38979 7.62082 2.48756 9.28962 2.48756 9.28962C2.48756 9.28962 2.85976 9.22074 3.04753 9.26185C3.2353 9.30296 3.58861 9.53183 3.58861 9.53183L4.18081 9.16741L4.60745 9.7596C4.60745 9.7596 4.90854 9.60405 5.32519 9.64516C5.74183 9.68627 6.02182 9.96736 6.02182 9.96736L6.60401 9.4885L7.25398 9.99959C7.25398 9.99959 7.55063 9.65405 8.10171 9.64405C8.65279 9.63405 8.98611 10.0396 8.98611 10.0396L9.71385 9.40517L10.4316 9.95625C10.4316 9.95625 10.7127 9.72738 11.0349 9.65516C11.3571 9.58183 11.596 9.70738 11.596 9.70738L12.0537 8.89631L12.5737 9.27074C12.5737 9.27074 12.7404 9.07297 12.9792 9.04186C13.2181 9.01075 13.5092 9.00075 13.5092 9.00075C13.5092 9.00075 13.4881 7.87748 13.4881 7.71082C13.4881 7.54416 13.4259 7.27417 13.3525 7.22195C13.2792 7.16973 12.6248 7.02419 12.6248 7.02419L3.32974 7.25306Z"
fill="#D92F0A"
/>
<path
d="M4.4997 7.73859L3.63197 7.93969L3.58752 9.53294C3.58752 9.53294 3.85195 9.45628 4.11639 9.48405C4.38082 9.51183 4.60636 9.76182 4.60636 9.76182C4.60636 9.76182 4.6008 8.72632 4.60969 8.55188C4.61858 8.37745 4.68636 8.04635 4.72302 7.98302C4.75968 7.91858 4.4997 7.73859 4.4997 7.73859Z"
fill="#E1E1E1"
/>
<path
d="M6.78407 8.07747L5.99189 8.36745C5.99189 8.36745 5.97078 9.10074 5.98967 9.39295C6.00744 9.68516 6.02078 9.96959 6.02078 9.96959C6.02078 9.96959 6.38187 9.84959 6.67519 9.86737C6.96739 9.88515 7.25293 10.0018 7.25293 10.0018L7.24182 8.433L6.78407 8.07747Z"
fill="#E1E1E1"
/>
<path
d="M8.95947 8.40634C8.95947 8.40634 8.98725 9.00075 8.99614 9.30184C9.00503 9.60294 8.98503 10.0429 8.98503 10.0429C8.98503 10.0429 9.41612 9.82293 9.75499 9.79515C10.0927 9.76738 10.4305 9.95959 10.4305 9.95959C10.4305 9.95959 10.4038 9.33851 10.3672 8.88187C10.3416 8.56299 10.2483 8.28745 10.2483 8.28745L9.38056 7.82192L8.95947 8.40634Z"
fill="#E1E1E1"
/>
<path
d="M11.5949 9.70961C11.5949 9.70961 11.8827 9.39296 12.1116 9.31963C12.3405 9.2463 12.5727 9.27296 12.5727 9.27296C12.5727 9.27296 12.5593 8.52522 12.5227 8.18635C12.486 7.84859 12.4527 7.65416 12.4527 7.65416L11.3905 7.19974L11.4205 8.03525C11.4205 8.03525 11.5461 8.31412 11.5827 8.79854C11.6183 9.28296 11.5949 9.70961 11.5949 9.70961Z"
fill="#E1E1E1"
/>
<path
d="M8.04625 4.2021L6.7641 4.77318C6.7641 4.77318 5.5986 5.64758 4.67643 6.23088C3.75425 6.81418 3.22428 7.03639 3.11429 7.08195C2.93874 7.15528 2.65431 7.24194 2.53099 7.32638C2.32655 7.46859 2.4121 7.53526 2.44543 7.54637C2.47877 7.55748 3.49871 8.01412 5.30639 8.28188C7.11408 8.54965 9.57172 8.44076 10.8317 8.19633C12.0905 7.9519 13.3526 7.22305 13.3526 7.22305C13.3526 7.22305 13.2471 7.04639 12.5482 6.74308C11.8483 6.43976 11.2539 6.11311 10.2395 5.4487C9.22508 4.78429 8.98065 4.45764 8.98065 4.45764L8.04625 4.2021Z"
fill="#FF6110"
/>
<path
d="M6.96185 5.00653C6.96185 5.00653 5.55081 6.452 3.81312 7.65305C3.62313 7.78416 3.63202 7.9397 3.63202 7.9397C3.63202 7.9397 3.77202 7.99303 4.11644 8.07303C4.46642 8.15414 4.66752 8.17414 4.66752 8.17414C4.66752 8.17414 4.8264 7.99192 5.16528 7.65305C5.77191 7.04642 6.14856 6.56533 6.62298 5.95092C6.9374 5.54316 7.31183 5.0543 7.31183 5.0543L6.96185 5.00653Z"
fill="white"
/>
<path
d="M7.5684 5.19318C7.5684 5.19318 7.0551 6.382 6.81067 6.89531C6.69512 7.13641 6.43735 7.58305 6.20403 7.95636C6.00071 8.2819 5.99182 8.36856 5.99182 8.36856C5.99182 8.36856 6.44846 8.43412 6.66956 8.43412C6.89066 8.43412 7.24064 8.43189 7.24064 8.43189C7.24064 8.43189 7.41619 7.90859 7.60285 6.85975C7.71173 6.24979 7.87061 5.18095 7.87061 5.18095L7.5684 5.19318Z"
fill="white"
/>
<path
d="M8.27954 5.23983C8.27954 5.23983 8.39509 6.38532 8.52397 6.98862C8.69841 7.80525 8.95839 8.40522 8.95839 8.40522C8.95839 8.40522 9.39837 8.38744 9.6428 8.36411C9.88723 8.34077 10.2461 8.28633 10.2461 8.28633C10.2461 8.28633 9.82613 7.6397 9.42059 6.98862C9.15283 6.55754 8.65063 5.55425 8.56953 5.16983C8.50064 4.83874 8.27954 5.23983 8.27954 5.23983Z"
fill="white"
/>
<path
d="M8.90845 5.02984C8.90845 5.06428 9.27732 5.85091 10.1906 6.83752C10.925 7.63081 11.415 8.03857 11.415 8.03857C11.415 8.03857 11.8338 7.90969 12.0216 7.8408C12.2427 7.75969 12.4527 7.65414 12.4527 7.65414C12.4527 7.65414 11.4616 6.9664 10.8783 6.48865C10.295 6.0109 9.49064 5.34649 9.26954 5.04317C9.04844 4.73874 8.90845 5.02984 8.90845 5.02984Z"
fill="white"
/>
<path
d="M8.03411 3.40993C7.78856 3.4277 7.70746 3.84102 7.57969 3.9699C7.45191 4.09767 6.76306 4.7743 6.76306 4.7743C6.76306 4.7743 6.88528 5.41316 8.09855 5.40093C9.20849 5.38871 9.41959 4.86318 9.41959 4.86318C9.41959 4.86318 8.71074 4.10989 8.54741 3.92323C8.38409 3.73547 8.36075 3.3866 8.03411 3.40993Z"
fill="#D92F0A"
/>
<path
d="M8.07516 2.86996C8.07516 2.86996 8.28515 2.8033 8.58402 2.83774C8.88845 2.8733 9.09066 2.97218 9.40731 2.96551C9.89173 2.95663 10.2817 2.72886 10.4217 2.59998C10.5617 2.4711 10.7406 2.26222 10.6472 2.19222C10.5539 2.12223 10.1562 2.22778 9.71174 2.05223C9.31954 1.89779 9.12288 1.49448 8.56624 1.43004C8.08738 1.3756 7.94739 1.57225 7.94739 1.57225L8.07516 2.86996Z"
fill="#FF6110"
/>
<path
d="M7.88843 1.43226V3.6177L8.23841 3.52437L8.20397 1.43226H7.88843Z"
fill="#D92F0A"
/>
</svg>
);
}
export default TentIcon;

View File

@@ -0,0 +1,27 @@
import { CSSProperties } from 'react';
function PromQLIcon({
fillColor,
}: {
fillColor: CSSProperties['color'];
}): JSX.Element {
return (
<svg
width="14"
height="14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<path
d="M12 2c1 2.538 2.5 2.962 3.5 3.808.942.78 1.481 1.845 1.5 2.961 0 1.122-.527 2.198-1.464 2.992C14.598 12.554 13.326 13 12 13s-2.598-.446-3.536-1.24C7.527 10.968 7 9.892 7 8.77c0-.255 0-.508.1-.762.085.25.236.48.443.673.207.193.463.342.75.437a2.334 2.334 0 001.767-.128c.263-.135.485-.32.65-.539.166-.22.269-.468.301-.727a1.452 1.452 0 00-.11-.765 1.699 1.699 0 00-.501-.644C8 4.115 11 2 12 2zM17 16l-5 6-5-6h10z"
stroke={fillColor}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export default PromQLIcon;

View File

@@ -16,6 +16,7 @@ import { useCallback, useMemo, useState } from 'react';
// interfaces
import { IField } from 'types/api/logs/fields';
import { ILog } from 'types/api/logs/log';
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
// components
import AddToQueryHOC, { AddToQueryHOCProps } from '../AddToQueryHOC';
@@ -50,7 +51,11 @@ function LogGeneralField({
}: LogFieldProps): JSX.Element {
const html = useMemo(
() => ({
__html: convert.toHtml(dompurify.sanitize(fieldValue)),
__html: convert.toHtml(
dompurify.sanitize(fieldValue, {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
}),
[fieldValue],
);
@@ -157,8 +162,8 @@ function ListLogView({
const timestampValue = useMemo(
() =>
typeof flattenLogData.timestamp === 'string'
? dayjs(flattenLogData.timestamp).format()
: dayjs(flattenLogData.timestamp / 1e6).format(),
? dayjs(flattenLogData.timestamp).format('YYYY-MM-DD HH:mm:ss.SSS')
: dayjs(flattenLogData.timestamp / 1e6).format('YYYY-MM-DD HH:mm:ss.SSS'),
[flattenLogData.timestamp],
);

View File

@@ -21,6 +21,7 @@ import {
useMemo,
useState,
} from 'react';
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
import LogLinesActionButtons from '../LogLinesActionButtons/LogLinesActionButtons';
import LogStateIndicator from '../LogStateIndicator/LogStateIndicator';
@@ -90,12 +91,12 @@ function RawLogView({
const text = useMemo(
() =>
typeof data.timestamp === 'string'
? `${dayjs(data.timestamp).format()} | ${attributesText} ${severityText} ${
data.body
}`
: `${dayjs(
data.timestamp / 1e6,
).format()} | ${attributesText} ${severityText} ${data.body}`,
? `${dayjs(data.timestamp).format(
'YYYY-MM-DD HH:mm:ss.SSS',
)} | ${attributesText} ${severityText} ${data.body}`
: `${dayjs(data.timestamp / 1e6).format(
'YYYY-MM-DD HH:mm:ss.SSS',
)} | ${attributesText} ${severityText} ${data.body}`,
[data.timestamp, data.body, severityText, attributesText],
);
@@ -144,7 +145,9 @@ function RawLogView({
const html = useMemo(
() => ({
__html: convert.toHtml(dompurify.sanitize(text)),
__html: convert.toHtml(
dompurify.sanitize(text, { FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS] }),
),
}),
[text],
);

View File

@@ -8,6 +8,7 @@ import dompurify from 'dompurify';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { FlatLogData } from 'lib/logs/flatLogData';
import { useMemo } from 'react';
import { FORBID_DOM_PURIFY_TAGS } from 'utils/app';
import LogStateIndicator from '../LogStateIndicator/LogStateIndicator';
import { getLogIndicatorTypeForTable } from '../LogStateIndicator/utils';
@@ -76,8 +77,8 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
render: (field, item): ColumnTypeRender<Record<string, unknown>> => {
const date =
typeof field === 'string'
? dayjs(field).format()
: dayjs(field / 1e6).format();
? dayjs(field).format('YYYY-MM-DD HH:mm:ss.SSS')
: dayjs(field / 1e6).format('YYYY-MM-DD HH:mm:ss.SSS');
return {
children: (
<div className="table-timestamp">
@@ -107,7 +108,11 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
children: (
<TableBodyContent
dangerouslySetInnerHTML={{
__html: convert.toHtml(dompurify.sanitize(field)),
__html: convert.toHtml(
dompurify.sanitize(field, {
FORBID_TAGS: [...FORBID_DOM_PURIFY_TAGS],
}),
),
}}
linesPerRow={linesPerRow}
isDarkMode={isDarkMode}

View File

@@ -1,8 +1,9 @@
/* eslint-disable react/jsx-props-no-spreading */
import './DynamicColumnTable.syles.scss';
import { Button, Dropdown, MenuProps, Switch } from 'antd';
import { Button, Dropdown, Flex, MenuProps, Switch } from 'antd';
import { ColumnsType } from 'antd/lib/table';
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
import { SlidersHorizontal } from 'lucide-react';
import { memo, useEffect, useState } from 'react';
import { popupContainer } from 'utils/selectPopupContainer';
@@ -20,6 +21,7 @@ function DynamicColumnTable({
columns,
dynamicColumns,
onDragColumn,
facingIssueBtn,
...restProps
}: DynamicColumnTableProps): JSX.Element {
const [columnsData, setColumnsData] = useState<ColumnsType | undefined>(
@@ -83,19 +85,22 @@ function DynamicColumnTable({
return (
<div className="DynamicColumnTable">
{dynamicColumns && (
<Dropdown
getPopupContainer={popupContainer}
menu={{ items }}
trigger={['click']}
>
<Button
className="dynamicColumnTable-button filter-btn"
size="middle"
icon={<SlidersHorizontal size={14} />}
/>
</Dropdown>
)}
<Flex justify="flex-end" align="center" gap={8}>
{facingIssueBtn && <FacingIssueBtn {...facingIssueBtn} />}
{dynamicColumns && (
<Dropdown
getPopupContainer={popupContainer}
menu={{ items }}
trigger={['click']}
>
<Button
className="dynamicColumnTable-button filter-btn"
size="middle"
icon={<SlidersHorizontal size={14} />}
/>
</Dropdown>
)}
</Flex>
<ResizeTable
columns={columnsData}

View File

@@ -2,6 +2,7 @@
import { TableProps } from 'antd';
import { ColumnsType } from 'antd/es/table';
import { ColumnGroupType, ColumnType } from 'antd/lib/table';
import { FacingIssueBtnProps } from 'components/facingIssueBtn/FacingIssueBtn';
import { TableDataSource } from './contants';
@@ -12,6 +13,7 @@ export interface DynamicColumnTableProps extends TableProps<any> {
tablesource: typeof TableDataSource[keyof typeof TableDataSource];
dynamicColumns: TableProps<any>['columns'];
onDragColumn?: (fromIndex: number, toIndex: number) => void;
facingIssueBtn?: FacingIssueBtnProps;
}
export type GetVisibleColumnsFunction = (

View File

@@ -0,0 +1,39 @@
.time-selection-target {
display: flex;
height: 32px;
padding: 6px 6px 6px 8px;
justify-content: space-between;
align-items: center;
gap: 4px;
align-self: stretch;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
box-shadow: none;
.button-selected-text {
display: flex;
align-items: center;
gap: 6px;
}
.selected-value {
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 16px; /* 133.333% */
}
}
.lightMode {
.time-selection-target {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
.selected-value {
color: var(--bg-ink-300);
}
}
}

View File

@@ -1,13 +1,15 @@
import './TimePreference.styles.scss';
import { DownOutlined } from '@ant-design/icons';
import { Button, Dropdown } from 'antd';
import { Button, Dropdown, Typography } from 'antd';
import TimeItems, {
timePreferance,
timePreferenceType,
} from 'container/NewWidget/RightContainer/timeItems';
import { Globe } from 'lucide-react';
import { Dispatch, SetStateAction, useCallback, useMemo } from 'react';
import { menuItems } from './config';
import { TextContainer } from './styles';
function TimePreference({
setSelectedTime,
@@ -32,13 +34,22 @@ function TimePreference({
);
return (
<TextContainer noButtonMargin>
<Dropdown menu={menu}>
<Button>
{selectedTime.name} <DownOutlined />
</Button>
</Dropdown>
</TextContainer>
<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">
{selectedTime.name}
</Typography.Text>
</div>
<DownOutlined />
</Button>
</Dropdown>
);
}

View File

@@ -0,0 +1,9 @@
.facing-issue-button {
color: var(--bg-amber-500);
border-color: var(--bg-amber-500);
.ant-btn:hover {
color: var(--bg-amber-400) !important;
border-color: var(--bg-amber-300) !important;
}
}

View File

@@ -0,0 +1,62 @@
import './FacingIssueBtn.style.scss';
import { Button, Tooltip } from 'antd';
import logEvent from 'api/common/logEvent';
import cx from 'classnames';
import { FeatureKeys } from 'constants/features';
import useFeatureFlags from 'hooks/useFeatureFlag';
import { defaultTo } from 'lodash-es';
import { HelpCircle } from 'lucide-react';
import { isCloudUser } from 'utils/app';
export interface FacingIssueBtnProps {
eventName: string;
attributes: Record<string, unknown>;
message?: string;
buttonText?: string;
className?: string;
onHoverText?: string;
}
function FacingIssueBtn({
attributes,
eventName,
message = '',
buttonText = '',
className = '',
onHoverText = '',
}: FacingIssueBtnProps): JSX.Element | null {
const handleFacingIssuesClick = (): void => {
logEvent(eventName, attributes);
if (window.Intercom) {
window.Intercom('showNewMessage', defaultTo(message, ''));
}
};
const isChatSupportEnabled = useFeatureFlags(FeatureKeys.CHAT_SUPPORT)?.active;
const isCloudUserVal = isCloudUser();
return isCloudUserVal && isChatSupportEnabled ? ( // Note: we would need to move this condition to license based in future
<div className="facing-issue-button">
<Tooltip title={onHoverText} autoAdjustOverflow>
<Button
className={cx('periscope-btn', 'facing-issue-button', className)}
onClick={handleFacingIssuesClick}
icon={<HelpCircle size={14} />}
>
{buttonText || 'Facing issues?'}
</Button>
</Tooltip>
</div>
) : null;
}
FacingIssueBtn.defaultProps = {
message: '',
buttonText: '',
className: '',
onHoverText: '',
};
export default FacingIssueBtn;

View File

@@ -0,0 +1,57 @@
import { PANEL_TYPES } from 'constants/queryBuilder';
import { AlertDef } from 'types/api/alerts/def';
import { Dashboard, DashboardData } from 'types/api/dashboard/getAll';
export const chartHelpMessage = (
selectedDashboard: Dashboard | undefined,
graphType: PANEL_TYPES,
): string => `
Hi Team,
I need help in creating this chart. Here are my dashboard details
Name: ${selectedDashboard?.data.title || ''}
Panel type: ${graphType}
Dashboard Id: ${selectedDashboard?.uuid || ''}
Thanks`;
export const dashboardHelpMessage = (
data: DashboardData | undefined,
selectedDashboard: Dashboard | undefined,
): string => `
Hi Team,
I need help with this dashboard. Here are my dashboard details
Name: ${data?.title || ''}
Dashboard Id: ${selectedDashboard?.uuid || ''}
Thanks`;
export const dashboardListMessage = `Hi Team,
I need help with dashboards.
Thanks`;
export const listAlertMessage = `Hi Team,
I need help with managing alerts.
Thanks`;
export const alertHelpMessage = (
alertDef: AlertDef,
ruleId: number,
): string => `
Hi Team,
I need help in configuring this alert. Here are my alert rule details
Name: ${alertDef?.alert || ''}
Alert Type: ${alertDef?.alertType || ''}
State: ${(alertDef as any)?.state || ''}
Alert Id: ${ruleId}
Thanks`;

View File

@@ -19,4 +19,5 @@ export enum FeatureKeys {
OSS = 'OSS',
ONBOARDING = 'ONBOARDING',
CHAT_SUPPORT = 'CHAT_SUPPORT',
PLANNED_MAINTENANCE = 'PLANNED_MAINTENANCE',
}

View File

@@ -18,4 +18,5 @@ export enum LOCALSTORAGE {
DASHBOARD_VARIABLES = 'DASHBOARD_VARIABLES',
SHOW_EXPLORER_TOOLBAR = 'SHOW_EXPLORER_TOOLBAR',
PINNED_ATTRIBUTES = 'PINNED_ATTRIBUTES',
THEME_ANALYTICS = 'THEME_ANALYTICS',
}

View File

@@ -30,4 +30,6 @@ export enum QueryParams {
integration = 'integration',
pagination = 'pagination',
relativeTime = 'relativeTime',
alertType = 'alertType',
ruleId = 'ruleId',
}

View File

@@ -289,6 +289,11 @@ export enum PANEL_TYPES {
EMPTY_WIDGET = 'EMPTY_WIDGET',
}
// eslint-disable-next-line @typescript-eslint/naming-convention
export enum PANEL_GROUP_TYPES {
ROW = 'row',
}
// eslint-disable-next-line @typescript-eslint/naming-convention
export enum ATTRIBUTE_TYPES {
SUM = 'Sum',

View File

@@ -267,6 +267,21 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
const isTracesView = (): boolean =>
routeKey === 'TRACES_EXPLORER' || routeKey === 'TRACES_SAVE_VIEWS';
const isDashboardListView = (): boolean => routeKey === 'ALL_DASHBOARD';
const isDashboardView = (): boolean => {
/**
* need to match using regex here as the getRoute function will not work for
* routes with id
*/
const regex = /^\/dashboard\/[a-zA-Z0-9_-]+$/;
return regex.test(pathname);
};
const isDashboardWidgetView = (): boolean => {
const regex = /^\/dashboard\/[a-zA-Z0-9_-]+\/new$/;
return regex.test(pathname);
};
useEffect(() => {
if (isDarkMode) {
document.body.classList.remove('lightMode');
@@ -331,7 +346,14 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
<LayoutContent>
<ChildrenContainer
style={{
margin: isLogsView() || isTracesView() ? 0 : ' 0 1rem',
margin:
isLogsView() ||
isTracesView() ||
isDashboardView() ||
isDashboardWidgetView() ||
isDashboardListView()
? 0
: '0 1rem',
}}
>
{isToDisplayLayout && !renderFullScreen && <TopNav />}

View File

@@ -15,6 +15,9 @@ export const Layout = styled(LayoutComponent)`
export const LayoutContent = styled(LayoutComponent.Content)`
overflow-y: auto;
height: 100%;
&::-webkit-scrollbar {
width: 0.1rem;
}
`;
export const ChildrenContainer = styled.div`

View File

@@ -1,4 +1,5 @@
.billing-container {
margin-bottom: 40px;
padding-top: 36px;
width: 65%;

View File

@@ -59,8 +59,8 @@ function CreateAlertChannels({
*Summary:* {{ .Annotations.summary }}
*Description:* {{ .Annotations.description }}
*RelatedLogs:* {{ .Annotations.related_logs }}
*RelatedTraces:* {{ .Annotations.related_traces }}
*RelatedLogs:* {{ if gt (len .Annotations.related_logs) 0 -}} View in <{{ .Annotations.related_logs }}|logs explorer> {{- end}}
*RelatedTraces:* {{ if gt (len .Annotations.related_traces) 0 -}} View in <{{ .Annotations.related_traces }}|traces explorer> {{- end}}
*Details:*
{{ range .Labels.SortedPairs }} • *{{ .Name }}:* {{ .Value }}

View File

@@ -1,7 +1,9 @@
import { Form, Row } from 'antd';
import { ENTITY_VERSION_V4 } from 'constants/app';
import { QueryParams } from 'constants/query';
import FormAlertRules from 'container/FormAlertRules';
import { useGetCompositeQueryParam } from 'hooks/queryBuilder/useGetCompositeQueryParam';
import history from 'lib/history';
import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { AlertTypes } from 'types/api/alerts/alertTypes';
@@ -18,15 +20,25 @@ import SelectAlertType from './SelectAlertType';
function CreateRules(): JSX.Element {
const [initValues, setInitValues] = useState<AlertDef | null>(null);
const [alertType, setAlertType] = useState<AlertTypes>(
AlertTypes.METRICS_BASED_ALERT,
);
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const version = queryParams.get('version');
const alertTypeFromParams = queryParams.get(QueryParams.alertType);
const compositeQuery = useGetCompositeQueryParam();
function getAlertTypeFromDataSource(): AlertTypes | null {
if (!compositeQuery) {
return null;
}
const dataSource = compositeQuery?.builder?.queryData[0]?.dataSource;
return ALERT_TYPE_VS_SOURCE_MAPPING[dataSource];
}
const [alertType, setAlertType] = useState<AlertTypes>(
(alertTypeFromParams as AlertTypes) || getAlertTypeFromDataSource(),
);
const [formInstance] = Form.useForm();
@@ -48,21 +60,17 @@ function CreateRules(): JSX.Element {
version: version || ENTITY_VERSION_V4,
});
}
queryParams.set(QueryParams.alertType, typ);
const generatedUrl = `${location.pathname}?${queryParams.toString()}`;
history.replace(generatedUrl);
};
useEffect(() => {
if (!compositeQuery) {
return;
}
const dataSource = compositeQuery?.builder?.queryData[0]?.dataSource;
const alertType = ALERT_TYPE_VS_SOURCE_MAPPING[dataSource];
if (alertType) {
onSelectType(alertType);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [compositeQuery]);
}, [alertType]);
if (!initValues) {
return (

View File

@@ -64,6 +64,10 @@
.view-options,
.actions {
.info-icon {
padding: 8px;
}
.hidden {
display: none;
}
@@ -252,6 +256,10 @@
color: var(--bg-ink-200);
background-color: var(--bg-vanilla-300);
}
.info-icon {
color: var(--bg-ink-200);
}
}
}

View File

@@ -1,6 +1,7 @@
/* eslint-disable react/jsx-props-no-spreading */
import './ExplorerOptions.styles.scss';
import { InfoCircleOutlined } from '@ant-design/icons';
import { Color } from '@signozhq/design-tokens';
import {
Button,
@@ -402,6 +403,28 @@ function ExplorerOptions({
</Button>
</div>
<div className="actions">
<Tooltip
title={
<div>
{sourcepage === DataSource.LOGS
? 'Learn more about Logs explorer '
: 'Learn more about Traces explorer '}
<Typography.Link
href={
sourcepage === DataSource.LOGS
? 'https://signoz.io/docs/product-features/logs-explorer/?utm_source=product&utm_medium=logs-explorer-toolbar'
: 'https://signoz.io/docs/product-features/trace-explorer/?utm_source=product&utm_medium=trace-explorer-toolbar'
}
target="_blank"
>
{' '}
here
</Typography.Link>{' '}
</div>
}
>
<InfoCircleOutlined className="info-icon" />
</Tooltip>
<Tooltip title="Hide">
<Button
disabled={disabled}

View File

@@ -252,11 +252,11 @@ function ChartPreview({
{queryResponse.error.message || t('preview_chart_unexpected_error')}
</FailedMessageContainer>
)}
{queryResponse.isLoading && (
<Spinner size="large" tip="Loading..." height="70vh" />
)}
{chartData && !queryResponse.isError && (
<div ref={graphRef} style={{ height: '100%' }}>
{queryResponse.isLoading && (
<Spinner size="large" tip="Loading..." height="100%" />
)}
<GridPanelSwitch
options={options}
panelType={graphType}

View File

@@ -22,6 +22,18 @@ export const ChartContainer = styled(Card)`
position: relative;
}
.plot-tag {
margin-left: 6px;
display: inline-flex;
padding: 0px 4px 0px 6px;
align-items: center;
gap: 6px;
border-radius: 4px;
background: var(--bg-slate-400);
backdrop-filter: blur(6px);
width: fit-content;
}
.ant-card-body {
padding: 1.5rem 0;
height: 57vh;

View File

@@ -1,45 +1,50 @@
.create-alert-modal {
.ant-modal-content {
background-color: var(--bg-ink-300);
.ant-modal-confirm-title {
color: var(--bg-vanilla-100);
}
.ant-modal-content {
background-color: var(--bg-ink-300);
.ant-modal-confirm-title {
color: var(--bg-vanilla-100);
}
.ant-modal-confirm-content {
.ant-typography {
color: var(--bg-vanilla-100);
}
}
.ant-modal-confirm-content {
.ant-typography {
color: var(--bg-vanilla-100);
}
}
.ant-modal-confirm-btns {
button:nth-of-type(1) {
background-color: var(--bg-slate-400);
border: none;
color: var(--bg-vanilla-100);
}
}
}
.ant-modal-confirm-btns {
button:nth-of-type(1) {
background-color: var(--bg-slate-400);
border: none;
color: var(--bg-vanilla-100);
}
}
}
}
.lightMode {
.ant-modal-content {
background-color: var(--bg-vanilla-100);
.ant-modal-confirm-title {
color: var(--bg-ink-500);
}
.ant-modal-content {
background-color: var(--bg-vanilla-100);
.ant-modal-confirm-title {
color: var(--bg-ink-500);
}
.ant-modal-confirm-content {
.ant-typography {
color: var(--bg-ink-500);
}
}
.ant-modal-confirm-content {
.ant-typography {
color: var(--bg-ink-500);
}
}
.ant-modal-confirm-btns {
button:nth-of-type(1) {
background-color: var(--bg-vanilla-300);
border: none;
color: var(--bg-ink-500);
}
}
}
}
.ant-modal-confirm-btns {
button:nth-of-type(1) {
background-color: var(--bg-vanilla-300);
border: none;
color: var(--bg-ink-500);
}
}
}
}
.facing-issue-btn {
margin-top: 20px;
width: 100%;
}

View File

@@ -1,12 +1,15 @@
import './QuerySection.styles.scss';
import { Color } from '@signozhq/design-tokens';
import { Button, Tabs, Tooltip } from 'antd';
import PromQLIcon from 'assets/Dashboard/PromQl';
import { ALERTS_DATA_SOURCE_MAP } from 'constants/alerts';
import { ENTITY_VERSION_V4 } from 'constants/app';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { QBShortcuts } from 'constants/shortcuts/QBShortcuts';
import { QueryBuilder } from 'container/QueryBuilder';
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { Atom, Play, Terminal } from 'lucide-react';
import { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -48,6 +51,8 @@ function QuerySection({
const renderChQueryUI = (): JSX.Element => <ChQuerySection />;
const isDarkMode = useIsDarkMode();
const renderMetricUI = (): JSX.Element => (
<QueryBuilder
panelType={panelType}
@@ -113,14 +118,16 @@ function QuerySection({
label: (
<Tooltip title="PromQL">
<Button className="nav-btns">
<img src="/Icons/promQL.svg" alt="Prom Ql" className="prom-ql-icon" />
<PromQLIcon
fillColor={isDarkMode ? Color.BG_VANILLA_200 : Color.BG_INK_300}
/>
</Button>
</Tooltip>
),
key: EQueryType.PROM,
},
],
[],
[isDarkMode],
);
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();

View File

@@ -147,7 +147,7 @@ function UserGuide({ queryType }: UserGuideProps): JSX.Element {
<Col flex="none">
<TextToolTip
text={t('user_tooltip_more_help')}
url="https://signoz.io/docs/userguide/alerts-management/#create-alert-rules"
url="https://signoz.io/docs/userguide/alerts-management/?utm_source=product&utm_medium=create-alert#creating-a-new-alert-in-signoz"
/>
</Col>
</Row>

View File

@@ -11,6 +11,8 @@ import {
} from 'antd';
import saveAlertApi from 'api/alerts/save';
import testAlertApi from 'api/alerts/testAlert';
import FacingIssueBtn from 'components/facingIssueBtn/FacingIssueBtn';
import { alertHelpMessage } from 'components/facingIssueBtn/util';
import { FeatureKeys } from 'constants/features';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
@@ -138,19 +140,29 @@ function FormAlertRules({
useEffect(() => {
// Set selectedQueryName based on the length of queryOptions
setAlertDef((def) => ({
...def,
condition: {
...def.condition,
selectedQueryName:
queryOptions.length > 0 ? String(queryOptions[0].value) : undefined,
},
}));
}, [currentQuery?.queryType, queryOptions]);
const selectedQueryName = alertDef?.condition?.selectedQueryName;
if (
!selectedQueryName ||
!queryOptions.some((option) => option.value === selectedQueryName)
) {
setAlertDef((def) => ({
...def,
condition: {
...def.condition,
selectedQueryName:
queryOptions.length > 0 ? String(queryOptions[0].value) : undefined,
},
}));
}
}, [alertDef, currentQuery?.queryType, queryOptions]);
const onCancelHandler = useCallback(() => {
history.replace(ROUTES.LIST_ALL_ALERT);
}, []);
urlQuery.delete(QueryParams.compositeQuery);
urlQuery.delete(QueryParams.panelTypes);
urlQuery.delete(QueryParams.ruleId);
urlQuery.delete(QueryParams.relativeTime);
history.replace(`${ROUTES.LIST_ALL_ALERT}?${urlQuery.toString()}`);
}, [urlQuery]);
// onQueryCategoryChange handles changes to query category
// in state as well as sets additional defaults
@@ -335,8 +347,13 @@ function FormAlertRules({
// invalidate rule in cache
ruleCache.invalidateQueries(['ruleId', ruleId]);
// eslint-disable-next-line sonarjs/no-identical-functions
setTimeout(() => {
history.replace(ROUTES.LIST_ALL_ALERT);
urlQuery.delete(QueryParams.compositeQuery);
urlQuery.delete(QueryParams.panelTypes);
urlQuery.delete(QueryParams.ruleId);
urlQuery.delete(QueryParams.relativeTime);
history.replace(`${ROUTES.LIST_ALL_ALERT}?${urlQuery.toString()}`);
}, 2000);
} else {
notifications.error({
@@ -352,12 +369,13 @@ function FormAlertRules({
}
setLoading(false);
}, [
t,
isFormValid,
ruleId,
ruleCache,
memoizedPreparePostData,
ruleId,
notifications,
t,
ruleCache,
urlQuery,
]);
const onSaveHandler = useCallback(async () => {
@@ -482,6 +500,8 @@ function FormAlertRules({
alertDef?.broadcastToAll ||
(alertDef.preferredChannels && alertDef.preferredChannels.length > 0);
const isRuleCreated = !ruleId || ruleId === 0;
return (
<>
{Element}
@@ -514,6 +534,7 @@ function FormAlertRules({
runQuery={handleRunQuery}
alertDef={alertDef}
panelType={panelType || PANEL_TYPES.TIME_SERIES}
key={currentQuery.queryType}
/>
<RuleOptions
@@ -563,6 +584,22 @@ function FormAlertRules({
</StyledLeftContainer>
<Col flex="1 1 300px">
<UserGuide queryType={currentQuery.queryType} />
<FacingIssueBtn
attributes={{
alert: alertDef?.alert,
alertType: alertDef?.alertType,
id: ruleId,
ruleType: alertDef?.ruleType,
state: (alertDef as any)?.state,
panelType,
screen: isRuleCreated ? 'Edit Alert' : 'New Alert',
}}
className="facing-issue-btn"
eventName="Alert: Facing Issues in alert"
buttonText="Need help with this alert?"
message={alertHelpMessage(alertDef, ruleId)}
onHoverText="Click here to get help with this alert"
/>
</Col>
</PanelContainer>
</>

View File

@@ -0,0 +1,244 @@
.dashboard-empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 500px;
.dashboard-content {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 12px;
.heading {
display: flex;
flex-direction: column;
gap: 4px;
.icons {
color: white;
}
.welcome {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 24px; /* 171.429% */
letter-spacing: -0.07px;
}
.welcome-info {
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 24px;
letter-spacing: -0.07px;
}
}
.actions {
display: flex;
flex-direction: column;
.actions-1 {
display: flex;
width: 560px;
padding: 12px;
justify-content: space-between;
align-items: flex-start;
border-radius: 4px 4px 0px 0px;
border: 1px solid var(--bg-slate-500);
background: var(--bg-ink-400);
.configure-button {
display: flex;
width: 113px;
height: 32px;
padding: 6px;
justify-content: center;
align-items: center;
flex-shrink: 0;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 10px; /* 83.333% */
letter-spacing: 0.12px;
}
.actions-configure {
display: flex;
flex-direction: column;
gap: 6px;
.actions-configure-text {
display: flex;
gap: 8px;
align-items: center;
.icons {
color: white;
}
.configure {
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: -0.07px;
}
}
.configure-info {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 166.667% */
letter-spacing: -0.06px;
padding-left: 22px;
}
}
.add-panel-btn {
display: flex;
width: 113px;
height: 32px;
padding: 6px;
justify-content: center;
align-items: center;
flex-shrink: 0;
border-radius: 2px;
border: 1px solid var(--bg-slate-400);
background: var(--bg-ink-300);
box-shadow: none;
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 10px; /* 83.333% */
letter-spacing: 0.12px;
}
.actions-add-panel {
display: flex;
flex-direction: column;
gap: 6px;
.actions-panel-text {
display: flex;
gap: 8px;
align-items: center;
.icons {
color: white;
}
.panel {
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: -0.07px;
}
}
.panel-info {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 166.667% */
letter-spacing: -0.06px;
padding-left: 22px;
}
}
}
}
}
}
.lightMode {
.dashboard-empty-state {
.dashboard-content {
.heading {
.icons {
color: var(--bg-ink-300);
}
.welcome {
color: var(--bg-ink-400);
}
.welcome-info {
color: var(--bg-ink-400);
}
}
.actions {
.actions-1 {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-100);
.configure-button {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
color: var(--bg-ink-400);
}
.actions-configure {
.actions-configure-text {
.icons {
color: var(--bg-ink-400);
}
.configure {
color: var(--bg-ink-400);
}
}
.configure-info {
color: var(--bg-ink-400);
}
}
.add-panel-btn {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-300);
color: var(--bg-ink-400);
}
.actions-add-panel {
.actions-panel-text {
.icons {
color: var(--bg-ink-300);
}
.panel {
color: var(--bg-ink-300);
}
}
.panel-info {
color: var(--bg-ink-300);
}
}
}
}
}
}
}

View File

@@ -0,0 +1,105 @@
/* eslint-disable jsx-a11y/img-redundant-alt */
import './DashboardEmptyState.styles.scss';
import { PlusOutlined } from '@ant-design/icons';
import { Button, Typography } from 'antd';
import SettingsDrawer from 'container/NewDashboard/DashboardDescription/SettingsDrawer';
import useComponentPermission from 'hooks/useComponentPermission';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useCallback } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import AppReducer from 'types/reducer/app';
import { ROLES, USER_ROLES } from 'types/roles';
import { ComponentTypes } from 'utils/permission';
export default function DashboardEmptyState(): JSX.Element {
const {
selectedDashboard,
isDashboardLocked,
handleToggleDashboardSlider,
} = useDashboard();
const { user, role } = useSelector<AppState, AppReducer>((state) => state.app);
let permissions: ComponentTypes[] = ['add_panel'];
if (isDashboardLocked) {
permissions = ['add_panel_locked_dashboard'];
}
const userRole: ROLES | null =
selectedDashboard?.created_by === user?.email
? (USER_ROLES.AUTHOR as ROLES)
: role;
const [addPanelPermission] = useComponentPermission(permissions, userRole);
const onEmptyWidgetHandler = useCallback(() => {
handleToggleDashboardSlider(true);
}, [handleToggleDashboardSlider]);
return (
<section className="dashboard-empty-state">
<div className="dashboard-content">
<section className="heading">
<img
src="/Icons/dashboard_emoji.svg"
alt="header-image"
style={{ height: '32px', width: '32px' }}
/>
<Typography.Text className="welcome">
Welcome to your new dashboard
</Typography.Text>
<Typography.Text className="welcome-info">
Follow the steps to populate it with data and share with your teammates
</Typography.Text>
</section>
<section className="actions">
<div className="actions-1">
<div className="actions-configure">
<div className="actions-configure-text">
<img
src="/Icons/tools.svg"
alt="header-image"
style={{ height: '14px', width: '14px' }}
/>
<Typography.Text className="configure">
Configure your new dashboard
</Typography.Text>
</div>
<Typography.Text className="configure-info">
Give it a name, add description, tags and variables
</Typography.Text>
</div>
<SettingsDrawer drawerTitle="Dashboard Configuration" />
</div>
<div className="actions-1">
<div className="actions-add-panel">
<div className="actions-panel-text">
<img
src="/Icons/landscape.svg"
alt="header-image"
style={{ height: '14px', width: '14px' }}
/>
<Typography.Text className="panel">Add panels</Typography.Text>
</div>
<Typography.Text className="panel-info">
Add panels to visualize your data
</Typography.Text>
</div>
{!isDashboardLocked && addPanelPermission && (
<Button
className="add-panel-btn"
onClick={onEmptyWidgetHandler}
icon={<PlusOutlined />}
type="primary"
data-testid="add-panel"
>
New Panel
</Button>
)}
</div>
</section>
</div>
</section>
);
}

View File

@@ -42,6 +42,7 @@ function FullView({
fullViewOptions = true,
version,
originalName,
tableProcessedDataRef,
isDependedDataLoaded = false,
onToggleModelHandler,
}: FullViewProps): JSX.Element {
@@ -222,6 +223,7 @@ function FullView({
setGraphVisibility={setGraphsVisibilityStates}
graphVisibility={graphsVisibilityStates}
onDragSelect={onDragSelect}
tableProcessedDataRef={tableProcessedDataRef}
/>
</GraphContainer>
</div>

View File

@@ -2,6 +2,7 @@ import { CheckboxChangeEvent } from 'antd/es/checkbox';
import { ToggleGraphProps } from 'components/Graph/types';
import { UplotProps } from 'components/Uplot/Uplot';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
import { Dispatch, MutableRefObject, SetStateAction } from 'react';
import { Widgets } from 'types/api/dashboard/getAll';
@@ -50,6 +51,7 @@ export interface FullViewProps {
fullViewOptions?: boolean;
onClickHandler?: OnClickPluginOpts['onClick'];
name: string;
tableProcessedDataRef: MutableRefObject<RowData[]>;
version?: string;
originalName: string;
yAxisUnit?: string;

View File

@@ -12,6 +12,7 @@ import { useNotifications } from 'hooks/useNotifications';
import useUrlQuery from 'hooks/useUrlQuery';
import createQueryParams from 'lib/createQueryParams';
import history from 'lib/history';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import {
Dispatch,
@@ -33,7 +34,6 @@ import FullView from './FullView';
import { Modal } from './styles';
import { WidgetGraphComponentProps } from './types';
import { getLocalStorageGraphVisibilityState } from './utils';
// import { getLocalStorageGraphVisibilityState } from './utils';
function WidgetGraphComponent({
widget,
@@ -59,7 +59,7 @@ function WidgetGraphComponent({
const lineChartRef = useRef<ToggleGraphProps>();
const [graphVisibility, setGraphVisibility] = useState<boolean[]>(
Array(queryResponse.data?.payload?.data.result.length || 0).fill(true),
Array(queryResponse.data?.payload?.data?.result?.length || 0).fill(true),
);
const graphRef = useRef<HTMLDivElement>(null);
@@ -72,6 +72,8 @@ function WidgetGraphComponent({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const tableProcessedDataRef = useRef<RowData[]>([]);
const { setLayouts, selectedDashboard, setSelectedDashboard } = useDashboard();
const featureResponse = useSelector<AppState, AppReducer['featureResponse']>(
@@ -135,7 +137,7 @@ function WidgetGraphComponent({
i: uuid,
w: 6,
x: 0,
h: 3,
h: 6,
y: 0,
},
];
@@ -158,7 +160,11 @@ function WidgetGraphComponent({
},
},
{
onSuccess: () => {
onSuccess: (updatedDashboard) => {
if (setLayouts) setLayouts(updatedDashboard.payload?.data?.layout || []);
if (setSelectedDashboard && updatedDashboard.payload) {
setSelectedDashboard(updatedDashboard.payload);
}
notifications.success({
message: 'Panel cloned successfully, redirecting to new copy.',
});
@@ -284,6 +290,7 @@ function WidgetGraphComponent({
widget={widget}
yAxisUnit={widget.yAxisUnit}
onToggleModelHandler={onToggleModelHandler}
tableProcessedDataRef={tableProcessedDataRef}
/>
</Modal>
@@ -301,6 +308,7 @@ function WidgetGraphComponent({
headerMenuList={headerMenuList}
isWarning={isWarning}
isFetchingResponse={isFetchingResponse}
tableProcessedDataRef={tableProcessedDataRef}
/>
</div>
{queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && (
@@ -319,6 +327,7 @@ function WidgetGraphComponent({
graphVisibility={graphVisibility}
onClickHandler={onClickHandler}
onDragSelect={onDragSelect}
tableProcessedDataRef={tableProcessedDataRef}
/>
</div>
)}

View File

@@ -8,6 +8,7 @@ import { useIntersectionObserver } from 'hooks/useIntersectionObserver';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import getTimeString from 'lib/getTimeString';
import { isEqual } from 'lodash-es';
import isEmpty from 'lodash-es/isEmpty';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { memo, useEffect, useRef, useState } from 'react';
@@ -125,6 +126,16 @@ function GridCardGraph({
};
});
useEffect(() => {
if (!isEqual(updatedQuery, requestData.query)) {
setRequestData((prev) => ({
...prev,
query: updatedQuery,
}));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [updatedQuery]);
const queryResponse = useGetQueryRange(
{
...requestData,

View File

@@ -6,12 +6,85 @@
border: none !important;
margin-top: 0;
.row-panel {
border-radius: 4px;
background: rgba(18, 19, 23, 0.4);
padding: 8px;
display: flex;
gap: 6px;
align-items: center;
.settings-icon {
color: var(--bg-vanilla-400);
cursor: pointer;
}
.row-icon {
color: var(--bg-vanilla-400);
cursor: pointer;
}
.grip {
cursor: move;
}
.section-title {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
letter-spacing: -0.07px;
}
}
.widget-graph-container {
&.graph {
height: 100%;
}
}
}
.footer {
display: flex;
flex-direction: column;
position: absolute;
bottom: 0;
width: -webkit-fill-available;
.locked-text {
align-self: flex-end;
width: 80px;
border: none;
cursor: default;
display: inline-flex;
padding: 4px 6px;
align-items: center;
gap: 4px;
border-radius: 4px 0px 0px 0px;
background: var(--bg-sakura-500);
backdrop-filter: blur(6px);
color: var(--bg-ink-500);
font-family: 'Space Mono';
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 16px; /* 133.333% */
letter-spacing: 0.48px;
text-transform: uppercase;
.ant-btn-icon {
margin-inline-end: 0px;
}
}
.locked-bar {
background: var(--bg-sakura-500);
height: 6px;
width: 100%;
}
}
}
.widget-graph-container {
@@ -32,18 +105,257 @@
}
}
.row-settings {
.ant-popover-inner {
width: 191px;
flex-shrink: 0;
border-radius: 4px;
border: 1px solid var(--bg-slate-400);
background: linear-gradient(
139deg,
rgba(18, 19, 23, 0.8) 0%,
rgba(18, 19, 23, 0.9) 98.68%
);
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(20px);
padding: 0px;
.menu-content {
.section-1 {
.rename-btn {
display: flex;
align-items: center;
gap: 6px;
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
letter-spacing: 0.14px;
padding: 14px;
width: 100%;
.ant-btn-icon {
margin-inline-end: 0px;
}
}
}
.section-2 {
border-top: 1px solid #1d212d;
.remove-section {
display: flex;
align-items: center;
width: 100%;
gap: 6px;
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
letter-spacing: 0.14px;
padding: 10px 18px 12px 14px;
color: var(--bg-cherry-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: normal;
letter-spacing: 0.14px;
.ant-btn-icon {
margin-inline-end: 0px;
}
}
}
}
}
}
.rename-section {
.ant-modal-content {
width: 384px;
height: auto;
flex-shrink: 0;
border-radius: 4px;
border: 1px solid var(--bg-slate-500);
background: var(--Ink-400, #121317);
box-shadow: 0px -4px 16px 2px rgba(0, 0, 0, 0.2);
padding: 0px;
.ant-modal-header {
padding: 16px;
background: var(--bg-ink-400);
border-bottom: 1px solid var(--bg-slate-500);
margin-bottom: 0px;
.ant-modal-title {
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px; /* 142.857% */
}
}
.ant-modal-body {
padding: 12px 16px 16px 16px;
.typography {
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 20px; /* 142.857% */
}
.ant-form-item {
margin-bottom: 0px;
.ant-input {
margin-top: 8px;
margin-bottom: 24px;
}
.action-btns {
display: flex;
align-items: center;
flex-direction: row-reverse;
gap: 12px;
.ok-btn {
display: flex;
align-items: center;
color: var(--bg-vanilla-100);
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 24px; /* 200% */
display: flex;
width: 140px;
padding: 4px 8px;
justify-content: center;
align-items: center;
gap: 4px;
border-radius: 2px;
background: var(--bg-robin-500);
.ant-btn-icon {
margin-inline-end: 0px;
}
}
.cancel-btn {
display: flex;
align-items: center;
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 24px; /* 200% */
display: flex;
padding: 4px 8px;
justify-content: center;
align-items: center;
gap: 4px;
border-radius: 2px;
background: var(--bg-slate-500);
.ant-btn-icon {
margin-inline-end: 0px;
}
}
}
}
}
}
}
.lightMode {
.fullscreen-grid-container {
background-color: rgb(250, 250, 250);
.react-grid-layout {
.row-panel {
background: var(--bg-vanilla-200);
.settings-icon {
color: var(--bg-ink-400);
}
.row-icon {
color: var(--bg-ink-400);
}
.section-title {
color: var(--bg-ink-400);
}
}
}
}
.widget-full-view {
.ant-modal-content {
background-color: var(--bg-vanilla-100);
}
.ant-modal-header {
background-color: var(--bg-vanilla-100);
.ant-modal-header {
background-color: var(--bg-vanilla-100);
}
}
}
.row-settings {
.ant-popover-inner {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-100);
.menu-content {
.section-1 {
.rename-btn {
color: var(--bg-ink-400);
}
}
.section-2 {
border-top: 1px solid var(--bg-vanilla-300);
.remove-section {
color: var(--bg-cherry-400);
}
}
}
}
}
.rename-section {
.ant-modal-content {
border: 1px solid var(--bg-vanilla-300);
background: var(--bg-vanilla-100);
.ant-modal-header {
background: var(--bg-vanilla-100);
border-bottom: 1px solid var(--bg-vanilla-300);
.ant-modal-title {
color: var(--bg-ink-300);
}
}
.ant-modal-body {
.typography {
color: var(--bg-ink-100);
}
.ant-form-item {
.action-btns {
.cancel-btn {
color: var(--bg-ink-300);
background: var(--bg-vanilla-300);
}
}
}
}
}
}
}

View File

@@ -1,24 +1,35 @@
import './GridCardLayout.styles.scss';
import { PlusOutlined } from '@ant-design/icons';
import { Tooltip } from 'antd';
import { Color } from '@signozhq/design-tokens';
import { Button, Form, Input, Modal, Typography } from 'antd';
import { useForm } from 'antd/es/form/Form';
import cx from 'classnames';
import { SOMETHING_WENT_WRONG } from 'constants/api';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
import { themeColors } from 'constants/theme';
import { DEFAULT_ROW_NAME } from 'container/NewDashboard/DashboardDescription/utils';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import useComponentPermission from 'hooks/useComponentPermission';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useNotifications } from 'hooks/useNotifications';
import useUrlQuery from 'hooks/useUrlQuery';
import history from 'lib/history';
import { defaultTo } from 'lodash-es';
import isEqual from 'lodash-es/isEqual';
import { FullscreenIcon } from 'lucide-react';
import {
Check,
ChevronDown,
ChevronUp,
GripVertical,
LockKeyhole,
X,
} from 'lucide-react';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useCallback, useEffect, useState } from 'react';
import { FullScreen, useFullScreenHandle } from 'react-full-screen';
import { Layout } from 'react-grid-layout';
import { useTranslation } from 'react-i18next';
import { sortLayout } from 'providers/Dashboard/util';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { FullScreen, FullScreenHandle } from 'react-full-screen';
import { ItemCallback, Layout } from 'react-grid-layout';
import { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { UpdateTimeInterval } from 'store/actions';
@@ -29,34 +40,34 @@ import { ROLES, USER_ROLES } from 'types/roles';
import { ComponentTypes } from 'utils/permission';
import { EditMenuAction, ViewMenuAction } from './config';
import DashboardEmptyState from './DashboardEmptyState/DashboardEmptyState';
import GridCard from './GridCard';
import {
Button,
ButtonContainer,
Card,
CardContainer,
ReactGridLayout,
} from './styles';
import { GraphLayoutProps } from './types';
import { Card, CardContainer, ReactGridLayout } from './styles';
import { removeUndefinedValuesFromLayout } from './utils';
import { WidgetRowHeader } from './WidgetRow';
function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
interface GraphLayoutProps {
handle: FullScreenHandle;
}
// eslint-disable-next-line sonarjs/cognitive-complexity
function GraphLayout(props: GraphLayoutProps): JSX.Element {
const { handle } = props;
const {
selectedDashboard,
layouts,
setLayouts,
panelMap,
setPanelMap,
setSelectedDashboard,
isDashboardLocked,
} = useDashboard();
const { data } = selectedDashboard || {};
const handle = useFullScreenHandle();
const { pathname } = useLocation();
const dispatch = useDispatch();
const { widgets, variables } = data || {};
const { t } = useTranslation(['dashboard']);
const { featureResponse, role, user } = useSelector<AppState, AppReducer>(
(state) => state.app,
);
@@ -65,6 +76,26 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
const [dashboardLayout, setDashboardLayout] = useState<Layout[]>([]);
const [isSettingsModalOpen, setIsSettingsModalOpen] = useState<boolean>(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState<boolean>(false);
const [currentSelectRowId, setCurrentSelectRowId] = useState<string | null>(
null,
);
const [currentPanelMap, setCurrentPanelMap] = useState<
Record<string, { widgets: Layout[]; collapsed: boolean }>
>({});
useEffect(() => {
setCurrentPanelMap(panelMap);
}, [panelMap]);
const [form] = useForm<{
title: string;
}>();
const updateDashboardMutation = useUpdateDashboard();
const { notifications } = useNotifications();
@@ -86,8 +117,13 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
userRole,
);
const [deleteWidget, editWidget] = useComponentPermission(
['delete_widget', 'edit_widget'],
role,
);
useEffect(() => {
setDashboardLayout(layouts);
setDashboardLayout(sortLayout(layouts));
}, [layouts]);
const onSaveHandler = (): void => {
@@ -97,6 +133,7 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
...selectedDashboard,
data: {
...selectedDashboard.data,
panelMap: { ...currentPanelMap },
layout: dashboardLayout.filter((e) => e.i !== PANEL_TYPES.EMPTY_WIDGET),
},
uuid: selectedDashboard.uuid,
@@ -106,8 +143,9 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
onSuccess: (updatedDashboard) => {
if (updatedDashboard.payload) {
if (updatedDashboard.payload.data.layout)
setLayouts(updatedDashboard.payload.data.layout);
setLayouts(sortLayout(updatedDashboard.payload.data.layout));
setSelectedDashboard(updatedDashboard.payload);
setPanelMap(updatedDashboard.payload?.data?.panelMap || {});
}
featureResponse.refetch();
@@ -130,7 +168,8 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
dashboardLayout,
);
if (!isEqual(filterLayout, filterDashboardLayout)) {
setDashboardLayout(layout);
const updatedLayout = sortLayout(layout);
setDashboardLayout(updatedLayout);
}
};
@@ -167,76 +206,399 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dashboardLayout]);
return (
<>
<ButtonContainer>
<Tooltip title="Open in Full Screen">
<Button
className="periscope-btn"
loading={updateDashboardMutation.isLoading}
onClick={handle.enter}
icon={<FullscreenIcon size={16} />}
disabled={updateDashboardMutation.isLoading}
/>
</Tooltip>
const onSettingsModalSubmit = (): void => {
const newTitle = form.getFieldValue('title');
if (!selectedDashboard) return;
{!isDashboardLocked && addPanelPermission && (
<Button
className="periscope-btn"
onClick={onAddPanelHandler}
icon={<PlusOutlined />}
data-testid="add-panel"
>
{t('dashboard:add_panel')}
</Button>
)}
</ButtonContainer>
if (!currentSelectRowId) return;
<FullScreen handle={handle} className="fullscreen-grid-container">
<ReactGridLayout
cols={12}
rowHeight={100}
autoSize
width={100}
useCSSTransforms
isDraggable={!isDashboardLocked && addPanelPermission}
isDroppable={!isDashboardLocked && addPanelPermission}
isResizable={!isDashboardLocked && addPanelPermission}
allowOverlap={false}
onLayoutChange={handleLayoutChange}
draggableHandle=".drag-handle"
layout={dashboardLayout}
style={{ backgroundColor: isDarkMode ? '' : themeColors.snowWhite }}
>
{dashboardLayout.map((layout) => {
const { i: id } = layout;
const currentWidget = (widgets || [])?.find((e) => e.id === id);
const currentWidget = selectedDashboard?.data?.widgets?.find(
(e) => e.id === currentSelectRowId,
);
if (!currentWidget) return;
currentWidget.title = newTitle;
const updatedWidgets = selectedDashboard?.data?.widgets?.filter(
(e) => e.id !== currentSelectRowId,
);
updatedWidgets?.push(currentWidget);
const updatedSelectedDashboard: Dashboard = {
...selectedDashboard,
data: {
...selectedDashboard.data,
widgets: updatedWidgets,
},
uuid: selectedDashboard.uuid,
};
updateDashboardMutation.mutateAsync(updatedSelectedDashboard, {
onSuccess: (updatedDashboard) => {
if (setLayouts) setLayouts(updatedDashboard.payload?.data?.layout || []);
if (setSelectedDashboard && updatedDashboard.payload) {
setSelectedDashboard(updatedDashboard.payload);
}
if (setPanelMap)
setPanelMap(updatedDashboard.payload?.data?.panelMap || {});
form.setFieldValue('title', '');
setIsSettingsModalOpen(false);
setCurrentSelectRowId(null);
featureResponse.refetch();
},
// eslint-disable-next-line sonarjs/no-identical-functions
onError: () => {
notifications.error({
message: SOMETHING_WENT_WRONG,
});
},
});
};
useEffect(() => {
if (!currentSelectRowId) return;
form.setFieldValue(
'title',
(widgets?.find((widget) => widget.id === currentSelectRowId)
?.title as string) || DEFAULT_ROW_NAME,
);
}, [currentSelectRowId, form, widgets]);
// eslint-disable-next-line sonarjs/cognitive-complexity
const handleRowCollapse = (id: string): void => {
if (!selectedDashboard) return;
const rowProperties = { ...currentPanelMap[id] };
const updatedPanelMap = { ...currentPanelMap };
let updatedDashboardLayout = [...dashboardLayout];
if (rowProperties.collapsed === true) {
rowProperties.collapsed = false;
const widgetsInsideTheRow = rowProperties.widgets;
let maxY = 0;
widgetsInsideTheRow.forEach((w) => {
maxY = Math.max(maxY, w.y + w.h);
});
const currentRowWidget = dashboardLayout.find((w) => w.i === id);
if (currentRowWidget && widgetsInsideTheRow.length) {
maxY -= currentRowWidget.h + currentRowWidget.y;
}
const idxCurrentRow = dashboardLayout.findIndex((w) => w.i === id);
for (let j = idxCurrentRow + 1; j < dashboardLayout.length; j++) {
updatedDashboardLayout[j].y += maxY;
if (updatedPanelMap[updatedDashboardLayout[j].i]) {
updatedPanelMap[updatedDashboardLayout[j].i].widgets = updatedPanelMap[
updatedDashboardLayout[j].i
// eslint-disable-next-line @typescript-eslint/no-loop-func
].widgets.map((w) => ({
...w,
y: w.y + maxY,
}));
}
}
updatedDashboardLayout = [...updatedDashboardLayout, ...widgetsInsideTheRow];
} else {
rowProperties.collapsed = true;
const currentIdx = dashboardLayout.findIndex((w) => w.i === id);
let widgetsInsideTheRow: Layout[] = [];
let isPanelMapUpdated = false;
for (let j = currentIdx + 1; j < dashboardLayout.length; j++) {
if (currentPanelMap[dashboardLayout[j].i]) {
rowProperties.widgets = widgetsInsideTheRow;
widgetsInsideTheRow = [];
isPanelMapUpdated = true;
break;
} else {
widgetsInsideTheRow.push(dashboardLayout[j]);
}
}
if (!isPanelMapUpdated) {
rowProperties.widgets = widgetsInsideTheRow;
}
let maxY = 0;
widgetsInsideTheRow.forEach((w) => {
maxY = Math.max(maxY, w.y + w.h);
});
const currentRowWidget = dashboardLayout[currentIdx];
if (currentRowWidget && widgetsInsideTheRow.length) {
maxY -= currentRowWidget.h + currentRowWidget.y;
}
for (let j = currentIdx + 1; j < updatedDashboardLayout.length; j++) {
updatedDashboardLayout[j].y += maxY;
if (updatedPanelMap[updatedDashboardLayout[j].i]) {
updatedPanelMap[updatedDashboardLayout[j].i].widgets = updatedPanelMap[
updatedDashboardLayout[j].i
// eslint-disable-next-line @typescript-eslint/no-loop-func
].widgets.map((w) => ({
...w,
y: w.y + maxY,
}));
}
}
updatedDashboardLayout = updatedDashboardLayout.filter(
(widget) => !rowProperties.widgets.some((w: Layout) => w.i === widget.i),
);
}
setCurrentPanelMap((prev) => ({
...prev,
...updatedPanelMap,
[id]: {
...rowProperties,
},
}));
setDashboardLayout(sortLayout(updatedDashboardLayout));
};
const handleDragStop: ItemCallback = (_, oldItem, newItem): void => {
if (currentPanelMap[oldItem.i]) {
const differenceY = newItem.y - oldItem.y;
const widgetsInsideRow = currentPanelMap[oldItem.i].widgets.map((w) => ({
...w,
y: w.y + differenceY,
}));
setCurrentPanelMap((prev) => ({
...prev,
[oldItem.i]: {
...prev[oldItem.i],
widgets: widgetsInsideRow,
},
}));
}
};
const handleRowDelete = (): void => {
if (!selectedDashboard) return;
if (!currentSelectRowId) return;
const updatedWidgets = selectedDashboard?.data?.widgets?.filter(
(e) => e.id !== currentSelectRowId,
);
const updatedLayout =
selectedDashboard.data.layout?.filter((e) => e.i !== currentSelectRowId) ||
[];
const updatedPanelMap = { ...currentPanelMap };
delete updatedPanelMap[currentSelectRowId];
const updatedSelectedDashboard: Dashboard = {
...selectedDashboard,
data: {
...selectedDashboard.data,
widgets: updatedWidgets,
layout: updatedLayout,
panelMap: updatedPanelMap,
},
uuid: selectedDashboard.uuid,
};
updateDashboardMutation.mutateAsync(updatedSelectedDashboard, {
onSuccess: (updatedDashboard) => {
if (setLayouts) setLayouts(updatedDashboard.payload?.data?.layout || []);
if (setSelectedDashboard && updatedDashboard.payload) {
setSelectedDashboard(updatedDashboard.payload);
}
if (setPanelMap)
setPanelMap(updatedDashboard.payload?.data?.panelMap || {});
setIsDeleteModalOpen(false);
setCurrentSelectRowId(null);
featureResponse.refetch();
},
// eslint-disable-next-line sonarjs/no-identical-functions
onError: () => {
notifications.error({
message: SOMETHING_WENT_WRONG,
});
},
});
};
const isDashboardEmpty = useMemo(
() =>
selectedDashboard?.data.layout
? selectedDashboard?.data.layout?.length === 0
: true,
[selectedDashboard],
);
return isDashboardEmpty ? (
<DashboardEmptyState />
) : (
<FullScreen handle={handle} className="fullscreen-grid-container">
<ReactGridLayout
cols={12}
rowHeight={45}
autoSize
width={100}
useCSSTransforms
isDraggable={!isDashboardLocked && addPanelPermission}
isDroppable={!isDashboardLocked && addPanelPermission}
isResizable={!isDashboardLocked && addPanelPermission}
allowOverlap={false}
onLayoutChange={handleLayoutChange}
onDragStop={handleDragStop}
draggableHandle=".drag-handle"
layout={dashboardLayout}
style={{ backgroundColor: isDarkMode ? '' : themeColors.snowWhite }}
>
{dashboardLayout.map((layout) => {
const { i: id } = layout;
const currentWidget = (widgets || [])?.find((e) => e.id === id);
if (currentWidget?.panelTypes === PANEL_GROUP_TYPES.ROW) {
const rowWidgetProperties = currentPanelMap[id] || {};
return (
<CardContainer
className={isDashboardLocked ? '' : 'enable-resize'}
className="row-card"
isDarkMode={isDarkMode}
key={id}
data-grid={JSON.stringify(currentWidget)}
>
<Card
className="grid-item"
$panelType={currentWidget?.panelTypes || PANEL_TYPES.TIME_SERIES}
>
<GridCard
widget={currentWidget || ({ id, query: {} } as Widgets)}
headerMenuList={widgetActions}
variables={variables}
version={selectedDashboard?.data?.version}
onDragSelect={onDragSelect}
<div className={cx('row-panel')}>
<div style={{ display: 'flex', gap: '6px', alignItems: 'center' }}>
{rowWidgetProperties.collapsed && (
<GripVertical
size={14}
className="drag-handle"
color={isDarkMode ? Color.BG_VANILLA_100 : Color.BG_INK_300}
cursor="move"
/>
)}
<Typography.Text className="section-title">
{currentWidget.title}
</Typography.Text>
{rowWidgetProperties.collapsed ? (
<ChevronDown
size={14}
onClick={(): void => handleRowCollapse(id)}
className="row-icon"
/>
) : (
<ChevronUp
size={14}
onClick={(): void => handleRowCollapse(id)}
className="row-icon"
/>
)}
</div>
<WidgetRowHeader
id={id}
rowWidgetProperties={rowWidgetProperties}
setCurrentSelectRowId={setCurrentSelectRowId}
setIsDeleteModalOpen={setIsDeleteModalOpen}
setIsSettingsModalOpen={setIsSettingsModalOpen}
editWidget={editWidget}
deleteWidget={deleteWidget}
/>
</Card>
</div>
</CardContainer>
);
})}
</ReactGridLayout>
</FullScreen>
</>
}
return (
<CardContainer
className={isDashboardLocked ? '' : 'enable-resize'}
isDarkMode={isDarkMode}
key={id}
data-grid={JSON.stringify(currentWidget)}
>
<Card
className="grid-item"
isDarkMode={isDarkMode}
$panelType={currentWidget?.panelTypes || PANEL_TYPES.TIME_SERIES}
>
<GridCard
widget={(currentWidget as Widgets) || ({ id, query: {} } as Widgets)}
headerMenuList={widgetActions}
variables={variables}
version={selectedDashboard?.data?.version}
onDragSelect={onDragSelect}
/>
</Card>
</CardContainer>
);
})}
</ReactGridLayout>
{isDashboardLocked && (
<div className="footer">
<Button
type="text"
icon={<LockKeyhole size={14} />}
className="locked-text"
>
Locked
</Button>
<div className="locked-bar" />
</div>
)}
<Modal
open={isSettingsModalOpen}
title="Rename Section"
rootClassName="rename-section"
destroyOnClose
footer={null}
onCancel={(): void => {
setIsSettingsModalOpen(false);
setCurrentSelectRowId(null);
}}
>
<Form form={form} onFinish={onSettingsModalSubmit} requiredMark>
<Typography.Text className="typography">
Enter section name
</Typography.Text>
<Form.Item required name={['title']}>
<Input
placeholder="Enter row name here..."
defaultValue={defaultTo(
widgets?.find((widget) => widget.id === currentSelectRowId)
?.title as string,
'Sample Title',
)}
/>
</Form.Item>
<Form.Item>
<div className="action-btns">
<Button
type="primary"
htmlType="submit"
className="ok-btn"
icon={<Check size={14} />}
disabled={updateDashboardMutation.isLoading}
>
Apply Changes
</Button>
<Button
type="text"
className="cancel-btn"
icon={<X size={14} />}
onClick={(): void => {
setIsSettingsModalOpen(false);
setCurrentSelectRowId(null);
}}
>
Cancel
</Button>
</div>
</Form.Item>
</Form>
</Modal>
<Modal
open={isDeleteModalOpen}
title="Delete Row"
destroyOnClose
onCancel={(): void => {
setIsDeleteModalOpen(false);
setCurrentSelectRowId(null);
}}
onOk={(): void => handleRowDelete()}
>
<Typography.Text>Are you sure you want to delete this row</Typography.Text>
</Modal>
</FullScreen>
);
}

View File

@@ -4,6 +4,7 @@ export enum MenuItemKeys {
Delete = 'delete',
Clone = 'clone',
CreateAlerts = 'createAlerts',
Download = 'download',
}
export const MENUITEM_KEYS_VS_LABELS = {
@@ -12,4 +13,5 @@ export const MENUITEM_KEYS_VS_LABELS = {
[MenuItemKeys.Delete]: 'Delete',
[MenuItemKeys.Clone]: 'Clone',
[MenuItemKeys.CreateAlerts]: 'Create Alerts',
[MenuItemKeys.Download]: 'Download as CSV',
};

View File

@@ -2,6 +2,7 @@ import './WidgetHeader.styles.scss';
import {
AlertOutlined,
CloudDownloadOutlined,
CopyOutlined,
DeleteOutlined,
EditFilled,
@@ -17,6 +18,9 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
import useCreateAlerts from 'hooks/queryBuilder/useCreateAlerts';
import useComponentPermission from 'hooks/useComponentPermission';
import history from 'lib/history';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { isEmpty } from 'lodash-es';
import { unparse } from 'papaparse';
import { ReactNode, useCallback, useMemo } from 'react';
import { UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux';
@@ -46,6 +50,7 @@ interface IWidgetHeaderProps {
headerMenuList?: MenuItemKeys[];
isWarning: boolean;
isFetchingResponse: boolean;
tableProcessedDataRef: React.MutableRefObject<RowData[]>;
}
function WidgetHeader({
@@ -61,6 +66,7 @@ function WidgetHeader({
headerMenuList,
isWarning,
isFetchingResponse,
tableProcessedDataRef,
}: IWidgetHeaderProps): JSX.Element | null {
const onEditHandler = useCallback((): void => {
const widgetId = widget.id;
@@ -75,6 +81,17 @@ function WidgetHeader({
const onCreateAlertsHandler = useCreateAlerts(widget);
const onDownloadHandler = useCallback((): void => {
const csv = unparse(tableProcessedDataRef.current);
const csvBlob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const csvUrl = URL.createObjectURL(csvBlob);
const downloadLink = document.createElement('a');
downloadLink.href = csvUrl;
downloadLink.download = `${!isEmpty(title) ? title : 'table-panel'}.csv`;
downloadLink.click();
downloadLink.remove();
}, [tableProcessedDataRef, title]);
const keyMethodMapping = useMemo(
() => ({
[MenuItemKeys.View]: onView,
@@ -82,8 +99,16 @@ function WidgetHeader({
[MenuItemKeys.Delete]: onDelete,
[MenuItemKeys.Clone]: onClone,
[MenuItemKeys.CreateAlerts]: onCreateAlertsHandler,
[MenuItemKeys.Download]: onDownloadHandler,
}),
[onDelete, onEditHandler, onView, onClone, onCreateAlertsHandler],
[
onView,
onEditHandler,
onDelete,
onClone,
onCreateAlertsHandler,
onDownloadHandler,
],
);
const onMenuItemSelectHandler: MenuProps['onClick'] = useCallback(
@@ -128,6 +153,13 @@ function WidgetHeader({
isVisible: headerMenuList?.includes(MenuItemKeys.Clone) || false,
disabled: !editWidget,
},
{
key: MenuItemKeys.Download,
icon: <CloudDownloadOutlined />,
label: MENUITEM_KEYS_VS_LABELS[MenuItemKeys.Download],
isVisible: widget.panelTypes === PANEL_TYPES.TABLE,
disabled: false,
},
{
key: MenuItemKeys.Delete,
icon: <DeleteOutlined />,
@@ -144,7 +176,13 @@ function WidgetHeader({
disabled: false,
},
],
[headerMenuList, queryResponse.isFetching, editWidget, deleteWidget],
[
headerMenuList,
queryResponse.isFetching,
editWidget,
deleteWidget,
widget.panelTypes,
],
);
const updatedMenuList = useMemo(() => generateMenuList(actions), [actions]);

View File

@@ -19,4 +19,5 @@ export const isTWidgetOptions = (value: string): value is MenuItemKeys =>
value === MenuItemKeys.Edit ||
value === MenuItemKeys.Delete ||
value === MenuItemKeys.Clone ||
value === MenuItemKeys.CreateAlerts;
value === MenuItemKeys.CreateAlerts ||
value === MenuItemKeys.Download;

View File

@@ -0,0 +1,82 @@
import { Button, Popover } from 'antd';
import { EllipsisIcon, PenLine, X } from 'lucide-react';
import { useState } from 'react';
import { Layout } from 'react-grid-layout';
interface WidgetRowHeaderProps {
rowWidgetProperties: {
widgets: Layout[];
collapsed: boolean;
};
editWidget: boolean;
deleteWidget: boolean;
setIsSettingsModalOpen: (value: React.SetStateAction<boolean>) => void;
setCurrentSelectRowId: (value: React.SetStateAction<string | null>) => void;
setIsDeleteModalOpen: (value: React.SetStateAction<boolean>) => void;
id: string;
}
export function WidgetRowHeader(props: WidgetRowHeaderProps): JSX.Element {
const {
rowWidgetProperties,
editWidget,
deleteWidget,
setCurrentSelectRowId,
setIsDeleteModalOpen,
setIsSettingsModalOpen,
id,
} = props;
const [isRowSettingsOpen, setIsRowSettingsOpen] = useState<boolean>(false);
return (
<Popover
open={isRowSettingsOpen}
arrow={false}
onOpenChange={(visible): void => setIsRowSettingsOpen(visible)}
rootClassName="row-settings"
trigger="hover"
placement="bottomRight"
content={
<div className="menu-content">
<section className="section-1">
<Button
className="rename-btn"
type="text"
disabled={!editWidget}
icon={<PenLine size={14} />}
onClick={(): void => {
setIsSettingsModalOpen(true);
setCurrentSelectRowId(id);
setIsRowSettingsOpen(false);
}}
>
Rename
</Button>
</section>
{!rowWidgetProperties.collapsed && (
<section className="section-2">
<Button
className="remove-section"
type="text"
icon={<X size={14} />}
disabled={!deleteWidget}
onClick={(): void => {
setIsDeleteModalOpen(true);
setCurrentSelectRowId(id);
setIsRowSettingsOpen(false);
}}
>
Remove Section
</Button>
</section>
)}
</div>
}
>
<EllipsisIcon
size={14}
className="settings-icon"
onClick={(): void => setIsRowSettingsOpen(!isRowSettingsOpen)}
/>
</Popover>
);
}

View File

@@ -16,6 +16,6 @@ export const EMPTY_WIDGET_LAYOUT = {
i: PANEL_TYPES.EMPTY_WIDGET,
w: 6,
x: 0,
h: 3,
h: 6,
y: 0,
};

View File

@@ -1,16 +1,13 @@
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useCallback } from 'react';
import { FullScreenHandle } from 'react-full-screen';
import GraphLayoutContainer from './GridCardLayout';
function GridGraph(): JSX.Element {
const { handleToggleDashboardSlider } = useDashboard();
const onEmptyWidgetHandler = useCallback(() => {
handleToggleDashboardSlider(true);
}, [handleToggleDashboardSlider]);
return <GraphLayoutContainer onAddPanelHandler={onEmptyWidgetHandler} />;
interface GridGraphProps {
handle: FullScreenHandle;
}
function GridGraph(props: GridGraphProps): JSX.Element {
const { handle } = props;
return <GraphLayoutContainer handle={handle} />;
}
export default GridGraph;

View File

@@ -8,12 +8,28 @@ const ReactGridLayoutComponent = WidthProvider(RGL);
interface CardProps {
$panelType: PANEL_TYPES;
isDarkMode: boolean;
}
export const Card = styled(CardComponent)<CardProps>`
&&& {
height: 100%;
overflow: hidden;
border-radius: 3px;
border: 1px solid var(--bg-slate-500);
background: linear-gradient(
0deg,
rgba(171, 189, 255, 0) 0%,
rgba(171, 189, 255, 0) 100%
),
#0b0c0e;
${({ isDarkMode }): StyledCSS =>
!isDarkMode &&
css`
border: 1px solid var(--bg-vanilla-300);
background: unset;
`}
}
.ant-card-body {
@@ -29,6 +45,18 @@ interface Props {
export const CardContainer = styled.div<Props>`
overflow: auto;
&.row-card {
.row-panel {
height: 100%;
display: flex;
justify-content: space-between;
background: ${({ isDarkMode }): string =>
isDarkMode ? 'var(--bg-ink-400)' : 'var(--bg-vanilla-300)'};
align-items: center;
overflow: hidden;
}
}
&.enable-resize {
:hover {
.react-resizable-handle {
@@ -63,6 +91,7 @@ export const ReactGridLayout = styled(ReactGridLayoutComponent)`
margin-top: 1rem;
position: relative;
min-height: 40vh;
margin: 16px;
.react-grid-item.react-grid-placeholder {
background: grey;

View File

@@ -1,3 +0,0 @@
export interface GraphLayoutProps {
onAddPanelHandler: VoidFunction;
}

View File

@@ -2,8 +2,12 @@ import { ExclamationCircleFilled } from '@ant-design/icons';
import { Space, Tooltip } from 'antd';
import { Events } from 'constants/events';
import { QueryTable } from 'container/QueryTable';
import { createTableColumnsFromQuery } from 'lib/query/createTableColumnsFromQuery';
import { memo, ReactNode, useEffect, useMemo } from 'react';
import {
createTableColumnsFromQuery,
RowData,
} from 'lib/query/createTableColumnsFromQuery';
import { get, set } from 'lodash-es';
import { memo, ReactNode, useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { eventEmitter } from 'utils/getEventEmitter';
@@ -15,6 +19,7 @@ function GridTableComponent({
data,
query,
thresholds,
tableProcessedDataRef,
...props
}: GridTableComponentProps): JSX.Element {
const { t } = useTranslation(['valueGraph']);
@@ -27,6 +32,33 @@ function GridTableComponent({
[data, query],
);
const createDataInCorrectFormat = useCallback(
(dataSource: RowData[]): RowData[] =>
dataSource.map((d) => {
const finalObject = {};
const keys = Object.keys(d);
keys.forEach((k) => {
const label = get(
columns.find((c) => get(c, 'dataIndex', '') === k) || {},
'title',
'',
);
if (label) {
set(finalObject, label as string, d[k]);
}
});
return finalObject as RowData;
}),
[columns],
);
useEffect(() => {
if (tableProcessedDataRef) {
// eslint-disable-next-line no-param-reassign
tableProcessedDataRef.current = createDataInCorrectFormat(dataSource);
}
}, [createDataInCorrectFormat, dataSource, tableProcessedDataRef]);
const newColumnData = columns.map((e) => ({
...e,
render: (text: string): ReactNode => {

View File

@@ -10,6 +10,7 @@ import { Query } from 'types/api/queryBuilder/queryBuilderData';
export type GridTableComponentProps = {
query: Query;
thresholds?: ThresholdProps[];
tableProcessedDataRef?: React.MutableRefObject<RowData[]>;
} & Pick<LogsExplorerTableProps, 'data'> &
Omit<TableProps<RowData>, 'columns' | 'dataSource'>;

View File

@@ -4,6 +4,7 @@ import { Input, Typography } from 'antd';
import type { ColumnsType } from 'antd/es/table/interface';
import saveAlertApi from 'api/alerts/save';
import DropDown from 'components/DropDown/DropDown';
import { listAlertMessage } from 'components/facingIssueBtn/util';
import {
DynamicColumnsKey,
TableDataSource,
@@ -116,14 +117,19 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
.refetch()
.then(() => {
const compositeQuery = mapQueryDataFromApi(record.condition.compositeQuery);
history.push(
`${ROUTES.EDIT_ALERTS}?ruleId=${record.id.toString()}&${
QueryParams.compositeQuery
}=${encodeURIComponent(JSON.stringify(compositeQuery))}&panelTypes=${
record.condition.compositeQuery.panelType
}`,
params.set(
QueryParams.compositeQuery,
encodeURIComponent(JSON.stringify(compositeQuery)),
);
params.set(
QueryParams.panelTypes,
record.condition.compositeQuery.panelType,
);
params.set(QueryParams.ruleId, record.id.toString());
history.push(`${ROUTES.EDIT_ALERTS}?${params.toString()}`);
})
.catch(handleError);
};
@@ -150,7 +156,8 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
setData(refetchData.payload || []);
setTimeout(() => {
const clonedAlert = refetchData.payload[refetchData.payload.length - 1];
history.push(`${ROUTES.EDIT_ALERTS}?ruleId=${clonedAlert.id}`);
params.set(QueryParams.ruleId, String(clonedAlert.id));
history.push(`${ROUTES.EDIT_ALERTS}?${params.toString()}`);
}, 2000);
}
if (status === 'error') {
@@ -337,7 +344,8 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
<TextToolTip
{...{
text: `More details on how to create alerts`,
url: 'https://signoz.io/docs/userguide/alerts-management/',
url:
'https://signoz.io/docs/alerts/?utm_source=product&utm_medium=list-alerts',
}}
/>
@@ -358,6 +366,15 @@ function ListAlert({ allAlertRules, refetch }: ListAlertProps): JSX.Element {
pagination={{
defaultCurrent: Number(paginationParam) || 1,
}}
facingIssueBtn={{
attributes: {
screen: 'Alert list page',
},
eventName: 'Alert: Facing Issues in alert',
buttonText: 'Facing issues with alerts?',
message: listAlertMessage,
onHoverText: 'Click here to get help with alerts',
}}
/>
</>
);

File diff suppressed because it is too large Load Diff

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